Skip to content

Commit 80a2d82

Browse files
authored
Merge pull request #290 from ServiceNow/aj/hilt
Human in the Loop Agent UI and Agent interface
2 parents 571b3f5 + 02347f8 commit 80a2d82

File tree

11 files changed

+1936
-0
lines changed

11 files changed

+1936
-0
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,4 @@ hint = [
109109
[project.scripts]
110110
agentlab-assistant = "agentlab.ui_assistant:main"
111111
agentlab-xray = "agentlab.analyze.agent_xray:main"
112+
agentlab-mentor = "agentlab.agents.hitl_agent.launch_hint_ui:main"

src/agentlab/agents/agent_utils.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
import copy
2+
13
from PIL import Image, ImageDraw
24
from playwright.sync_api import Page
35

6+
from agentlab.analyze import overlay_utils
7+
from agentlab.llm.llm_utils import img_to_base_64
8+
49

510
def draw_mouse_pointer(image: Image.Image, x: int, y: int) -> Image.Image:
611
"""
@@ -128,3 +133,24 @@ def zoom_webpage(page: Page, zoom_factor: float = 1.5):
128133

129134
page.evaluate(f"document.documentElement.style.zoom='{zoom_factor*100}%'")
130135
return page
136+
137+
138+
def overlay_action(obs, action):
139+
"""Overlays actions on screenshot in-place"""
140+
act_img = copy.deepcopy(obs["screenshot"])
141+
act_img = Image.fromarray(act_img)
142+
143+
new_obs_properties = copy.deepcopy(obs["extra_element_properties"])
144+
import os
145+
146+
if os.getenv("AGENTLAB_USE_RETINA"):
147+
# HACK: divide everything by 2 in the obs
148+
# TODO: make this more robust by changing login in annotate_action directly (or maybe in the obs section?)
149+
for key, value in new_obs_properties.items():
150+
try:
151+
new_obs_properties[key]["bbox"] = [elem / 2 for elem in value["bbox"]]
152+
except:
153+
pass
154+
155+
overlay_utils.annotate_action(act_img, action, properties=new_obs_properties)
156+
return img_to_base_64(act_img)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from typing_extensions import Protocol
2+
3+
from agentlab.agents.agent_args import AgentArgs
4+
5+
6+
class MultiCandidateAgent(Protocol):
7+
"""
8+
Protocol for agents that generate multiple candidates for get_action.
9+
10+
This protocol defines the contract for agents that can generate
11+
multiple candidate actions and allow selection of one of them for execution.
12+
"""
13+
14+
def get_candidate_generations(
15+
self, obs: dict, hint: list[str] | None = None, n_candidates: int = 3
16+
) -> "list[dict]":
17+
"""
18+
Generate multiple candidate actions for the given observation.
19+
20+
You can pass extra info in agent_info to update internal state of the
21+
agent based on the selected candidate. Your internal state management
22+
should be robust to multiple calls to the get_candidate_generations method
23+
in a single step.
24+
25+
Args:
26+
obs: The current observation dictionary containing environment state
27+
hint: Optional list of hint strings to guide candidate generation
28+
n_candidates: Number of candidate actions to generate
29+
"""
30+
...
31+
32+
def update_agent_state_from_selected_candidate(self, output: dict):
33+
"""
34+
Update the agent's internal state based on the selected candidate.
35+
This can include any memory or planning updates.
36+
37+
Args:
38+
output: The selected candidate action dictionary
39+
"""
40+
pass
41+
42+
43+
class MultiCandidateAgentArgs(AgentArgs):
44+
def make_agent(self) -> MultiCandidateAgent: ...
45+
46+
def __post_init__(self):
47+
"""Prefix subagent name with 'MC-'."""
48+
super().__post_init__()
49+
if hasattr(self, "agent_name") and self.agent_name:
50+
self.agent_name = "MC-" + self.agent_name

0 commit comments

Comments
 (0)