Skip to content

refactor+fix: add LangChainAgent abstraction and fix HRI communication #538

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Apr 28, 2025

Conversation

boczekbartek
Copy link
Member

@boczekbartek boczekbartek commented Apr 18, 2025

Purpose

With new API based on the register_callback, the previous implementation is obsolete. Also #536 broke the HRI communication in ReActAgent

Proposed Changes

  • Refactor ReActAgent and extract LangChainAgent
  • Base HRI communication in ReActAgent on register_callback
  • refactor ROS2HRIConnector

Issues

Testing

python examples/agents/react.py
ros2 topic echo /to_human rai_interfaces/msg/HRIMessage

python examples/agents/react_ros2.py
ros2 topic pub /from_human rai_interfaces/msg/HRIMessage "{'text': 'Hello, who are you?'}" --once
ros2 topic echo /to_human rai_interfaces/msg/HRIMessage

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features
    • Introduced a new LangChainAgent class with configurable message handling behaviors for human-robot interaction.
    • Added a new ReActAgent implementation built on the LangChainAgent framework.
    • Provided a new example demonstrating ReActAgent integration with ROS2 communication.
  • Refactor
    • Simplified ROS2HRIConnector for streamlined message publishing and callback registration.
    • Updated import paths and agent lifecycle management for improved clarity and control.
    • Replaced legacy ReActAgent implementation with the new LangChain-based version.
  • Bug Fixes
    • Enhanced handling and warnings for missing ROS 2 message dependencies.
  • Tests
    • Added tests validating message reduction behaviors in the LangChainAgent.
  • Chores
    • Renamed methods and updated type annotations for consistency and clarity.

@boczekbartek boczekbartek changed the title fix: ReActAgent refactor: add LangChainAgent abstraction Apr 23, 2025
@boczekbartek boczekbartek changed the title refactor: add LangChainAgent abstraction refactor: add LangChainAgent abstraction and fix HRI communication Apr 23, 2025
@boczekbartek boczekbartek changed the title refactor: add LangChainAgent abstraction and fix HRI communication refactor+fix: add LangChainAgent abstraction and fix HRI communication Apr 23, 2025
@boczekbartek boczekbartek force-pushed the fix/react_agent branch 3 times, most recently from baebccb to 60f4348 Compare April 24, 2025 10:48
@boczekbartek boczekbartek marked this pull request as ready for review April 24, 2025 10:48
@boczekbartek
Copy link
Member Author

@CodeRabbit full review

Copy link
Contributor

coderabbitai bot commented Apr 24, 2025

Walkthrough

This set of changes refactors the implementation of the ReActAgent by relocating it from rai.agents.react_agent to a new module rai.agents.langchain.react_agent. It introduces a new base class LangChainAgent in rai.agents.langchain.agent that manages message buffering, reduction behaviors, and integration with LangChain runnables and HRI connectors. Example scripts and imports are updated to reflect the new agent structure and lifecycle management. The ROS2HRIConnector is simplified by removing explicit topic configuration and focusing on basic message publishing and callback registration with improved preprocessing. The callback handler and connector interfaces are adjusted for consistency. Conditional importing is added for ROS2 message types, and new tests validate message reduction behaviors in the agent.

Changes

File(s) Change Summary
examples/agents/react.py Updated imports for ReActAgent and ROS2HRIConnector; changed agent and connector initialization to new interfaces and lifecycle methods; replaced AgentRunner usage with direct agent control.
examples/agents/react_ros2.py Added new example demonstrating ReActAgent usage with ROS2 connectors and agent runner.
src/rai_core/rai/agents/init.py Updated import path for ReActAgent to rai.agents.langchain.react_agent.
src/rai_core/rai/agents/langchain/agent.py Added new LangChainAgent class implementing message buffering, reduction behaviors, asynchronous processing, and integration with LangChain runnables and HRI connectors.
src/rai_core/rai/agents/langchain/react_agent.py Added new ReActAgent class extending LangChainAgent, constructing the agent with connectors, optional language model, tools, state, and system prompt.
src/rai_core/rai/agents/react_agent.py Removed old ReActAgent class and all its methods.
src/rai_core/rai/agents/langchain/callback.py Updated type annotations for connectors; changed message sending logic to build and send messages per target; removed some logging lines; adjusted variable names for clarity.
src/rai_core/rai/communication/hri_connector.py Renamed method _build_message to build_message without changing behavior.
src/rai_core/rai/communication/ros2/connectors/hri_connector.py Refactored ROS2HRIConnector to inherit from both ROS2BaseConnector and HRIConnector; removed explicit topic configuration and service/action management; simplified publishing and callback registration; added preprocessing method.
src/rai_core/rai/communication/ros2/connectors/base.py Added return type annotation to general_callback_preprocessor method.
src/rai_core/rai/communication/ros2/messages.py Changed import of ROS2 message types to conditional import using importlib.util.find_spec; updated from_ros2 method to return cls instance for better subclassing support.
tests/agents/test_langchain_agent.py Added parameterized tests for _apply_reduction_behavior method in LangChainAgent covering all supported message handling behaviors.
tests/communication/ros2/test_connectors.py Updated ROS2HRIConnector instantiation by removing explicit targets argument to match new constructor signature.

