mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-09-30 23:13:15 +00:00
124 lines
4.2 KiB
Python
124 lines
4.2 KiB
Python
from typing import Any
|
|
from uuid import uuid4
|
|
|
|
from langchain.agents.middleware import AgentState, after_model
|
|
from langchain_core.messages import AnyMessage, ToolMessage
|
|
from langgraph.config import get_config
|
|
from langgraph.runtime import Runtime
|
|
|
|
from .check_message_queue import DASHBOARD_HANDOFF_MARKER
|
|
|
|
_DASHBOARD_SOURCE = "dashboard"
|
|
|
|
|
|
def get_every_message_since_last_human(state: AgentState) -> list[AnyMessage]:
|
|
messages = state["messages"]
|
|
last_human_idx = -1
|
|
for i in range(len(messages) - 1, -1, -1):
|
|
if messages[i].type == "human":
|
|
last_human_idx = i
|
|
break
|
|
return messages[last_human_idx + 1 :]
|
|
|
|
|
|
def check_if_model_messaged_user(messages: list[AnyMessage]) -> bool:
|
|
for msg in messages:
|
|
if msg.type == "tool" and msg.name in [
|
|
"slack_thread_reply",
|
|
"linear_comment",
|
|
]:
|
|
return True
|
|
return False
|
|
|
|
|
|
def check_if_confirming_completion(messages: list[AnyMessage]) -> bool:
|
|
for msg in messages:
|
|
if msg.type == "tool" and msg.name == "confirming_completion":
|
|
return True
|
|
return False
|
|
|
|
|
|
def check_if_no_op(messages: list[AnyMessage]) -> bool:
|
|
for msg in messages:
|
|
if msg.type == "tool" and msg.name == "no_op":
|
|
return True
|
|
return False
|
|
|
|
|
|
def _content_contains_text(content: object, text: str) -> bool:
|
|
if isinstance(content, str):
|
|
return text in content
|
|
if not isinstance(content, list):
|
|
return False
|
|
for block in content:
|
|
if isinstance(block, dict) and text in str(block.get("text", "")):
|
|
return True
|
|
return False
|
|
|
|
|
|
def _last_human_is_dashboard_handoff(state: AgentState) -> bool:
|
|
for msg in reversed(state["messages"]):
|
|
if msg.type == "human":
|
|
return _content_contains_text(msg.content, DASHBOARD_HANDOFF_MARKER)
|
|
return False
|
|
|
|
|
|
def _is_dashboard_source() -> bool:
|
|
try:
|
|
config = get_config()
|
|
except RuntimeError:
|
|
return False
|
|
configurable = config.get("configurable", {})
|
|
if not isinstance(configurable, dict):
|
|
return False
|
|
return configurable.get("source") == _DASHBOARD_SOURCE
|
|
|
|
|
|
@after_model
|
|
def ensure_no_empty_msg(state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
|
|
last_msg = state["messages"][-1]
|
|
has_contents = bool(last_msg.text())
|
|
has_tool_calls = bool(last_msg.tool_calls)
|
|
if not has_tool_calls and not has_contents:
|
|
messages_since_last_human = get_every_message_since_last_human(state)
|
|
if check_if_no_op(messages_since_last_human):
|
|
return None
|
|
|
|
if check_if_model_messaged_user(messages_since_last_human):
|
|
return None
|
|
|
|
tc_id = str(uuid4())
|
|
last_msg.tool_calls = [{"name": "no_op", "args": {}, "id": tc_id}]
|
|
no_op_tool_msg = ToolMessage(
|
|
content="No operation performed."
|
|
+ "Please continue with the task, ensuring you ALWAYS call at least one tool in"
|
|
+ " every message unless you are absolutely sure the task has been fully completed.",
|
|
tool_call_id=tc_id,
|
|
)
|
|
|
|
return {"messages": [last_msg, no_op_tool_msg]}
|
|
|
|
if has_contents and not has_tool_calls:
|
|
messages_since_last_human = get_every_message_since_last_human(state)
|
|
|
|
if (
|
|
check_if_model_messaged_user(messages_since_last_human)
|
|
or check_if_confirming_completion(messages_since_last_human)
|
|
or _is_dashboard_source()
|
|
or _last_human_is_dashboard_handoff(state)
|
|
):
|
|
return None
|
|
|
|
tc_id = str(uuid4())
|
|
last_msg.tool_calls = [{"name": "confirming_completion", "args": {}, "id": tc_id}]
|
|
no_op_tool_msg = ToolMessage(
|
|
content="Confirming task completion. I see you did not call a tool, which would end the task, however you haven't called a tool to message the user or open a pull request."
|
|
+ "This may indicate premature termination - please ensure you fully complete the task before ending it. "
|
|
+ "If you do not call any tools it will end the task.",
|
|
name="confirming_completion",
|
|
tool_call_id=tc_id,
|
|
)
|
|
|
|
return {"messages": [last_msg, no_op_tool_msg]}
|
|
|
|
return None
|