Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a038627
chore: remove old code gen artifacts
onur-askui Jul 11, 2025
b75c67e
feat: update proto file
onur-askui Jul 11, 2025
2329387
feat: introduce codegen and update references
onur-askui Jul 11, 2025
ffbd226
feat: feat update `askui_controller`
onur-askui Jul 15, 2025
3b42d89
feat: introduce basic tests
onur-askui Jul 15, 2025
0093c92
chore: remove readme
onur-askui Jul 15, 2025
7b3a9ba
chore: merge grpc:gen and grpc:fix-import
onur-askui Jul 15, 2025
7f96091
chore: ignore formatting for generated code
onur-askui Jul 15, 2025
80d15c1
chore: extract grpc generation to dedicated script
onur-askui Jul 18, 2025
7fc75eb
chore: specify generated path to exclude
onur-askui Jul 18, 2025
82c2a66
format: use backtick for default parameters in docstring
onur-askui Jul 18, 2025
457917d
feat: introduce controller json schema and codegen
onur-askui Jul 18, 2025
b4613e3
feat: introduce helper functions for json api
onur-askui Jul 18, 2025
a8828ea
feat: update controller to support new commands
onur-askui Jul 18, 2025
dfcecc8
test: add tests for newly added controller commands
onur-askui Jul 18, 2025
1fde246
style: reorganise imports
onur-askui Jul 18, 2025
eede6cf
style: update docstring for style annotations
onur-askui Jul 18, 2025
cf60fa7
chore: create a dev dep group and move dev deps
onur-askui Jul 21, 2025
7d02bcc
refactor: remove wrapper functions for RenderImage and RenderText
onur-askui Jul 22, 2025
7e2ee62
refactor: fix type error
onur-askui Jul 22, 2025
4acbdad
Merge pull request #98 from askui/feat/expose-visualisation
onur-askui Jul 22, 2025
0f81da4
feat: add GetCursorPositionTool to VisionAgent
danyalxahid-askui Jul 24, 2025
27981ed
Merge branch 'feat/update-controller-grpc' into CL-1581-lib-python-ge…
danyalxahid-askui Jul 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions pdm.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,14 @@ typecheck = "mypy"
"typecheck:all" = "mypy ."
"chat:api" = "uvicorn askui.chat.api.app:app --reload --port 9261"
"mcp:dev" = "mcp dev src/askui/mcp/__init__.py"
<<<<<<< HEAD
=======
"qa:fix" = { composite = [
"typecheck:all",
"format",
"lint:fix",
] }
>>>>>>> feat/update-controller-grpc
"grpc:gen" = "bash scripts/grpc-gen.sh"
"json:gen" = "datamodel-codegen --output-model-type pydantic_v2.BaseModel --input src/askui/tools/askui/askui_ui_controller_grpc/json_schema/ --input-file-type jsonschema --output src/askui/tools/askui/askui_ui_controller_grpc/generated/"