Sequence Diagram(s)

sequenceDiagram
    participant Human
    participant ROS2HRIConnector
    participant LangChainAgent
    participant LangChainRunnable
    participant HRICallbackHandler

    Human->>ROS2HRIConnector: Send HRI message (/from_human)
    ROS2HRIConnector->>LangChainAgent: source_callback(msg)
    LangChainAgent->>LangChainAgent: Buffer message, possibly interrupt
    LangChainAgent->>LangChainAgent: Reduce messages (per behavior)
    LangChainAgent->>LangChainRunnable: stream(aggregated message, callbacks)
    LangChainRunnable-->>LangChainAgent: Streaming response
    LangChainAgent->>HRICallbackHandler: Callback with response
    HRICallbackHandler->>ROS2HRIConnector: build_message, send_message (/to_human)
    ROS2HRIConnector->>Human: Human receives response
Loading

Possibly related PRs

  • RobotecAI/rai#524: Refactors agent imports and usage in examples, closely related to the new agent and runner structure.
  • RobotecAI/rai#518: Refactors base agent and connector APIs, directly affecting agent and connector initialization patterns.
  • RobotecAI/rai#508: Enhances the ReAct agent with system prompt support and new tools, overlapping with changes to the ReActAgent class.

Suggested reviewers

  • rachwalk

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (2)
tests/agents/test_langchain_agent.py (1)

20-20: Import suggests a type annotation issue.

The newMessageBehaviorType appears to be imported from rai.agents.langchain.agent, suggesting it might be a type alias or enum, but it's using Python's lower camel case naming convention which is unusual for a type. Consider using snake_case or PascalCase for consistency with Python conventions.

src/rai_core/rai/agents/langchain/callback.py (1)

50-52: Fix typo in logging message.

There's a typo in the logging message: "targer" should be "target".

-            self.logger.info(f"Sending {len(tokens)} tokens to targer: {target}")
+            self.logger.info(f"Sending {len(tokens)} tokens to target: {target}")
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0eec253 and 60f4348.

📒 Files selected for processing (11)
  • examples/agents/react.py (1 hunks)
  • src/rai_core/rai/agents/__init__.py (1 hunks)
  • src/rai_core/rai/agents/langchain/agent.py (1 hunks)
  • src/rai_core/rai/agents/langchain/callback.py (3 hunks)
  • src/rai_core/rai/agents/langchain/react_agent.py (1 hunks)
  • src/rai_core/rai/agents/react_agent.py (0 hunks)
  • src/rai_core/rai/communication/hri_connector.py (1 hunks)
  • src/rai_core/rai/communication/ros2/connectors/base.py (1 hunks)
  • src/rai_core/rai/communication/ros2/connectors/hri_connector.py (1 hunks)
  • src/rai_core/rai/communication/ros2/messages.py (3 hunks)
  • tests/agents/test_langchain_agent.py (1 hunks)
💤 Files with no reviewable changes (1)
  • src/rai_core/rai/agents/react_agent.py
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/rai_core/rai/agents/__init__.py (1)
src/rai_core/rai/agents/langchain/react_agent.py (1)
  • ReActAgent (26-42)
src/rai_core/rai/communication/ros2/connectors/base.py (2)
src/rai_core/rai/communication/ros2/connectors/hri_connector.py (1)
  • general_callback_preprocessor (56-57)
src/rai_core/rai/communication/base_connector.py (1)
  • general_callback_preprocessor (173-175)
