File size: 4,894 Bytes
0913c52
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
"""
OpenHands integration tool for coding tasks.

This tool provides access to an external OpenHands coding agent.
The agent instance should be passed via the tool context (ctx).
"""

import os
from typing import TYPE_CHECKING

from loguru import logger

from .registry import register_tool, register_toolset_desc

if TYPE_CHECKING:
    from openhands.sdk import Conversation, LocalConversation

register_toolset_desc(
    "openhands",
    "OpenHands coding toolset. This toolset provides access to an external AI coding agent "
    "that can read, write, and modify code files using natural language instructions. "
    "The agent maintains conversation history across calls within the same session. "
    "Use this for complex coding tasks that require multi-turn interactions.",
)


@register_tool(
    "openhands",
    {
        "type": "function",
        "function": {
            "name": "code_subagent",
            "description": (
                "Execute a coding task using the OpenHands external coding agent. "
                "This agent can read, write, and modify code files based on natural language instructions. "
                "The conversation history persists across calls, allowing for multi-turn coding sessions. "
                "IMPORTANT: Be specific about file paths and desired changes."
            ),
            "parameters": {
                "type": "object",
                "properties": {
                    "instruction": {
                        "type": "string",
                        "description": (
                            "Natural language instruction for the coding agent. "
                            "Be specific about what files to modify and what changes to make. "
                            "Example: 'Add error handling to the load_data function in src/utils.py'"
                        ),
                    },
                    "bg_info": {
                        "type": "string",
                        "description": (
                            "Background information for the coding agent, such as current working directory, information about the code base, background knowledge of the task. "
                        ),
                    },
                },
                "required": ["instruction", "bg_info"],
            },
        },
    },
)
def code_subagent(agent_state, instruction: str, bg_info: str) -> str:
    """
    Execute a coding task using the OpenHands agent.

    Args:
        instruction: Natural language instruction for the coding task
        bg_info: Background information for the coding task

    Returns:
        Result message from the coding agent
    """
    logger.debug("Calling OpenHands code_subagent with instruction: {}", instruction)

    if not instruction.strip():
        return "Error: instruction must be a non-empty string."

    enable_openhands = os.getenv("SCIEVO_ENABLE_OPENHANDS", "").strip().lower() in {
        "1",
        "true",
        "yes",
        "y",
    }
    if not enable_openhands:
        return (
            "Error: OpenHands toolset is disabled.\n"
            "Hint: set env `SCIEVO_ENABLE_OPENHANDS=1` to enable it, or switch to the Claude coding subagent "
            "(set `CODING_AGENT_VERSION=v3`)."
        )

    # Setup openhands paths first (must be before any openhands imports)
    # Keep this import local to avoid mutating sys.path unless OpenHands is explicitly enabled.
    from scievo.core import openhands_import  # noqa: F401

    conversation: "Conversation | LocalConversation" = getattr(
        agent_state, "openhands_conversation", None
    )
    if conversation is None:
        return "Error: openhands_conversation not found in agent state."

    try:
        # Send message to the OpenHands agent
        conversation.send_message(
            f"""\
# Requirements:
- At the end of your response, provide a detailed explanation of what you did and why.
- Ensure that all changes are made in a way that maintains the integrity of the codebase.

# Workspace
{os.path.abspath(agent_state.workspace.working_dir)}

# Background information:
{bg_info}

# Task:
{instruction}
"""
        )

        # Run the agent until completion
        conversation.run()

        if (e := conversation.state.events[-1]).source == "agent":
            last_response = "\n".join([c.text for c in e.llm_message.content])
        else:
            last_response = "Error: No response from the coding agent at the end."

        # Return success message
        # Note: The exact response format depends on OpenHands SDK API
        # This may need adjustment based on actual SDK implementation
        return f"""\
Coding task completed. The summary of changes is as follows:
{last_response}
"""

    except Exception as e:
        logger.exception("OpenHands agent error")
        return f"Error executing coding task: {str(e)}"