Expand Down Expand Up @@ -222,3 +225,7 @@ pynput = [
web = [
"playwright>=1.41.0",
]
dev = [
"datamodel-code-generator>=0.31.2",
"grpcio-tools>=1.73.1",
]
11 changes: 5 additions & 6 deletions src/askui/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@
from askui.agent_base import AgentBase
from askui.container import telemetry
from askui.locators.locators import Locator
from askui.models.shared.settings import (
COMPUTER_USE_20241022_BETA_FLAG,
COMPUTER_USE_20250124_BETA_FLAG,
ActSettings,
MessageSettings,
)
from askui.models.shared.settings import (COMPUTER_USE_20241022_BETA_FLAG,
COMPUTER_USE_20250124_BETA_FLAG,
ActSettings, MessageSettings)
from askui.models.shared.tools import Tool
from askui.tools.computer import Computer20241022Tool, Computer20250124Tool
from askui.tools.cursor_position_tool import CursorPositionTool
from askui.tools.exception_tool import ExceptionTool

from .logger import logger
Expand Down Expand Up @@ -115,6 +113,7 @@ def __init__(
models=models,
tools=[
ExceptionTool(),
CursorPositionTool(agent_os=self.tools.os),
]
+ (act_tools or []),
agent_os=self.tools.os,
Expand Down
90 changes: 90 additions & 0 deletions src/askui/tools/askui/askui_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,55 @@ def get_automation_target_list(
return response

@telemetry.record_call()
<<<<<<< HEAD
def set_test_configuration(
self,
default_capture_parameters: controller_v1_pbs.CaptureParameters | None = None,
mouse_delay_ms: int = 0,
keyboard_delay_ms: int = 0,
) -> None:
"""
Configure test settings including default capture parameters and delays.

Args:
default_capture_parameters
(controller_v1_pbs.CaptureParameters | None, optional):
Default capture parameters with displayID and captureArea.
Defaults to `None`.
mouse_delay_ms (int, optional): Mouse delay in milliseconds.
Defaults to `0`.
keyboard_delay_ms (int, optional): Keyboard delay in milliseconds.
Defaults to `0`.
"""
assert isinstance(self._stub, controller_v1.ControllerAPIStub), (
"Stub is not initialized"
)

self._reporter.add_message(
"AgentOS",
f"set_test_configuration("
f"{default_capture_parameters}, "
f"{mouse_delay_ms}, "
f"{keyboard_delay_ms})",
)

# Use provided capture parameters or create empty one
capture_params = (
default_capture_parameters or controller_v1_pbs.CaptureParameters()
)

self._stub.SetTestConfiguration(
controller_v1_pbs.Reuqest_SetTestConfiguration(
sessionInfo=self._session_info,
defaultCaptureParameters=capture_params,
mouseDelayInMilliseconds=mouse_delay_ms,
keyboardDelayInMilliseconds=keyboard_delay_ms,
)
)

@telemetry.record_call()
=======
>>>>>>> feat/update-controller-grpc
def set_mouse_delay(self, delay_ms: int) -> None:
"""
Configure mouse action delay.
Expand Down Expand Up @@ -1083,7 +1132,12 @@ def remove_all_actions(self) -> None:
controller_v1_pbs.Request_RemoveAllActions(sessionInfo=self._session_info)
)

<<<<<<< HEAD
@telemetry.record_call(exclude={"message"})
def send_message(self, message: str) -> controller_v1_pbs.Response_Send:
=======
def _send_message(self, message: str) -> controller_v1_pbs.Response_Send:
>>>>>>> feat/update-controller-grpc
"""
Send a general message to the controller.

Expand Down Expand Up @@ -1121,7 +1175,11 @@ def get_mouse_position(self) -> Coordinate:
self._session_guid
).model_dump_json(exclude_unset=True)
self._reporter.add_message("AgentOS", "get_mouse_position()")
<<<<<<< HEAD
res = self.send_message(req_json)
=======
res = self._send_message(req_json)
>>>>>>> feat/update-controller-grpc
parsed_res = AskuiAgentosSendResponseSchema.model_validate_json(res.message)
return Coordinate(
x=parsed_res.message.command.response.position.x.root, # type: ignore[union-attr]
Expand All @@ -1144,7 +1202,11 @@ def set_mouse_position(self, x: int, y: int) -> None:
x, y, self._session_guid
).model_dump_json(exclude_unset=True)
self._reporter.add_message("AgentOS", f"set_mouse_position({x},{y})")
<<<<<<< HEAD
self.send_message(req_json)
=======
self._send_message(req_json)
>>>>>>> feat/update-controller-grpc

@telemetry.record_call()
def render_quad(self, style: RenderObjectStyle) -> int:
Expand All @@ -1164,7 +1226,11 @@ def render_quad(self, style: RenderObjectStyle) -> int:
req_json = create_quad_command(style, self._session_guid).model_dump_json(
exclude_unset=True, by_alias=True
)
<<<<<<< HEAD
res = self.send_message(req_json)
=======
res = self._send_message(req_json)
>>>>>>> feat/update-controller-grpc
parsed_response = AskuiAgentosSendResponseSchema.model_validate_json(
res.message
)
Expand All @@ -1189,7 +1255,11 @@ def render_line(self, style: RenderObjectStyle, points: list[Coordinate]) -> int
req = create_line_command(style, points, self._session_guid).model_dump_json(
exclude_unset=True, by_alias=True
)
<<<<<<< HEAD
res = self.send_message(req)
=======
res = self._send_message(req)
>>>>>>> feat/update-controller-grpc
parsed_response = AskuiAgentosSendResponseSchema.model_validate_json(
res.message
)
Expand All @@ -1214,7 +1284,11 @@ def render_image(self, style: RenderObjectStyle, image_data: str) -> int:
req = create_image_command(
style, image_data, self._session_guid
).model_dump_json(exclude_unset=True, by_alias=True)
<<<<<<< HEAD
res = self.send_message(req)
=======
res = self._send_message(req)
>>>>>>> feat/update-controller-grpc

parsed_response = AskuiAgentosSendResponseSchema.model_validate_json(
res.message
Expand All @@ -1241,7 +1315,11 @@ def render_text(self, style: RenderObjectStyle, content: str) -> int:
req = create_text_command(style, content, self._session_guid).model_dump_json(
exclude_unset=True, by_alias=True
)
<<<<<<< HEAD
res = self.send_message(req)
=======
res = self._send_message(req)
>>>>>>> feat/update-controller-grpc
parsed_response = AskuiAgentosSendResponseSchema.model_validate_json(
res.message
)
Expand All @@ -1268,7 +1346,11 @@ def update_render_object(self, object_id: int, style: RenderObjectStyle) -> None
req = create_update_render_object_command(
object_id, style, self._session_guid
).model_dump_json(exclude_unset=True, by_alias=True)
<<<<<<< HEAD
self.send_message(req)
=======
self._send_message(req)
>>>>>>> feat/update-controller-grpc

@telemetry.record_call()
def delete_render_object(self, object_id: int) -> None:
Expand All @@ -1285,7 +1367,11 @@ def delete_render_object(self, object_id: int) -> None:
req = create_delete_render_object_command(
object_id, self._session_guid
).model_dump_json(exclude_unset=True, by_alias=True)
<<<<<<< HEAD
self.send_message(req)
=======
self._send_message(req)
>>>>>>> feat/update-controller-grpc

@telemetry.record_call()
def clear_render_objects(self) -> None:
Expand All @@ -1299,4 +1385,8 @@ def clear_render_objects(self) -> None:
req = create_clear_render_objects_command(self._session_guid).model_dump_json(
exclude_unset=True, by_alias=True
)
<<<<<<< HEAD
self.send_message(req)
=======
self._send_message(req)
>>>>>>> feat/update-controller-grpc
29 changes: 29 additions & 0 deletions src/askui/tools/cursor_position_tool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from askui.models.shared.tools import Tool
from askui.tools.agent_os import AgentOs, Coordinate


class CursorPositionTool(Tool):
"""
Tool to get the current mouse cursor position.
"""

def __init__(self, agent_os: AgentOs):
super().__init__(
name="get_cursor_position",
description=
"""
Gets the current position of the mouse cursor.
Returns a JSON string with 'x' and 'y' coordinates, e.g. {"x": 100, "y": 200}.
"""
)
self._agent_os: AgentOs = agent_os

def __call__(self) -> str:
"""
Gets the current mouse cursor position.

Returns:
str: A JSON string with 'x' and 'y' coordinates of the cursor, e.g. {"x": 100, "y": 200}.
"""
point: Coordinate = self._agent_os.get_mouse_position()
return point.model_dump_json()