src/rai_core/rai/agents/langchain/callback.py (2)
src/rai_core/rai/communication/hri_connector.py (3)
  • HRIConnector (140-153)
  • HRIMessage (38-134)
  • build_message (146-153)
src/rai_core/rai/communication/ros2/connectors/hri_connector.py (1)
  • send_message (39-54)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build-and-test-ros2 (humble)
  • GitHub Check: build-and-test-ros2 (jazzy)
🔇 Additional comments (22)
src/rai_core/rai/agents/__init__.py (1)

17-17: Import path updated to reflect new LangChainAgent abstraction

The import path for ReActAgent has been updated to reflect its new location in the langchain subpackage, which aligns with the PR objective of extracting the LangChainAgent abstraction.

src/rai_core/rai/communication/ros2/connectors/base.py (1)

155-155: Added proper return type annotation

Adding the return type annotation -> T improves type safety and makes the API more explicit. This correctly indicates that the method returns an instance of the generic type T that the connector is bound to.

src/rai_core/rai/communication/hri_connector.py (1)

146-146: Method renamed from private to public API

The method has been renamed from _build_message to build_message, removing the leading underscore to indicate it's now part of the public API. This aligns with the PR objective of updating HRI communication to use the register_callback mechanism.

This change is needed as the method is likely now being called from the refactored agent implementations.

src/rai_core/rai/communication/ros2/messages.py (2)

15-15: Improved module import robustness

The conditional import of rai_interfaces.msg using importlib.util.find_spec is a better approach than the previous implementation. It checks for the module's presence before attempting to import it and provides a more accurate warning message.

Also applies to: 31-33


68-76: Enhanced subclassing support with cls instead of concrete class

Using cls instead of the concrete class name ROS2HRIMessage in the return statement of the class method supports proper inheritance and follows the Liskov Substitution Principle. This change allows subclasses to correctly extend this method without unexpected behavior.

tests/agents/test_langchain_agent.py (1)

22-31: Test parametrization looks good with comprehensive behavior coverage.

The parametrization covers various message reduction behaviors effectively. I particularly appreciate the inclusion of both normal and interrupt-based behaviors.

examples/agents/react.py (4)

16-20: Import reorganization looks good.

The imports have been correctly updated to reflect the new module structure with the refactored ReActAgent now imported from rai.agents.langchain.react_agent.


26-27: ROS2HRIConnector instantiation simplified.

The connector is now instantiated without explicit parameters, aligning with the refactored connector that no longer manages topic configurations directly.


28-34: Agent configuration updated to use target_connectors.

The agent now properly uses the target_connectors parameter mapping the /to_human topic to the hri_connector instance, aligning with the new LangChainAgent base class design.


34-35: Source subscription improved with explicit method.

Using subscribe_source makes the subscription more explicit and aligns with the new agent design that supports multiple connectors with explicit subscription methods.

src/rai_core/rai/agents/langchain/callback.py (3)

17-17: Type import update is appropriate.

Adding Dict to the imports alongside List and Optional is a good practice for proper type annotations.


30-30: Type annotation update for connectors parameter.

The type annotation change from dict to Dict aligns with PEP 585 recommendations for using the standard typing module annotations.


50-64: Message sending approach updated for new connector interface.

The refactored code now correctly:

  1. Iterates through target-connector pairs
  2. Builds a message using connector.build_message
  3. Sends it with connector.send_message(to_send, target)

This aligns with the simplified connector interface in the refactored architecture.

src/rai_core/rai/agents/langchain/react_agent.py (2)

26-42: Well-structured ReActAgent implementation.

The ReActAgent class extends LangChainAgent and provides a clean interface for constructing React agents with LangChain models and tools. The implementation:

  1. Accepts appropriate parameters (target connectors, language model, tools, state, system prompt)
  2. Creates a runnable with create_react_runnable
  3. Properly initializes the parent class

This approach promotes code reuse by leveraging the shared functionality in the base class while maintaining specific React agent behavior.


26-42:

✅ Verification successful

Verify implementation of create_react_runnable function.

Make sure that the create_react_runnable function properly initializes a React agent runnable with the provided parameters. Since it's a key component connecting the agent to the LangChain ecosystem, it's important to ensure it's working correctly.


🏁 Script executed:

#!/bin/bash
# Check the implementation of create_react_runnable function
fd "create_react_runnable" --type file | xargs cat

Length of output: 50


To locate and inspect the actual implementation of create_react_runnable, let’s do a recursive text search across the repo:


🏁 Script executed:

#!/bin/bash
# Search for both the definition and all call sites of create_react_runnable
rg -n "create_react_runnable" -A5 -B5 .

Length of output: 5789


To inspect the full implementation of create_react_runnable, let’s pull its definition and body:


🏁 Script executed:

#!/bin/bash
# Show definition and body of create_react_runnable
rg -n "def create_react_runnable" -A200 src/rai_core/rai/agents/langchain/runnables.py

Length of output: 1683


Implementation Verified: create_react_runnable correctly initializes the React agent runnable

The function

  • Defaults to the configured “complex_model” if no LLM is provided
  • Builds a StateGraph with proper start → LLM and optional tools → LLM transitions
  • Binds tools to the LLM when supplied
  • Compiles and returns the runnable graph

No further changes are needed.

src/rai_core/rai/communication/ros2/connectors/hri_connector.py (3)

39-54: Well-structured message publishing method with clear parameters

The refactored send_message method provides a clean interface for publishing HRI messages with appropriate optional parameters for QoS profile customization.


56-57: Good addition of preprocessor for human messages

The new callback preprocessor method correctly handles incoming messages and sets the author to "human", which supports the HRI communication flow.


32-32: Well-defined inheritance structure

The connector now properly inherits from both ROS2BaseConnector and HRIConnector, establishing a clear type hierarchy and interface implementation.

src/rai_core/rai/agents/langchain/agent.py (4)

147-166: Well-implemented message reduction behavior

The static method for applying different message reduction behaviors is well-structured with clear logic for each behavior type.


32-43: Good type definitions with clear documentation

The code provides well-defined types for state and message behaviors, which improves API clarity (aside from the typo mentioned earlier).


90-96: Clean thread management for agent execution

The run method properly creates and starts a dedicated thread for the agent's execution loop.


137-146: Robust agent stopping mechanism

The stop method ensures proper cleanup of threads and events, preventing resource leaks.

@boczekbartek
Copy link
Member Author

@CodeRabbit full review

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
tests/agents/test_langchain_agent.py (1)

1-42: Test implementation for the new message reduction behaviors is good, but has typos in behavior names.

The test properly verifies the _apply_reduction_behavior static method of the LangChainAgent class using appropriate parameterization for different message behaviors.

There are two typos in the behavior names:

-        ("interupt_take_all", [1, 2, 3], [], [1, 2, 3]),
-        ("interupt_keep_last", [1, 2, 3], [], [3]),
+        ("interrupt_take_all", [1, 2, 3], [], [1, 2, 3]),
+        ("interrupt_keep_last", [1, 2, 3], [], [3]),
examples/agents/react.py (1)

29-31: Updated agent construction with the new API pattern.

The target_connectors parameter is now used instead of the previous connectors dictionary, which is consistent with the refactored ReActAgent implementation.

There's a small typo in the comment: "agnet's" should be "agent's".

-        },  # agnet's output is sent to /to_human ros2 topic
+        },  # agent's output is sent to /to_human ros2 topic
src/rai_core/rai/agents/langchain/agent.py (1)

182-187: Consider using event-based waiting instead of sleep

The use of time.sleep(0.5) could be replaced with an event-based approach to be more efficient and responsive to new messages.

    def _run_agent(self):
        if len(self._received_messages) == 0:
            self._agent_ready_event.set()
            self.logger.info("Waiting for messages...")
-            time.sleep(0.5)
+            # Wait for a signal that messages have arrived
+            message_wait_event = threading.Event()
+            self._message_wait_event = message_wait_event
+            message_wait_event.wait(timeout=0.5)
+            self._message_wait_event = None
            return

With corresponding updates to the message receiving code to signal this event.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Lite

📥 Commits

Reviewing files that changed from the base of the PR and between c2251f0 and 28f7f71.

📒 Files selected for processing (13)
  • examples/agents/react.py (1 hunks)
  • examples/agents/react_ros2.py (1 hunks)
  • src/rai_core/rai/agents/__init__.py (1 hunks)
  • src/rai_core/rai/agents/langchain/agent.py (1 hunks)
  • src/rai_core/rai/agents/langchain/callback.py (3 hunks)
  • src/rai_core/rai/agents/langchain/react_agent.py (1 hunks)
  • src/rai_core/rai/agents/react_agent.py (0 hunks)
  • src/rai_core/rai/communication/hri_connector.py (1 hunks)
  • src/rai_core/rai/communication/ros2/connectors/base.py (1 hunks)
  • src/rai_core/rai/communication/ros2/connectors/hri_connector.py (1 hunks)
  • src/rai_core/rai/communication/ros2/messages.py (3 hunks)
  • tests/agents/test_langchain_agent.py (1 hunks)
  • tests/communication/ros2/test_connectors.py (1 hunks)
