oagi-core 0.10.3__py3-none-any.whl → 0.12.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- oagi/__init__.py +1 -3
- oagi/actor/__init__.py +21 -0
- oagi/{task → actor}/async_.py +23 -7
- oagi/{task → actor}/async_short.py +1 -1
- oagi/actor/base.py +222 -0
- oagi/{task → actor}/short.py +1 -1
- oagi/{task → actor}/sync.py +21 -5
- oagi/agent/default.py +5 -0
- oagi/agent/factories.py +75 -3
- oagi/agent/observer/exporters.py +6 -0
- oagi/agent/observer/report_template.html +19 -0
- oagi/agent/tasker/planner.py +31 -19
- oagi/agent/tasker/taskee_agent.py +26 -7
- oagi/agent/tasker/tasker_agent.py +4 -0
- oagi/cli/agent.py +54 -30
- oagi/client/async_.py +54 -96
- oagi/client/base.py +81 -133
- oagi/client/sync.py +52 -99
- oagi/constants.py +7 -2
- oagi/handler/__init__.py +16 -0
- oagi/handler/_macos.py +137 -0
- oagi/handler/_windows.py +101 -0
- oagi/handler/async_pyautogui_action_handler.py +8 -0
- oagi/handler/capslock_manager.py +55 -0
- oagi/handler/pyautogui_action_handler.py +21 -39
- oagi/server/session_store.py +3 -3
- oagi/server/socketio_server.py +4 -4
- oagi/task/__init__.py +22 -8
- oagi/types/__init__.py +2 -1
- oagi/types/models/__init__.py +0 -2
- oagi/types/models/action.py +4 -1
- oagi/types/models/client.py +1 -17
- oagi/types/step_observer.py +2 -0
- oagi/types/url.py +25 -0
- oagi/utils/__init__.py +12 -0
- oagi/utils/output_parser.py +166 -0
- oagi/utils/prompt_builder.py +44 -0
- {oagi_core-0.10.3.dist-info → oagi_core-0.12.0.dist-info}/METADATA +90 -10
- oagi_core-0.12.0.dist-info/RECORD +76 -0
- oagi/task/base.py +0 -158
- oagi_core-0.10.3.dist-info/RECORD +0 -70
- {oagi_core-0.10.3.dist-info → oagi_core-0.12.0.dist-info}/WHEEL +0 -0
- {oagi_core-0.10.3.dist-info → oagi_core-0.12.0.dist-info}/entry_points.txt +0 -0
- {oagi_core-0.10.3.dist-info → oagi_core-0.12.0.dist-info}/licenses/LICENSE +0 -0
oagi/handler/_macos.py
CHANGED
|
@@ -6,6 +6,15 @@
|
|
|
6
6
|
# Licensed under the MIT License.
|
|
7
7
|
# -----------------------------------------------------------------------------
|
|
8
8
|
|
|
9
|
+
"""macOS-specific keyboard and mouse input handling.
|
|
10
|
+
|
|
11
|
+
This module provides:
|
|
12
|
+
- macos_click(): Fix for PyAutoGUI multi-click bug on macOS
|
|
13
|
+
- typewrite_exact(): Type text exactly, ignoring system capslock state
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import time
|
|
17
|
+
|
|
9
18
|
import pyautogui
|
|
10
19
|
|
|
11
20
|
from ..exceptions import check_optional_dependency
|
|
@@ -13,6 +22,134 @@ from ..exceptions import check_optional_dependency
|
|
|
13
22
|
check_optional_dependency("Quartz", "macOS multiple clicks", "desktop")
|
|
14
23
|
import Quartz # noqa: E402
|
|
15
24
|
|
|
25
|
+
# macOS virtual key codes for typeable characters
|
|
26
|
+
KEYCODE_MAP = {
|
|
27
|
+
"a": 0x00,
|
|
28
|
+
"b": 0x0B,
|
|
29
|
+
"c": 0x08,
|
|
30
|
+
"d": 0x02,
|
|
31
|
+
"e": 0x0E,
|
|
32
|
+
"f": 0x03,
|
|
33
|
+
"g": 0x05,
|
|
34
|
+
"h": 0x04,
|
|
35
|
+
"i": 0x22,
|
|
36
|
+
"j": 0x26,
|
|
37
|
+
"k": 0x28,
|
|
38
|
+
"l": 0x25,
|
|
39
|
+
"m": 0x2E,
|
|
40
|
+
"n": 0x2D,
|
|
41
|
+
"o": 0x1F,
|
|
42
|
+
"p": 0x23,
|
|
43
|
+
"q": 0x0C,
|
|
44
|
+
"r": 0x0F,
|
|
45
|
+
"s": 0x01,
|
|
46
|
+
"t": 0x11,
|
|
47
|
+
"u": 0x20,
|
|
48
|
+
"v": 0x09,
|
|
49
|
+
"w": 0x0D,
|
|
50
|
+
"x": 0x07,
|
|
51
|
+
"y": 0x10,
|
|
52
|
+
"z": 0x06,
|
|
53
|
+
"1": 0x12,
|
|
54
|
+
"2": 0x13,
|
|
55
|
+
"3": 0x14,
|
|
56
|
+
"4": 0x15,
|
|
57
|
+
"5": 0x17,
|
|
58
|
+
"6": 0x16,
|
|
59
|
+
"7": 0x1A,
|
|
60
|
+
"8": 0x1C,
|
|
61
|
+
"9": 0x19,
|
|
62
|
+
"0": 0x1D,
|
|
63
|
+
" ": 0x31, # space
|
|
64
|
+
"-": 0x1B,
|
|
65
|
+
"=": 0x18,
|
|
66
|
+
"[": 0x21,
|
|
67
|
+
"]": 0x1E,
|
|
68
|
+
"\\": 0x2A,
|
|
69
|
+
";": 0x29,
|
|
70
|
+
"'": 0x27,
|
|
71
|
+
"`": 0x32,
|
|
72
|
+
",": 0x2B,
|
|
73
|
+
".": 0x2F,
|
|
74
|
+
"/": 0x2C,
|
|
75
|
+
"\t": 0x30, # tab
|
|
76
|
+
"\n": 0x24, # return
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# Characters that require shift key (on US keyboard layout)
|
|
80
|
+
SHIFT_CHARS = set('~!@#$%^&*()_+{}|:"<>?ABCDEFGHIJKLMNOPQRSTUVWXYZ')
|
|
81
|
+
|
|
82
|
+
# Mapping of shifted characters to their base key
|
|
83
|
+
SHIFT_KEY_MAP = {
|
|
84
|
+
"~": "`",
|
|
85
|
+
"!": "1",
|
|
86
|
+
"@": "2",
|
|
87
|
+
"#": "3",
|
|
88
|
+
"$": "4",
|
|
89
|
+
"%": "5",
|
|
90
|
+
"^": "6",
|
|
91
|
+
"&": "7",
|
|
92
|
+
"*": "8",
|
|
93
|
+
"(": "9",
|
|
94
|
+
")": "0",
|
|
95
|
+
"_": "-",
|
|
96
|
+
"+": "=",
|
|
97
|
+
"{": "[",
|
|
98
|
+
"}": "]",
|
|
99
|
+
"|": "\\",
|
|
100
|
+
":": ";",
|
|
101
|
+
'"': "'",
|
|
102
|
+
"<": ",",
|
|
103
|
+
">": ".",
|
|
104
|
+
"?": "/",
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def typewrite_exact(text: str, interval: float = 0.01) -> None:
|
|
109
|
+
"""Type text exactly as specified, ignoring system capslock state.
|
|
110
|
+
|
|
111
|
+
This function uses Quartz CGEventCreateKeyboardEvent with explicit
|
|
112
|
+
flag control via CGEventSetFlags() to type each character with the
|
|
113
|
+
correct case, regardless of the system's capslock state.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
text: The text to type exactly as specified
|
|
117
|
+
interval: Time in seconds between each character (default: 0.01)
|
|
118
|
+
"""
|
|
119
|
+
for char in text:
|
|
120
|
+
# Determine if this character needs shift
|
|
121
|
+
needs_shift = char in SHIFT_CHARS
|
|
122
|
+
|
|
123
|
+
# Get the base key (for shifted chars, look up the unshifted version)
|
|
124
|
+
if char.isupper():
|
|
125
|
+
base_char = char.lower()
|
|
126
|
+
elif char in SHIFT_KEY_MAP:
|
|
127
|
+
base_char = SHIFT_KEY_MAP[char]
|
|
128
|
+
else:
|
|
129
|
+
base_char = char
|
|
130
|
+
|
|
131
|
+
# Get keycode for the base character
|
|
132
|
+
keycode = KEYCODE_MAP.get(base_char)
|
|
133
|
+
if keycode is None:
|
|
134
|
+
# Character not in our keycode map, skip it
|
|
135
|
+
continue
|
|
136
|
+
|
|
137
|
+
# Set flags: shift if needed, otherwise clear all flags
|
|
138
|
+
flags = Quartz.kCGEventFlagMaskShift if needs_shift else 0
|
|
139
|
+
|
|
140
|
+
# Key down
|
|
141
|
+
event_down = Quartz.CGEventCreateKeyboardEvent(None, keycode, True)
|
|
142
|
+
Quartz.CGEventSetFlags(event_down, flags)
|
|
143
|
+
Quartz.CGEventPost(Quartz.kCGHIDEventTap, event_down)
|
|
144
|
+
|
|
145
|
+
# Key up
|
|
146
|
+
event_up = Quartz.CGEventCreateKeyboardEvent(None, keycode, False)
|
|
147
|
+
Quartz.CGEventSetFlags(event_up, flags)
|
|
148
|
+
Quartz.CGEventPost(Quartz.kCGHIDEventTap, event_up)
|
|
149
|
+
|
|
150
|
+
if interval > 0:
|
|
151
|
+
time.sleep(interval)
|
|
152
|
+
|
|
16
153
|
|
|
17
154
|
def macos_click(x: int, y: int, clicks: int = 1) -> None:
|
|
18
155
|
"""
|
oagi/handler/_windows.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# -----------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) OpenAGI Foundation
|
|
3
|
+
# All rights reserved.
|
|
4
|
+
#
|
|
5
|
+
# This file is part of the official API project.
|
|
6
|
+
# Licensed under the MIT License.
|
|
7
|
+
# -----------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
"""Windows-specific keyboard input handling.
|
|
10
|
+
|
|
11
|
+
This module provides typewrite_exact() which types text exactly as specified,
|
|
12
|
+
ignoring the system's capslock state by using SendInput with KEYEVENTF_UNICODE.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import ctypes
|
|
16
|
+
import time
|
|
17
|
+
from ctypes import wintypes
|
|
18
|
+
|
|
19
|
+
INPUT_KEYBOARD = 1
|
|
20
|
+
KEYEVENTF_UNICODE = 0x0004
|
|
21
|
+
KEYEVENTF_KEYUP = 0x0002
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class KEYBDINPUT(ctypes.Structure):
|
|
25
|
+
_fields_ = [
|
|
26
|
+
("wVk", wintypes.WORD),
|
|
27
|
+
("wScan", wintypes.WORD),
|
|
28
|
+
("dwFlags", wintypes.DWORD),
|
|
29
|
+
("time", wintypes.DWORD),
|
|
30
|
+
("dwExtraInfo", ctypes.POINTER(ctypes.c_ulong)),
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class MOUSEINPUT(ctypes.Structure):
|
|
35
|
+
_fields_ = [
|
|
36
|
+
("dx", ctypes.c_long),
|
|
37
|
+
("dy", ctypes.c_long),
|
|
38
|
+
("mouseData", wintypes.DWORD),
|
|
39
|
+
("dwFlags", wintypes.DWORD),
|
|
40
|
+
("time", wintypes.DWORD),
|
|
41
|
+
("dwExtraInfo", ctypes.POINTER(ctypes.c_ulong)),
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class HARDWAREINPUT(ctypes.Structure):
|
|
46
|
+
_fields_ = [
|
|
47
|
+
("uMsg", wintypes.DWORD),
|
|
48
|
+
("wParamL", wintypes.WORD),
|
|
49
|
+
("wParamH", wintypes.WORD),
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class INPUT(ctypes.Structure):
|
|
54
|
+
class _I(ctypes.Union):
|
|
55
|
+
_fields_ = [
|
|
56
|
+
("ki", KEYBDINPUT),
|
|
57
|
+
("mi", MOUSEINPUT),
|
|
58
|
+
("hi", HARDWAREINPUT),
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
_anonymous_ = ("i",)
|
|
62
|
+
_fields_ = [
|
|
63
|
+
("type", wintypes.DWORD),
|
|
64
|
+
("i", _I),
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# Configure SendInput with proper argtypes for 64-bit compatibility
|
|
69
|
+
SendInput = ctypes.windll.user32.SendInput
|
|
70
|
+
SendInput.argtypes = [wintypes.UINT, ctypes.POINTER(INPUT), ctypes.c_int]
|
|
71
|
+
SendInput.restype = wintypes.UINT
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def typewrite_exact(text: str, interval: float = 0.01) -> None:
|
|
75
|
+
"""Type text exactly using Unicode input - ignores capslock, keyboard layout, etc.
|
|
76
|
+
|
|
77
|
+
This function uses SendInput with KEYEVENTF_UNICODE to send characters
|
|
78
|
+
directly by their Unicode codepoint, completely bypassing keyboard state
|
|
79
|
+
(capslock, layout, etc.).
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
text: The text to type exactly as specified
|
|
83
|
+
interval: Time in seconds between each character (default: 0.01)
|
|
84
|
+
"""
|
|
85
|
+
for char in text:
|
|
86
|
+
inputs = (INPUT * 2)()
|
|
87
|
+
|
|
88
|
+
# Key down
|
|
89
|
+
inputs[0].type = INPUT_KEYBOARD
|
|
90
|
+
inputs[0].ki.wScan = ord(char)
|
|
91
|
+
inputs[0].ki.dwFlags = KEYEVENTF_UNICODE
|
|
92
|
+
|
|
93
|
+
# Key up
|
|
94
|
+
inputs[1].type = INPUT_KEYBOARD
|
|
95
|
+
inputs[1].ki.wScan = ord(char)
|
|
96
|
+
inputs[1].ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP
|
|
97
|
+
|
|
98
|
+
SendInput(2, inputs, ctypes.sizeof(INPUT))
|
|
99
|
+
|
|
100
|
+
if interval > 0:
|
|
101
|
+
time.sleep(interval)
|
|
@@ -29,6 +29,14 @@ class AsyncPyautoguiActionHandler:
|
|
|
29
29
|
self.sync_handler = PyautoguiActionHandler(config=config)
|
|
30
30
|
self.config = config or PyautoguiConfig()
|
|
31
31
|
|
|
32
|
+
def reset(self):
|
|
33
|
+
"""Reset handler state.
|
|
34
|
+
|
|
35
|
+
Delegates to the underlying synchronous handler's reset method.
|
|
36
|
+
Called at automation start/end and when FINISH action is received.
|
|
37
|
+
"""
|
|
38
|
+
self.sync_handler.reset()
|
|
39
|
+
|
|
32
40
|
async def __call__(self, actions: list[Action]) -> None:
|
|
33
41
|
"""
|
|
34
42
|
Execute actions asynchronously using a thread pool executor.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# -----------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) OpenAGI Foundation
|
|
3
|
+
# All rights reserved.
|
|
4
|
+
#
|
|
5
|
+
# This file is part of the official API project.
|
|
6
|
+
# Licensed under the MIT License.
|
|
7
|
+
# -----------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CapsLockManager:
|
|
11
|
+
"""Manages caps lock state for text transformation.
|
|
12
|
+
|
|
13
|
+
This class maintains an internal caps lock state that can be toggled
|
|
14
|
+
independently of the system's caps lock state. This allows for consistent
|
|
15
|
+
text case handling during automation regardless of the system state.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, mode: str = "session"):
|
|
19
|
+
"""Initialize caps lock manager.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
mode: Either "session" (internal state) or "system" (OS-level)
|
|
23
|
+
"""
|
|
24
|
+
self.mode = mode
|
|
25
|
+
self.caps_enabled = False
|
|
26
|
+
|
|
27
|
+
def reset(self):
|
|
28
|
+
"""Reset caps lock state to default (off).
|
|
29
|
+
|
|
30
|
+
Called at automation start/end and when FINISH action is received.
|
|
31
|
+
"""
|
|
32
|
+
self.caps_enabled = False
|
|
33
|
+
|
|
34
|
+
def toggle(self):
|
|
35
|
+
"""Toggle caps lock state in session mode."""
|
|
36
|
+
if self.mode == "session":
|
|
37
|
+
self.caps_enabled = not self.caps_enabled
|
|
38
|
+
|
|
39
|
+
def transform_text(self, text: str) -> str:
|
|
40
|
+
"""Transform text based on caps lock state.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
text: Input text to transform
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
Transformed text (uppercase alphabets if caps enabled in session mode)
|
|
47
|
+
"""
|
|
48
|
+
if self.mode == "session" and self.caps_enabled:
|
|
49
|
+
# Transform letters to uppercase, preserve special characters
|
|
50
|
+
return "".join(c.upper() if c.isalpha() else c for c in text)
|
|
51
|
+
return text
|
|
52
|
+
|
|
53
|
+
def should_use_system_capslock(self) -> bool:
|
|
54
|
+
"""Check if system-level caps lock should be used."""
|
|
55
|
+
return self.mode == "system"
|
|
@@ -13,48 +13,15 @@ from pydantic import BaseModel, Field
|
|
|
13
13
|
|
|
14
14
|
from ..exceptions import check_optional_dependency
|
|
15
15
|
from ..types import Action, ActionType, parse_coords, parse_drag_coords, parse_scroll
|
|
16
|
+
from .capslock_manager import CapsLockManager
|
|
16
17
|
|
|
17
18
|
check_optional_dependency("pyautogui", "PyautoguiActionHandler", "desktop")
|
|
18
19
|
import pyautogui # noqa: E402
|
|
19
20
|
|
|
20
21
|
if sys.platform == "darwin":
|
|
21
22
|
from . import _macos
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class CapsLockManager:
|
|
25
|
-
"""Manages caps lock state for text transformation."""
|
|
26
|
-
|
|
27
|
-
def __init__(self, mode: str = "session"):
|
|
28
|
-
"""Initialize caps lock manager.
|
|
29
|
-
|
|
30
|
-
Args:
|
|
31
|
-
mode: Either "session" (internal state) or "system" (OS-level)
|
|
32
|
-
"""
|
|
33
|
-
self.mode = mode
|
|
34
|
-
self.caps_enabled = False
|
|
35
|
-
|
|
36
|
-
def toggle(self):
|
|
37
|
-
"""Toggle caps lock state in session mode."""
|
|
38
|
-
if self.mode == "session":
|
|
39
|
-
self.caps_enabled = not self.caps_enabled
|
|
40
|
-
|
|
41
|
-
def transform_text(self, text: str) -> str:
|
|
42
|
-
"""Transform text based on caps lock state.
|
|
43
|
-
|
|
44
|
-
Args:
|
|
45
|
-
text: Input text to transform
|
|
46
|
-
|
|
47
|
-
Returns:
|
|
48
|
-
Transformed text (uppercase if caps enabled in session mode)
|
|
49
|
-
"""
|
|
50
|
-
if self.mode == "session" and self.caps_enabled:
|
|
51
|
-
# Transform letters to uppercase, preserve special characters
|
|
52
|
-
return "".join(c.upper() if c.isalpha() else c for c in text)
|
|
53
|
-
return text
|
|
54
|
-
|
|
55
|
-
def should_use_system_capslock(self) -> bool:
|
|
56
|
-
"""Check if system-level caps lock should be used."""
|
|
57
|
-
return self.mode == "system"
|
|
23
|
+
elif sys.platform == "win32":
|
|
24
|
+
from . import _windows
|
|
58
25
|
|
|
59
26
|
|
|
60
27
|
class PyautoguiConfig(BaseModel):
|
|
@@ -111,6 +78,14 @@ class PyautoguiActionHandler:
|
|
|
111
78
|
# Initialize caps lock manager
|
|
112
79
|
self.caps_manager = CapsLockManager(mode=self.config.capslock_mode)
|
|
113
80
|
|
|
81
|
+
def reset(self):
|
|
82
|
+
"""Reset handler state.
|
|
83
|
+
|
|
84
|
+
Called at automation start/end and when FINISH action is received.
|
|
85
|
+
Resets the internal capslock state.
|
|
86
|
+
"""
|
|
87
|
+
self.caps_manager.reset()
|
|
88
|
+
|
|
114
89
|
def _denormalize_coords(self, x: float, y: float) -> tuple[int, int]:
|
|
115
90
|
"""Convert coordinates from 0-1000 range to actual screen coordinates.
|
|
116
91
|
|
|
@@ -238,7 +213,14 @@ class PyautoguiActionHandler:
|
|
|
238
213
|
text = arg.strip("\"'")
|
|
239
214
|
# Apply caps lock transformation if needed
|
|
240
215
|
text = self.caps_manager.transform_text(text)
|
|
241
|
-
|
|
216
|
+
# Use platform-specific typing that ignores system capslock
|
|
217
|
+
if sys.platform == "darwin":
|
|
218
|
+
_macos.typewrite_exact(text)
|
|
219
|
+
elif sys.platform == "win32":
|
|
220
|
+
_windows.typewrite_exact(text)
|
|
221
|
+
else:
|
|
222
|
+
# Fallback for other platforms
|
|
223
|
+
pyautogui.typewrite(text)
|
|
242
224
|
|
|
243
225
|
case ActionType.SCROLL:
|
|
244
226
|
x, y, direction = self._parse_scroll(arg)
|
|
@@ -251,8 +233,8 @@ class PyautoguiActionHandler:
|
|
|
251
233
|
pyautogui.scroll(scroll_amount)
|
|
252
234
|
|
|
253
235
|
case ActionType.FINISH:
|
|
254
|
-
# Task completion -
|
|
255
|
-
|
|
236
|
+
# Task completion - reset handler state
|
|
237
|
+
self.reset()
|
|
256
238
|
|
|
257
239
|
case ActionType.WAIT:
|
|
258
240
|
# Wait for a short period
|
oagi/server/session_store.py
CHANGED
|
@@ -11,7 +11,7 @@ from datetime import datetime
|
|
|
11
11
|
from typing import Any
|
|
12
12
|
from uuid import uuid4
|
|
13
13
|
|
|
14
|
-
from ..constants import MODE_ACTOR, MODEL_ACTOR
|
|
14
|
+
from ..constants import DEFAULT_TEMPERATURE_LOW, MODE_ACTOR, MODEL_ACTOR
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class Session:
|
|
@@ -21,7 +21,7 @@ class Session:
|
|
|
21
21
|
instruction: str,
|
|
22
22
|
mode: str = MODE_ACTOR,
|
|
23
23
|
model: str = MODEL_ACTOR,
|
|
24
|
-
temperature: float =
|
|
24
|
+
temperature: float = DEFAULT_TEMPERATURE_LOW,
|
|
25
25
|
):
|
|
26
26
|
self.session_id: str = session_id
|
|
27
27
|
self.instruction: str = instruction
|
|
@@ -57,7 +57,7 @@ class SessionStore:
|
|
|
57
57
|
instruction: str,
|
|
58
58
|
mode: str = MODE_ACTOR,
|
|
59
59
|
model: str = MODEL_ACTOR,
|
|
60
|
-
temperature: float =
|
|
60
|
+
temperature: float = DEFAULT_TEMPERATURE_LOW,
|
|
61
61
|
session_id: str | None = None,
|
|
62
62
|
) -> str:
|
|
63
63
|
if session_id is None:
|
oagi/server/socketio_server.py
CHANGED
|
@@ -158,8 +158,8 @@ class SessionNamespace(socketio.AsyncNamespace):
|
|
|
158
158
|
session_store.update_activity(session_id)
|
|
159
159
|
|
|
160
160
|
logger.info(
|
|
161
|
-
f"Session {session_id} initialized with: {
|
|
162
|
-
f"(mode={
|
|
161
|
+
f"Session {session_id} initialized with: {session.instruction} "
|
|
162
|
+
f"(mode={session.mode}, model={session.model})"
|
|
163
163
|
)
|
|
164
164
|
|
|
165
165
|
# Create agent and wrappers
|
|
@@ -168,8 +168,8 @@ class SessionNamespace(socketio.AsyncNamespace):
|
|
|
168
168
|
api_key=self.config.oagi_api_key,
|
|
169
169
|
base_url=self.config.oagi_base_url,
|
|
170
170
|
max_steps=self.config.max_steps,
|
|
171
|
-
model=
|
|
172
|
-
temperature=
|
|
171
|
+
model=session.model,
|
|
172
|
+
temperature=session.temperature,
|
|
173
173
|
)
|
|
174
174
|
|
|
175
175
|
action_handler = SocketIOActionHandler(self, session)
|
oagi/task/__init__.py
CHANGED
|
@@ -6,16 +6,30 @@
|
|
|
6
6
|
# Licensed under the MIT License.
|
|
7
7
|
# -----------------------------------------------------------------------------
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
"""Deprecated: Use oagi.actor instead. This module will be removed in a future version."""
|
|
10
|
+
|
|
11
|
+
import warnings
|
|
12
|
+
|
|
13
|
+
from oagi.actor import (
|
|
14
|
+
Actor,
|
|
15
|
+
AsyncActor,
|
|
16
|
+
AsyncShortTask,
|
|
17
|
+
AsyncTask,
|
|
18
|
+
ShortTask,
|
|
19
|
+
Task,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
warnings.warn(
|
|
23
|
+
"oagi.task is deprecated, use oagi.actor instead",
|
|
24
|
+
DeprecationWarning,
|
|
25
|
+
stacklevel=2,
|
|
26
|
+
)
|
|
13
27
|
|
|
14
28
|
__all__ = [
|
|
15
29
|
"Actor",
|
|
16
30
|
"AsyncActor",
|
|
17
|
-
"Task",
|
|
18
|
-
"AsyncTask",
|
|
19
|
-
"ShortTask",
|
|
20
|
-
"AsyncShortTask",
|
|
31
|
+
"Task",
|
|
32
|
+
"AsyncTask",
|
|
33
|
+
"ShortTask",
|
|
34
|
+
"AsyncShortTask",
|
|
21
35
|
]
|
oagi/types/__init__.py
CHANGED
|
@@ -32,7 +32,7 @@ from .step_observer import (
|
|
|
32
32
|
SplitEvent,
|
|
33
33
|
StepEvent,
|
|
34
34
|
)
|
|
35
|
-
from .url import URL
|
|
35
|
+
from .url import URL, extract_uuid_from_url
|
|
36
36
|
|
|
37
37
|
__all__ = [
|
|
38
38
|
"Action",
|
|
@@ -55,6 +55,7 @@ __all__ = [
|
|
|
55
55
|
"ImageProvider",
|
|
56
56
|
"AsyncImageProvider",
|
|
57
57
|
"URL",
|
|
58
|
+
"extract_uuid_from_url",
|
|
58
59
|
"parse_coords",
|
|
59
60
|
"parse_drag_coords",
|
|
60
61
|
"parse_scroll",
|
oagi/types/models/__init__.py
CHANGED
|
@@ -17,7 +17,6 @@ from .client import (
|
|
|
17
17
|
ErrorDetail,
|
|
18
18
|
ErrorResponse,
|
|
19
19
|
GenerateResponse,
|
|
20
|
-
LLMResponse,
|
|
21
20
|
UploadFileResponse,
|
|
22
21
|
Usage,
|
|
23
22
|
)
|
|
@@ -31,7 +30,6 @@ __all__ = [
|
|
|
31
30
|
"ErrorResponse",
|
|
32
31
|
"GenerateResponse",
|
|
33
32
|
"ImageConfig",
|
|
34
|
-
"LLMResponse",
|
|
35
33
|
"Step",
|
|
36
34
|
"UploadFileResponse",
|
|
37
35
|
"Usage",
|
oagi/types/models/action.py
CHANGED
|
@@ -81,4 +81,7 @@ def parse_scroll(args_str: str) -> tuple[int, int, str] | None:
|
|
|
81
81
|
match = re.match(r"(\d+),\s*(\d+),\s*(\w+)", args_str)
|
|
82
82
|
if not match:
|
|
83
83
|
return None
|
|
84
|
-
|
|
84
|
+
direction = match.group(3).lower()
|
|
85
|
+
if direction not in ("up", "down"):
|
|
86
|
+
return None
|
|
87
|
+
return int(match.group(1)), int(match.group(2)), direction
|
oagi/types/models/client.py
CHANGED
|
@@ -8,8 +8,6 @@
|
|
|
8
8
|
|
|
9
9
|
from pydantic import BaseModel, Field
|
|
10
10
|
|
|
11
|
-
from .action import Action
|
|
12
|
-
|
|
13
11
|
|
|
14
12
|
class Usage(BaseModel):
|
|
15
13
|
prompt_tokens: int
|
|
@@ -30,21 +28,6 @@ class ErrorResponse(BaseModel):
|
|
|
30
28
|
error: ErrorDetail | None
|
|
31
29
|
|
|
32
30
|
|
|
33
|
-
class LLMResponse(BaseModel):
|
|
34
|
-
id: str
|
|
35
|
-
task_id: str
|
|
36
|
-
object: str = "task.completion"
|
|
37
|
-
created: int
|
|
38
|
-
model: str
|
|
39
|
-
task_description: str
|
|
40
|
-
is_complete: bool
|
|
41
|
-
actions: list[Action]
|
|
42
|
-
reason: str | None = None
|
|
43
|
-
usage: Usage
|
|
44
|
-
error: ErrorDetail | None = None
|
|
45
|
-
raw_output: str | None = None
|
|
46
|
-
|
|
47
|
-
|
|
48
31
|
class UploadFileResponse(BaseModel):
|
|
49
32
|
"""Response from S3 presigned URL upload."""
|
|
50
33
|
|
|
@@ -66,3 +49,4 @@ class GenerateResponse(BaseModel):
|
|
|
66
49
|
deprecated=True,
|
|
67
50
|
description="This field is deprecated",
|
|
68
51
|
)
|
|
52
|
+
request_id: str | None = None
|
oagi/types/step_observer.py
CHANGED
|
@@ -35,6 +35,7 @@ class StepEvent(BaseEvent):
|
|
|
35
35
|
step_num: int
|
|
36
36
|
image: bytes | str
|
|
37
37
|
step: Step
|
|
38
|
+
task_id: str | None = None
|
|
38
39
|
|
|
39
40
|
|
|
40
41
|
class ActionEvent(BaseEvent):
|
|
@@ -68,6 +69,7 @@ class PlanEvent(BaseEvent):
|
|
|
68
69
|
image: bytes | str | None = None
|
|
69
70
|
reasoning: str
|
|
70
71
|
result: str | None = None
|
|
72
|
+
request_id: str | None = None
|
|
71
73
|
|
|
72
74
|
|
|
73
75
|
ObserverEvent = ImageEvent | StepEvent | ActionEvent | LogEvent | SplitEvent | PlanEvent
|
oagi/types/url.py
CHANGED
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
import re
|
|
1
2
|
from typing import NewType
|
|
2
3
|
|
|
3
4
|
URL = NewType("URL", str)
|
|
5
|
+
|
|
6
|
+
# Pattern to extract UUID from S3 URL
|
|
7
|
+
# Formats:
|
|
8
|
+
# - https://bucket.s3.amazonaws.com/{user_id}/{uuid}.{ext} (download URL)
|
|
9
|
+
# - https://bucket.s3.amazonaws.com/{user_id}/{uuid}?... (presigned URL)
|
|
10
|
+
_UUID_PATTERN = re.compile(
|
|
11
|
+
r"/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:\.[a-z]+)?(?:\?|$)",
|
|
12
|
+
re.IGNORECASE,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def extract_uuid_from_url(url: str) -> str | None:
|
|
17
|
+
"""Extract UUID from S3 URL.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
url: S3 URL in one of these formats:
|
|
21
|
+
- https://bucket.s3.amazonaws.com/{user_id}/{uuid}.jpg (download URL)
|
|
22
|
+
- https://bucket.s3.amazonaws.com/{user_id}/{uuid}?... (presigned URL)
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
UUID string if found, None otherwise
|
|
26
|
+
"""
|
|
27
|
+
match = _UUID_PATTERN.search(url)
|
|
28
|
+
return match.group(1) if match else None
|
oagi/utils/__init__.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# -----------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) OpenAGI Foundation
|
|
3
|
+
# All rights reserved.
|
|
4
|
+
#
|
|
5
|
+
# This file is part of the official API project.
|
|
6
|
+
# Licensed under the MIT License.
|
|
7
|
+
# -----------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
from .output_parser import parse_raw_output
|
|
10
|
+
from .prompt_builder import build_prompt
|
|
11
|
+
|
|
12
|
+
__all__ = ["build_prompt", "parse_raw_output"]
|