💤 Files with no reviewable changes (1)
  • src/rai_core/rai/agents/react_agent.py
🧰 Additional context used
🧬 Code Graph Analysis (5)
src/rai_core/rai/agents/__init__.py (1)
src/rai_core/rai/agents/langchain/react_agent.py (1)
  • ReActAgent (26-42)
src/rai_core/rai/communication/ros2/connectors/base.py (2)
src/rai_core/rai/communication/ros2/connectors/hri_connector.py (1)
  • general_callback_preprocessor (79-80)
src/rai_core/rai/communication/base_connector.py (1)
  • general_callback_preprocessor (173-175)
examples/agents/react_ros2.py (8)
src/rai_core/rai/agents/runner.py (1)
  • AgentRunner (65-115)
src/rai_core/rai/agents/langchain/react_agent.py (1)
  • ReActAgent (26-42)
src/rai_core/rai/communication/ros2/connectors/ros2_connector.py (1)
  • ROS2Connector (19-20)
src/rai_core/rai/communication/ros2/context.py (1)
  • ROS2Context (23-146)
src/rai_core/rai/communication/ros2/connectors/hri_connector.py (1)
  • ROS2HRIConnector (32-80)
src/rai_core/rai/tools/ros2/generic/toolkit.py (1)
  • ROS2Toolkit (22-50)
examples/agents/react.py (1)
  • main (24-37)
src/rai_core/rai/agents/langchain/agent.py (2)
  • subscribe_source (136-140)
  • run (155-161)
src/rai_core/rai/agents/langchain/react_agent.py (3)
src/rai_core/rai/agents/langchain/runnables.py (2)
  • create_react_runnable (69-114)
  • ReActAgentState (29-38)
src/rai_core/rai/agents/langchain/agent.py (1)
  • LangChainAgent (45-257)
src/rai_core/rai/communication/hri_connector.py (2)
  • HRIConnector (140-153)
  • HRIMessage (38-134)
src/rai_core/rai/communication/ros2/connectors/hri_connector.py (5)
src/rai_core/rai/communication/hri_connector.py (1)
  • HRIConnector (140-153)
src/rai_core/rai/communication/ros2/connectors/base.py (4)
  • ROS2BaseConnector (44-293)
  • send_message (137-153)
  • register_callback (158-178)
  • general_callback_preprocessor (155-156)
src/rai_core/rai/communication/ros2/messages.py (3)
  • ROS2HRIMessage (45-108)
  • to_ros2_dict (78-104)
  • from_ros2 (47-76)
src/rai_core/rai/communication/base_connector.py (3)
  • send_message (85-94)
  • register_callback (109-133)
  • general_callback_preprocessor (173-175)
src/rai_core/rai/communication/ros2/api/topic.py (1)
  • publish (153-180)
🔇 Additional comments (39)
src/rai_core/rai/communication/ros2/messages.py (2)

31-34: Improved conditional ROS2 dependency handling

The conditional importing using importlib.util.find_spec is a more elegant approach than the previous try-except block. This early detection of missing dependencies provides clearer messaging to users when ROS2 message functionality isn't available.


68-68: Good polymorphism enhancement

Changing the return statement to use cls instead of explicitly returning an instance of ROS2HRIMessage enables proper subclassing. This is an important improvement that follows the factory method pattern and ensures that subclasses of ROS2HRIMessage can use this method correctly.

src/rai_core/rai/agents/__init__.py (1)

17-17: LGTM: Updated import path for refactored ReActAgent

The import path has been correctly updated to match the new location of the ReActAgent class, which now inherits from the new LangChainAgent base class. This is a necessary change for the architectural refactoring described in the PR.

tests/communication/ros2/test_connectors.py (1)

173-173: LGTM: Updated connector instantiation

The ROS2HRIConnector is now instantiated without explicit topic targets, aligning with the refactored implementation that no longer requires explicit topic configuration. The test still performs the same verification of message publishing and reception functionality.

src/rai_core/rai/communication/ros2/connectors/base.py (1)

155-155: LGTM: Added return type annotation

Adding the explicit return type annotation -> T to the general_callback_preprocessor method improves type safety and aligns with the implementation in the subclass ROS2HRIConnector. This makes the code more maintainable and helps catch potential type errors.

src/rai_core/rai/communication/hri_connector.py (1)

146-146: Method visibility change from protected to public.

The method _build_message has been renamed to build_message, changing its visibility from protected to public. This change aligns with the refactoring of agent architecture where the new LangChainAgent and derived classes directly use this method instead of accessing a protected method.

examples/agents/react_ros2.py (2)

15-21: Clean imports aligned with the new architecture.

The imports correctly reference the new module structure following the refactoring of ReActAgent into the rai.agents.langchain package.


23-38: Good example demonstrating the new API pattern.

This example properly demonstrates:

  1. The use of the refactored ReActAgent with the new target_connectors parameter
  2. The simplified ROS2HRIConnector instantiation without explicit topic configuration
  3. The new subscription pattern using subscribe_source which leverages the register_callback method
  4. The use of AgentRunner for lifecycle management

This provides a clear reference implementation for users to understand the updated agent lifecycle and communication patterns.

examples/agents/react.py (3)

16-19: Updated imports to match the refactored architecture.

The imports have been correctly updated to reference:

  1. The relocated ReActAgent class from rai.agents.langchain.react_agent
  2. The explicit import of HRIMessage from rai.communication.hri_connector
  3. The updated import path for ROS2HRIConnector

These changes align with the refactoring performed in this PR.


26-26: Simplified connector instantiation.

The ROS2HRIConnector instantiation has been simplified by removing explicit topic configuration, which aligns with the updated connector implementation that now uses the register_callback method for topic subscription.


34-37: Updated agent lifecycle management.

The agent lifecycle is now managed directly through method calls (run(), wait(), stop()) rather than using AgentRunner. This reflects the new agent design that manages its own asynchronous execution and message handling internally.

src/rai_core/rai/agents/langchain/callback.py (5)

17-17: Updated type import for better consistency

The import statement has been expanded to include Dict from typing, which aligns with Python typing conventions and the use of capitalized type names.


30-30: Updated type annotation for better correctness

Changed from lowercase dict to Dict from the typing module, which is the correct typing convention.


50-51: Improved variable naming and logging clarity

Renamed variables from (connector_name, connector) to (target, connector) which better represents their purpose and aligns with the rest of the API.


53-59: Updated message handling approach

The previous implementation using connector.send_all_targets has been replaced with a more explicit two-step process:

  1. Building a message with connector.build_message
  2. Sending it with connector.send_message(to_send, target)

This change aligns with the updated interface in HRIConnector and provides better separation of concerns.


60-63: Generalized log message references

Updated log messages to reference generic "hri_connector" or "target" instead of connector-specific names, making the logs more consistent and maintainable.

src/rai_core/rai/agents/langchain/react_agent.py (2)

15-24: Well-structured imports with proper organization

The imports are well-organized, separating standard library imports from third-party imports and internal modules, following good Python practices.


26-42: Clean implementation of ReActAgent class

The ReActAgent class extends LangChainAgent and provides a clean implementation focused on React agent functionality. The constructor appropriately delegates to the superclass after creating a runnable with the provided LLM, tools, and system prompt.

This design follows good OOP principles by reusing the base class functionality while specializing for a specific use case.

src/rai_core/rai/communication/ros2/connectors/hri_connector.py (6)

15-21: Improved import organization and runtime checking

The imports have been reorganized and a runtime check for rai_interfaces.msg has been added using importlib.util.find_spec, which is a better approach than importing and handling import errors.


26-29: Updated warning message for missing package

The warning message now correctly references "rai_interfaces.msg" rather than "sounddevice", making it more relevant and helpful for users.


32-37: Improved class definition with multiple inheritance

Updated the class definition to inherit from both ROS2BaseConnector[ROS2HRIMessage] and HRIConnector[ROS2HRIMessage], which provides a more coherent interface. The constructor has been simplified by removing explicit targets and sources parameters.


39-54: Streamlined send_message implementation

The send_message method now uses the generic _topic_api.publish method with explicit message type and QoS parameters, replacing the previous approach. This makes the code cleaner and more maintainable.


56-77: Added flexible register_callback method

Added a new register_callback method that provides a flexible way to register callbacks for source topics with proper error handling and defaults. This improves the API by making it more intuitive and consistent with the base class.


79-80: Added general_callback_preprocessor for message conversion

Added a new general_callback_preprocessor method that converts incoming ROS2 messages into ROS2HRIMessage instances with a fixed message author, improving message handling.

src/rai_core/rai/agents/langchain/agent.py (15)

15-30: Well-structured imports and dependencies

The imports are well-organized, grouped by standard library, third-party packages, and internal modules. The use of specific imports rather than wildcard imports improves code clarity.


32-34: Simple BaseState TypedDict definition

The BaseState TypedDict provides a clear structure for the agent's state, with a simple definition that includes a list of messages.


36-42: Clear message behavior type definition

The newMessageBehaviorType Literal type clearly defines the allowed values for message handling behavior, making the API more self-documenting and enabling type checking.


45-104: Comprehensive docstring with examples

The class docstring is thorough and well-structured, explaining:

  • The purpose of the agent
  • Its parameters with detailed descriptions
  • How to use the agent with examples
  • Different message behavior options

This makes the class easily understandable and usable.


106-134: Robust initialization with proper thread safety

The constructor initializes all necessary components including:

  • Logger
  • Agent runnable and state
  • Callback handlers
  • Thread-safe message buffer with lock
  • Thread management primitives for concurrency

The use of a lock for the message buffer and various events for signaling between threads shows good attention to thread safety.


136-141: Simple source subscription method

The subscribe_source method provides a clean interface for subscribing to message sources using the connector's register_callback method.


142-154: Thread-safe message handling in call

The __call__ method safely handles incoming messages by:

  1. Using a lock to protect buffer access
  2. Checking for buffer overflow and dropping oldest messages if needed
  3. Submitting interrupt tasks to an executor if needed
  4. Logging message reception
  5. Appending the message to the buffer

The thread safety and error handling here are well-implemented.


155-161: Clean agent start method

The run method checks if the agent is already running, creates and starts a new thread for the agent loop, sets the agent ready event, and logs the start. This prevents duplicate starts and ensures proper initialization.


163-170: Agent readiness check and wait methods

The ready and wait methods provide ways to check if the agent is ready and to wait until it's ready and its message queue is empty. This enables proper synchronization in client code.


172-181: Agent interruption handling

The _interrupt_agent_and_run method handles interrupting the agent if it's processing a message, waits until it's ready again, and clears the interrupt event. This mechanism allows for responsive handling of high-priority messages.


182-204: Main agent processing loop

The _run_agent method:

  1. Checks if there are messages to process
  2. Sets the agent ready status
  3. Reduces messages according to the configured behavior
  4. Converts messages to LangChain format
  5. Updates the agent state
  6. Runs the agent with callbacks
  7. Handles interruptions during processing
  8. Ensures the agent is marked as ready when done

The method is well-structured with proper error handling via try/finally.


205-209: Efficient agent run loop

The _run_loop method runs a loop that periodically checks if the agent should stop and if it's ready to process the next message. The short wait time (0.01s) ensures responsiveness while avoiding busy-waiting.


210-219: Clean agent stop implementation

The stop method:

  1. Sets the stop event
  2. Sets the interrupt event to stop any ongoing processing
  3. Waits for the agent to be ready
  4. Joins the thread if it exists
  5. Logs the agent stopping and stopped states

This ensures proper cleanup and prevents resource leaks.


220-238: Clear message reduction behavior implementation

The _apply_reduction_behavior static method implements different message handling strategies based on the configured behavior:

  • "take_all": processes all messages from oldest to newest
  • "keep_last": takes only the most recent message
  • "queue": processes one message at a time in FIFO order

The implementation is clear, well-commented, and raises appropriate errors for invalid behaviors.


240-257: Thread-safe message reduction

The _reduce_messages method:

  1. Acquires the buffer lock to safely access the message buffer
  2. Applies the configured reduction behavior
  3. Combines messages into a single message with concatenated text and aggregated media
  4. Returns a new HRIMessage with the combined content

The thread safety and message handling are well-implemented.

Copy link
Member

@maciejmajek maciejmajek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@maciejmajek maciejmajek merged commit c7b011b into development Apr 28, 2025
7 checks passed
@maciejmajek maciejmajek deleted the fix/react_agent branch April 28, 2025 09:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants