oagi-core 0.9.0__py3-none-any.whl → 0.9.1__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 +13 -16
- oagi/agent/default.py +23 -7
- oagi/agent/factories.py +5 -0
- oagi/agent/tasker/taskee_agent.py +40 -15
- oagi/agent/tasker/tasker_agent.py +5 -1
- oagi/cli/agent.py +31 -15
- oagi/cli/display.py +56 -0
- oagi/cli/tracking.py +45 -0
- oagi/handler/__init__.py +24 -0
- oagi/{async_pyautogui_action_handler.py → handler/async_pyautogui_action_handler.py} +1 -1
- oagi/{async_screenshot_maker.py → handler/async_screenshot_maker.py} +1 -1
- oagi/{pil_image.py → handler/pil_image.py} +2 -2
- oagi/{pyautogui_action_handler.py → handler/pyautogui_action_handler.py} +2 -2
- oagi/{screenshot_maker.py → handler/screenshot_maker.py} +2 -2
- oagi/logging.py +8 -0
- oagi/task/__init__.py +10 -3
- oagi/task/async_.py +26 -1
- oagi/task/async_short.py +15 -3
- oagi/task/base.py +2 -0
- oagi/task/short.py +15 -3
- oagi/task/sync.py +26 -1
- oagi/types/__init__.py +2 -0
- oagi/types/step_observer.py +34 -0
- {oagi_core-0.9.0.dist-info → oagi_core-0.9.1.dist-info}/METADATA +3 -29
- {oagi_core-0.9.0.dist-info → oagi_core-0.9.1.dist-info}/RECORD +28 -26
- oagi/async_single_step.py +0 -85
- oagi/single_step.py +0 -87
- {oagi_core-0.9.0.dist-info → oagi_core-0.9.1.dist-info}/WHEEL +0 -0
- {oagi_core-0.9.0.dist-info → oagi_core-0.9.1.dist-info}/entry_points.txt +0 -0
- {oagi_core-0.9.0.dist-info → oagi_core-0.9.1.dist-info}/licenses/LICENSE +0 -0
oagi/__init__.py
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
# -----------------------------------------------------------------------------
|
|
8
8
|
import importlib
|
|
9
9
|
|
|
10
|
-
from oagi.async_single_step import async_single_step
|
|
11
10
|
from oagi.client import AsyncClient, SyncClient
|
|
12
11
|
from oagi.exceptions import (
|
|
13
12
|
APIError,
|
|
@@ -21,8 +20,7 @@ from oagi.exceptions import (
|
|
|
21
20
|
ServerError,
|
|
22
21
|
ValidationError,
|
|
23
22
|
)
|
|
24
|
-
from oagi.
|
|
25
|
-
from oagi.task import AsyncShortTask, AsyncTask, ShortTask, Task
|
|
23
|
+
from oagi.task import Actor, AsyncActor, AsyncShortTask, AsyncTask, ShortTask, Task
|
|
26
24
|
from oagi.types import (
|
|
27
25
|
AsyncActionHandler,
|
|
28
26
|
AsyncImageProvider,
|
|
@@ -33,12 +31,12 @@ from oagi.types.models import ErrorDetail, ErrorResponse, LLMResponse
|
|
|
33
31
|
# Lazy imports for pyautogui-dependent modules
|
|
34
32
|
# These will only be imported when actually accessed
|
|
35
33
|
_LAZY_IMPORTS = {
|
|
36
|
-
"AsyncPyautoguiActionHandler": "oagi.async_pyautogui_action_handler",
|
|
37
|
-
"AsyncScreenshotMaker": "oagi.async_screenshot_maker",
|
|
38
|
-
"PILImage": "oagi.pil_image",
|
|
39
|
-
"PyautoguiActionHandler": "oagi.pyautogui_action_handler",
|
|
40
|
-
"PyautoguiConfig": "oagi.pyautogui_action_handler",
|
|
41
|
-
"ScreenshotMaker": "oagi.screenshot_maker",
|
|
34
|
+
"AsyncPyautoguiActionHandler": "oagi.handler.async_pyautogui_action_handler",
|
|
35
|
+
"AsyncScreenshotMaker": "oagi.handler.async_screenshot_maker",
|
|
36
|
+
"PILImage": "oagi.handler.pil_image",
|
|
37
|
+
"PyautoguiActionHandler": "oagi.handler.pyautogui_action_handler",
|
|
38
|
+
"PyautoguiConfig": "oagi.handler.pyautogui_action_handler",
|
|
39
|
+
"ScreenshotMaker": "oagi.handler.screenshot_maker",
|
|
42
40
|
# Agent modules (to avoid circular imports)
|
|
43
41
|
"TaskerAgent": "oagi.agent.tasker",
|
|
44
42
|
# Server modules (optional - requires server dependencies)
|
|
@@ -59,18 +57,17 @@ def __getattr__(name: str):
|
|
|
59
57
|
|
|
60
58
|
__all__ = [
|
|
61
59
|
# Core sync classes
|
|
62
|
-
"
|
|
63
|
-
"
|
|
60
|
+
"Actor",
|
|
61
|
+
"AsyncActor",
|
|
62
|
+
"Task", # Deprecated: Use Actor instead
|
|
63
|
+
"ShortTask", # Deprecated
|
|
64
64
|
"SyncClient",
|
|
65
65
|
# Core async classes
|
|
66
|
-
"AsyncTask",
|
|
67
|
-
"AsyncShortTask",
|
|
66
|
+
"AsyncTask", # Deprecated: Use AsyncActor instead
|
|
67
|
+
"AsyncShortTask", # Deprecated
|
|
68
68
|
"AsyncClient",
|
|
69
69
|
# Agent classes
|
|
70
70
|
"TaskerAgent",
|
|
71
|
-
# Functions
|
|
72
|
-
"single_step",
|
|
73
|
-
"async_single_step",
|
|
74
71
|
# Async protocols
|
|
75
72
|
"AsyncActionHandler",
|
|
76
73
|
"AsyncImageProvider",
|
oagi/agent/default.py
CHANGED
|
@@ -8,10 +8,11 @@
|
|
|
8
8
|
|
|
9
9
|
import logging
|
|
10
10
|
|
|
11
|
-
from .. import
|
|
11
|
+
from .. import AsyncActor
|
|
12
12
|
from ..types import (
|
|
13
13
|
AsyncActionHandler,
|
|
14
14
|
AsyncImageProvider,
|
|
15
|
+
AsyncStepObserver,
|
|
15
16
|
)
|
|
16
17
|
|
|
17
18
|
logger = logging.getLogger(__name__)
|
|
@@ -27,12 +28,14 @@ class AsyncDefaultAgent:
|
|
|
27
28
|
model: str = "lux-v1",
|
|
28
29
|
max_steps: int = 30,
|
|
29
30
|
temperature: float | None = None,
|
|
31
|
+
step_observer: AsyncStepObserver | None = None,
|
|
30
32
|
):
|
|
31
33
|
self.api_key = api_key
|
|
32
34
|
self.base_url = base_url
|
|
33
35
|
self.model = model
|
|
34
36
|
self.max_steps = max_steps
|
|
35
37
|
self.temperature = temperature
|
|
38
|
+
self.step_observer = step_observer
|
|
36
39
|
|
|
37
40
|
async def execute(
|
|
38
41
|
self,
|
|
@@ -40,11 +43,11 @@ class AsyncDefaultAgent:
|
|
|
40
43
|
action_handler: AsyncActionHandler,
|
|
41
44
|
image_provider: AsyncImageProvider,
|
|
42
45
|
) -> bool:
|
|
43
|
-
async with
|
|
46
|
+
async with AsyncActor(
|
|
44
47
|
api_key=self.api_key, base_url=self.base_url, model=self.model
|
|
45
|
-
) as self.
|
|
48
|
+
) as self.actor:
|
|
46
49
|
logger.info(f"Starting async task execution: {instruction}")
|
|
47
|
-
await self.
|
|
50
|
+
await self.actor.init_task(instruction, max_steps=self.max_steps)
|
|
48
51
|
|
|
49
52
|
for i in range(self.max_steps):
|
|
50
53
|
logger.debug(f"Executing step {i + 1}/{self.max_steps}")
|
|
@@ -53,15 +56,28 @@ class AsyncDefaultAgent:
|
|
|
53
56
|
image = await image_provider()
|
|
54
57
|
|
|
55
58
|
# Get next step from OAGI
|
|
56
|
-
step = await self.
|
|
59
|
+
step = await self.actor.step(image, temperature=self.temperature)
|
|
57
60
|
|
|
58
61
|
# Log reasoning
|
|
59
62
|
if step.reason:
|
|
60
|
-
logger.
|
|
63
|
+
logger.info(f"Step {i + 1}: {step.reason}")
|
|
64
|
+
|
|
65
|
+
# Notify observer if present
|
|
66
|
+
if self.step_observer and step.actions:
|
|
67
|
+
await self.step_observer.on_step(i + 1, step.reason, step.actions)
|
|
61
68
|
|
|
62
69
|
# Execute actions if any
|
|
63
70
|
if step.actions:
|
|
64
|
-
logger.
|
|
71
|
+
logger.info(f"Actions ({len(step.actions)}):")
|
|
72
|
+
for action in step.actions:
|
|
73
|
+
count_suffix = (
|
|
74
|
+
f" x{action.count}"
|
|
75
|
+
if action.count and action.count > 1
|
|
76
|
+
else ""
|
|
77
|
+
)
|
|
78
|
+
logger.info(
|
|
79
|
+
f" [{action.type.value}] {action.argument}{count_suffix}"
|
|
80
|
+
)
|
|
65
81
|
await action_handler(step.actions)
|
|
66
82
|
|
|
67
83
|
# Check if task is complete
|
oagi/agent/factories.py
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
# Licensed under the MIT License.
|
|
7
7
|
# -----------------------------------------------------------------------------
|
|
8
8
|
from oagi.agent.tasker import TaskerAgent
|
|
9
|
+
from oagi.types import AsyncStepObserver
|
|
9
10
|
|
|
10
11
|
from .default import AsyncDefaultAgent
|
|
11
12
|
from .protocol import AsyncAgent
|
|
@@ -19,6 +20,7 @@ def create_default_agent(
|
|
|
19
20
|
model: str = "lux-v1",
|
|
20
21
|
max_steps: int = 20,
|
|
21
22
|
temperature: float = 0.1,
|
|
23
|
+
step_observer: AsyncStepObserver | None = None,
|
|
22
24
|
) -> AsyncAgent:
|
|
23
25
|
return AsyncDefaultAgent(
|
|
24
26
|
api_key=api_key,
|
|
@@ -26,6 +28,7 @@ def create_default_agent(
|
|
|
26
28
|
model=model,
|
|
27
29
|
max_steps=max_steps,
|
|
28
30
|
temperature=temperature,
|
|
31
|
+
step_observer=step_observer,
|
|
29
32
|
)
|
|
30
33
|
|
|
31
34
|
|
|
@@ -37,6 +40,7 @@ def create_planner_agent(
|
|
|
37
40
|
max_steps: int = 30,
|
|
38
41
|
temperature: float = 0.0,
|
|
39
42
|
reflection_interval: int = 20,
|
|
43
|
+
step_observer: AsyncStepObserver | None = None,
|
|
40
44
|
) -> AsyncAgent:
|
|
41
45
|
tasker = TaskerAgent(
|
|
42
46
|
api_key=api_key,
|
|
@@ -45,6 +49,7 @@ def create_planner_agent(
|
|
|
45
49
|
max_steps=max_steps,
|
|
46
50
|
temperature=temperature,
|
|
47
51
|
reflection_interval=reflection_interval,
|
|
52
|
+
step_observer=step_observer,
|
|
48
53
|
)
|
|
49
54
|
# tasker.set_task()
|
|
50
55
|
return tasker
|
|
@@ -10,8 +10,8 @@ import logging
|
|
|
10
10
|
from datetime import datetime
|
|
11
11
|
from typing import Any
|
|
12
12
|
|
|
13
|
-
from oagi import
|
|
14
|
-
from oagi.types import AsyncActionHandler, AsyncImageProvider
|
|
13
|
+
from oagi import AsyncActor
|
|
14
|
+
from oagi.types import AsyncActionHandler, AsyncImageProvider, AsyncStepObserver
|
|
15
15
|
|
|
16
16
|
from ..protocol import AsyncAgent
|
|
17
17
|
from .memory import PlannerMemory
|
|
@@ -42,6 +42,7 @@ class TaskeeAgent(AsyncAgent):
|
|
|
42
42
|
planner: Planner | None = None,
|
|
43
43
|
external_memory: PlannerMemory | None = None,
|
|
44
44
|
todo_index: int | None = None,
|
|
45
|
+
step_observer: AsyncStepObserver | None = None,
|
|
45
46
|
):
|
|
46
47
|
"""Initialize the taskee agent.
|
|
47
48
|
|
|
@@ -55,6 +56,7 @@ class TaskeeAgent(AsyncAgent):
|
|
|
55
56
|
planner: Planner for planning and reflection
|
|
56
57
|
external_memory: External memory from parent agent
|
|
57
58
|
todo_index: Index of the todo being executed
|
|
59
|
+
step_observer: Optional observer for step tracking
|
|
58
60
|
"""
|
|
59
61
|
self.api_key = api_key
|
|
60
62
|
self.base_url = base_url
|
|
@@ -65,9 +67,10 @@ class TaskeeAgent(AsyncAgent):
|
|
|
65
67
|
self.planner = planner or Planner()
|
|
66
68
|
self.external_memory = external_memory
|
|
67
69
|
self.todo_index = todo_index
|
|
70
|
+
self.step_observer = step_observer
|
|
68
71
|
|
|
69
72
|
# Internal state
|
|
70
|
-
self.
|
|
73
|
+
self.actor: AsyncActor | None = None
|
|
71
74
|
self.current_todo: str = ""
|
|
72
75
|
self.current_instruction: str = ""
|
|
73
76
|
self.actions: list[Action] = []
|
|
@@ -135,10 +138,10 @@ class TaskeeAgent(AsyncAgent):
|
|
|
135
138
|
)
|
|
136
139
|
return False
|
|
137
140
|
finally:
|
|
138
|
-
# Clean up
|
|
139
|
-
if self.
|
|
140
|
-
await self.
|
|
141
|
-
self.
|
|
141
|
+
# Clean up actor
|
|
142
|
+
if self.actor:
|
|
143
|
+
await self.actor.close()
|
|
144
|
+
self.actor = None
|
|
142
145
|
|
|
143
146
|
async def _initial_plan(self, image_provider: AsyncImageProvider) -> None:
|
|
144
147
|
"""Generate initial plan for the todo.
|
|
@@ -199,17 +202,17 @@ class TaskeeAgent(AsyncAgent):
|
|
|
199
202
|
logger.info(f"Executing subtask with max {max_steps} steps")
|
|
200
203
|
|
|
201
204
|
# Use async with for automatic resource management
|
|
202
|
-
async with
|
|
205
|
+
async with AsyncActor(
|
|
203
206
|
api_key=self.api_key,
|
|
204
207
|
base_url=self.base_url,
|
|
205
208
|
model=self.model,
|
|
206
209
|
temperature=self.temperature,
|
|
207
|
-
) as
|
|
210
|
+
) as actor:
|
|
208
211
|
# Store reference for potential cleanup in execute's finally block
|
|
209
|
-
self.
|
|
212
|
+
self.actor = actor
|
|
210
213
|
|
|
211
|
-
# Initialize
|
|
212
|
-
await
|
|
214
|
+
# Initialize actor with current instruction
|
|
215
|
+
await actor.init_task(self.current_instruction)
|
|
213
216
|
|
|
214
217
|
steps_taken = 0
|
|
215
218
|
for step_num in range(max_steps):
|
|
@@ -218,7 +221,7 @@ class TaskeeAgent(AsyncAgent):
|
|
|
218
221
|
|
|
219
222
|
# Get next step from OAGI
|
|
220
223
|
try:
|
|
221
|
-
step = await
|
|
224
|
+
step = await actor.step(screenshot, instruction=None)
|
|
222
225
|
except Exception as e:
|
|
223
226
|
logger.error(f"Error getting step from OAGI: {e}")
|
|
224
227
|
self._record_action(
|
|
@@ -228,8 +231,24 @@ class TaskeeAgent(AsyncAgent):
|
|
|
228
231
|
)
|
|
229
232
|
break
|
|
230
233
|
|
|
234
|
+
# Log reasoning
|
|
235
|
+
if step.reason:
|
|
236
|
+
logger.info(f"Step {self.total_actions + 1}: {step.reason}")
|
|
237
|
+
|
|
231
238
|
# Record OAGI actions
|
|
232
239
|
if step.actions:
|
|
240
|
+
# Log actions with details
|
|
241
|
+
logger.info(f"Actions ({len(step.actions)}):")
|
|
242
|
+
for action in step.actions:
|
|
243
|
+
count_suffix = (
|
|
244
|
+
f" x{action.count}"
|
|
245
|
+
if action.count and action.count > 1
|
|
246
|
+
else ""
|
|
247
|
+
)
|
|
248
|
+
logger.info(
|
|
249
|
+
f" [{action.type.value}] {action.argument}{count_suffix}"
|
|
250
|
+
)
|
|
251
|
+
|
|
233
252
|
for action in step.actions:
|
|
234
253
|
self._record_action(
|
|
235
254
|
action_type=action.type.lower(),
|
|
@@ -237,6 +256,12 @@ class TaskeeAgent(AsyncAgent):
|
|
|
237
256
|
reasoning=step.reason,
|
|
238
257
|
)
|
|
239
258
|
|
|
259
|
+
# Notify observer if present
|
|
260
|
+
if self.step_observer:
|
|
261
|
+
await self.step_observer.on_step(
|
|
262
|
+
self.total_actions + 1, step.reason, step.actions
|
|
263
|
+
)
|
|
264
|
+
|
|
240
265
|
# Execute actions
|
|
241
266
|
await action_handler(step.actions)
|
|
242
267
|
self.total_actions += len(step.actions)
|
|
@@ -255,9 +280,9 @@ class TaskeeAgent(AsyncAgent):
|
|
|
255
280
|
logger.info("Reflection interval reached")
|
|
256
281
|
break
|
|
257
282
|
|
|
258
|
-
#
|
|
283
|
+
# Actor will be automatically closed by async with context manager
|
|
259
284
|
# Clear reference after context manager closes it
|
|
260
|
-
self.
|
|
285
|
+
self.actor = None
|
|
261
286
|
return steps_taken
|
|
262
287
|
|
|
263
288
|
async def _reflect_and_decide(self, image_provider: AsyncImageProvider) -> bool:
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import logging
|
|
10
10
|
from typing import Any
|
|
11
11
|
|
|
12
|
-
from oagi.types import AsyncActionHandler, AsyncImageProvider
|
|
12
|
+
from oagi.types import AsyncActionHandler, AsyncImageProvider, AsyncStepObserver
|
|
13
13
|
|
|
14
14
|
from ..protocol import AsyncAgent
|
|
15
15
|
from .memory import PlannerMemory
|
|
@@ -39,6 +39,7 @@ class TaskerAgent(AsyncAgent):
|
|
|
39
39
|
temperature: float = 0.0,
|
|
40
40
|
reflection_interval: int = 20,
|
|
41
41
|
planner: Planner | None = None,
|
|
42
|
+
step_observer: AsyncStepObserver | None = None,
|
|
42
43
|
):
|
|
43
44
|
"""Initialize the tasker agent.
|
|
44
45
|
|
|
@@ -50,6 +51,7 @@ class TaskerAgent(AsyncAgent):
|
|
|
50
51
|
temperature: Sampling temperature
|
|
51
52
|
reflection_interval: Actions before reflection
|
|
52
53
|
planner: Planner for planning and reflection
|
|
54
|
+
step_observer: Optional observer for step tracking
|
|
53
55
|
"""
|
|
54
56
|
self.api_key = api_key
|
|
55
57
|
self.base_url = base_url
|
|
@@ -58,6 +60,7 @@ class TaskerAgent(AsyncAgent):
|
|
|
58
60
|
self.temperature = temperature
|
|
59
61
|
self.reflection_interval = reflection_interval
|
|
60
62
|
self.planner = planner or Planner()
|
|
63
|
+
self.step_observer = step_observer
|
|
61
64
|
|
|
62
65
|
# Memory for tracking workflow
|
|
63
66
|
self.memory = PlannerMemory()
|
|
@@ -174,6 +177,7 @@ class TaskerAgent(AsyncAgent):
|
|
|
174
177
|
planner=self.planner,
|
|
175
178
|
external_memory=self.memory, # Share memory with child
|
|
176
179
|
todo_index=todo_index, # Pass the todo index
|
|
180
|
+
step_observer=self.step_observer, # Pass step observer
|
|
177
181
|
)
|
|
178
182
|
|
|
179
183
|
self.current_todo_index = todo_index
|
oagi/cli/agent.py
CHANGED
|
@@ -10,9 +10,14 @@ import argparse
|
|
|
10
10
|
import asyncio
|
|
11
11
|
import os
|
|
12
12
|
import sys
|
|
13
|
+
import time
|
|
14
|
+
import traceback
|
|
13
15
|
|
|
14
16
|
from oagi.exceptions import check_optional_dependency
|
|
15
17
|
|
|
18
|
+
from .display import display_step_table
|
|
19
|
+
from .tracking import StepTracker
|
|
20
|
+
|
|
16
21
|
|
|
17
22
|
def add_agent_parser(subparsers: argparse._SubParsersAction) -> None:
|
|
18
23
|
agent_parser = subparsers.add_parser("agent", help="Agent execution commands")
|
|
@@ -79,7 +84,10 @@ def run_agent(args: argparse.Namespace) -> None:
|
|
|
79
84
|
temperature = args.temperature if args.temperature is not None else 0.0
|
|
80
85
|
mode = args.mode or "actor"
|
|
81
86
|
|
|
82
|
-
# Create
|
|
87
|
+
# Create step tracker
|
|
88
|
+
step_tracker = StepTracker()
|
|
89
|
+
|
|
90
|
+
# Create agent with step tracker
|
|
83
91
|
agent = create_agent(
|
|
84
92
|
mode=mode,
|
|
85
93
|
api_key=api_key,
|
|
@@ -87,6 +95,7 @@ def run_agent(args: argparse.Namespace) -> None:
|
|
|
87
95
|
model=model,
|
|
88
96
|
max_steps=max_steps,
|
|
89
97
|
temperature=temperature,
|
|
98
|
+
step_observer=step_tracker,
|
|
90
99
|
)
|
|
91
100
|
|
|
92
101
|
# Create handlers
|
|
@@ -99,7 +108,10 @@ def run_agent(args: argparse.Namespace) -> None:
|
|
|
99
108
|
)
|
|
100
109
|
print("-" * 60)
|
|
101
110
|
|
|
102
|
-
|
|
111
|
+
start_time = time.time()
|
|
112
|
+
success = False
|
|
113
|
+
interrupted = False
|
|
114
|
+
|
|
103
115
|
try:
|
|
104
116
|
success = asyncio.run(
|
|
105
117
|
agent.execute(
|
|
@@ -108,18 +120,22 @@ def run_agent(args: argparse.Namespace) -> None:
|
|
|
108
120
|
image_provider=image_provider,
|
|
109
121
|
)
|
|
110
122
|
)
|
|
111
|
-
|
|
112
|
-
print("-" * 60)
|
|
113
|
-
if success:
|
|
114
|
-
print("Task completed successfully!")
|
|
115
|
-
sys.exit(0)
|
|
116
|
-
else:
|
|
117
|
-
print("Task failed or reached max steps without completion.")
|
|
118
|
-
sys.exit(1)
|
|
119
|
-
|
|
120
123
|
except KeyboardInterrupt:
|
|
121
|
-
print("\nAgent execution interrupted
|
|
122
|
-
|
|
124
|
+
print("\nAgent execution interrupted by user (Ctrl+C)")
|
|
125
|
+
interrupted = True
|
|
123
126
|
except Exception as e:
|
|
124
|
-
print(f"
|
|
125
|
-
|
|
127
|
+
print(f"\nError during agent execution: {e}", file=sys.stderr)
|
|
128
|
+
traceback.print_exc()
|
|
129
|
+
finally:
|
|
130
|
+
duration = time.time() - start_time
|
|
131
|
+
|
|
132
|
+
if step_tracker.steps:
|
|
133
|
+
print("\n" + "=" * 60)
|
|
134
|
+
display_step_table(step_tracker.steps, success, duration)
|
|
135
|
+
else:
|
|
136
|
+
print("\nNo steps were executed.")
|
|
137
|
+
|
|
138
|
+
if interrupted:
|
|
139
|
+
sys.exit(130)
|
|
140
|
+
elif not success:
|
|
141
|
+
sys.exit(1)
|
oagi/cli/display.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
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 rich.console import Console
|
|
10
|
+
from rich.table import Table
|
|
11
|
+
|
|
12
|
+
from .tracking import StepData
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def display_step_table(
|
|
16
|
+
steps: list[StepData], success: bool, duration: float | None = None
|
|
17
|
+
):
|
|
18
|
+
console = Console()
|
|
19
|
+
|
|
20
|
+
table = Table(title="Agent Execution Summary", show_lines=True)
|
|
21
|
+
table.add_column("Step", justify="center", style="cyan", width=6)
|
|
22
|
+
table.add_column("Reasoning", style="white")
|
|
23
|
+
table.add_column("Actions", style="yellow", width=35)
|
|
24
|
+
table.add_column("Status", justify="center", width=8)
|
|
25
|
+
|
|
26
|
+
for step in steps:
|
|
27
|
+
reason = step.reasoning or "N/A"
|
|
28
|
+
|
|
29
|
+
actions_display = []
|
|
30
|
+
for action in step.actions[:3]:
|
|
31
|
+
arg = action.argument[:20] if action.argument else ""
|
|
32
|
+
actions_display.append(f"{action.type.value}({arg})")
|
|
33
|
+
|
|
34
|
+
actions_str = ", ".join(actions_display)
|
|
35
|
+
if len(step.actions) > 3:
|
|
36
|
+
actions_str += f" (+{len(step.actions) - 3} more)"
|
|
37
|
+
|
|
38
|
+
status_display = "✓" if step.status == "complete" else "→"
|
|
39
|
+
|
|
40
|
+
table.add_row(
|
|
41
|
+
str(step.step_num),
|
|
42
|
+
reason,
|
|
43
|
+
actions_str,
|
|
44
|
+
status_display,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
console.print(table)
|
|
48
|
+
|
|
49
|
+
status_text = "Success" if success else "Failed/Interrupted"
|
|
50
|
+
console.print(
|
|
51
|
+
f"\nTotal Steps: {len(steps)} | Status: {status_text}",
|
|
52
|
+
style="bold",
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
if duration:
|
|
56
|
+
console.print(f"Duration: {duration:.2f}s")
|
oagi/cli/tracking.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
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 dataclasses import dataclass
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
|
|
12
|
+
from oagi.types import Action
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class StepData:
|
|
17
|
+
step_num: int
|
|
18
|
+
timestamp: datetime
|
|
19
|
+
reasoning: str | None
|
|
20
|
+
actions: list[Action]
|
|
21
|
+
action_count: int
|
|
22
|
+
status: str
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class StepTracker:
|
|
26
|
+
"""Tracks agent step execution by implementing AsyncStepObserver protocol."""
|
|
27
|
+
|
|
28
|
+
def __init__(self):
|
|
29
|
+
self.steps: list[StepData] = []
|
|
30
|
+
|
|
31
|
+
async def on_step(
|
|
32
|
+
self,
|
|
33
|
+
step_num: int,
|
|
34
|
+
reasoning: str | None,
|
|
35
|
+
actions: list[Action],
|
|
36
|
+
) -> None:
|
|
37
|
+
step_data = StepData(
|
|
38
|
+
step_num=step_num,
|
|
39
|
+
timestamp=datetime.now(),
|
|
40
|
+
reasoning=reasoning,
|
|
41
|
+
actions=actions,
|
|
42
|
+
action_count=len(actions),
|
|
43
|
+
status="running",
|
|
44
|
+
)
|
|
45
|
+
self.steps.append(step_data)
|
oagi/handler/__init__.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
from oagi.handler.async_pyautogui_action_handler import AsyncPyautoguiActionHandler
|
|
9
|
+
from oagi.handler.async_screenshot_maker import AsyncScreenshotMaker
|
|
10
|
+
from oagi.handler.pil_image import PILImage
|
|
11
|
+
from oagi.handler.pyautogui_action_handler import (
|
|
12
|
+
PyautoguiActionHandler,
|
|
13
|
+
PyautoguiConfig,
|
|
14
|
+
)
|
|
15
|
+
from oagi.handler.screenshot_maker import ScreenshotMaker
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"PILImage",
|
|
19
|
+
"PyautoguiActionHandler",
|
|
20
|
+
"PyautoguiConfig",
|
|
21
|
+
"AsyncPyautoguiActionHandler",
|
|
22
|
+
"ScreenshotMaker",
|
|
23
|
+
"AsyncScreenshotMaker",
|
|
24
|
+
]
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
import io
|
|
10
10
|
|
|
11
|
-
from
|
|
12
|
-
from
|
|
11
|
+
from ..exceptions import check_optional_dependency
|
|
12
|
+
from ..types.models.image_config import ImageConfig
|
|
13
13
|
|
|
14
14
|
check_optional_dependency("PIL", "PILImage", "desktop")
|
|
15
15
|
from PIL import Image as PILImageLib # noqa: E402
|
|
@@ -11,8 +11,8 @@ import time
|
|
|
11
11
|
|
|
12
12
|
from pydantic import BaseModel, Field
|
|
13
13
|
|
|
14
|
-
from
|
|
15
|
-
from
|
|
14
|
+
from ..exceptions import check_optional_dependency
|
|
15
|
+
from ..types import Action, ActionType
|
|
16
16
|
|
|
17
17
|
check_optional_dependency("pyautogui", "PyautoguiActionHandler", "desktop")
|
|
18
18
|
import pyautogui # noqa: E402
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
from typing import Optional
|
|
10
10
|
|
|
11
|
+
from ..types import Image
|
|
12
|
+
from ..types.models.image_config import ImageConfig
|
|
11
13
|
from .pil_image import PILImage
|
|
12
|
-
from .types import Image
|
|
13
|
-
from .types.models.image_config import ImageConfig
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class ScreenshotMaker:
|
oagi/logging.py
CHANGED
|
@@ -44,4 +44,12 @@ def get_logger(name: str) -> logging.Logger:
|
|
|
44
44
|
# Always update level in case environment variable changed
|
|
45
45
|
oagi_root.setLevel(level)
|
|
46
46
|
|
|
47
|
+
# Suppress verbose httpx logs unless DEBUG level is enabled
|
|
48
|
+
# httpx logs every HTTP request at INFO level by default
|
|
49
|
+
httpx_logger = logging.getLogger("httpx")
|
|
50
|
+
if level == logging.DEBUG:
|
|
51
|
+
httpx_logger.setLevel(logging.DEBUG)
|
|
52
|
+
else:
|
|
53
|
+
httpx_logger.setLevel(logging.WARNING)
|
|
54
|
+
|
|
47
55
|
return logger
|
oagi/task/__init__.py
CHANGED
|
@@ -6,9 +6,16 @@
|
|
|
6
6
|
# Licensed under the MIT License.
|
|
7
7
|
# -----------------------------------------------------------------------------
|
|
8
8
|
|
|
9
|
-
from .async_ import AsyncTask
|
|
9
|
+
from .async_ import AsyncActor, AsyncTask
|
|
10
10
|
from .async_short import AsyncShortTask
|
|
11
11
|
from .short import ShortTask
|
|
12
|
-
from .sync import Task
|
|
12
|
+
from .sync import Actor, Task
|
|
13
13
|
|
|
14
|
-
__all__ = [
|
|
14
|
+
__all__ = [
|
|
15
|
+
"Actor",
|
|
16
|
+
"AsyncActor",
|
|
17
|
+
"Task", # Deprecated: Use Actor instead
|
|
18
|
+
"AsyncTask", # Deprecated: Use AsyncActor instead
|
|
19
|
+
"ShortTask", # Deprecated
|
|
20
|
+
"AsyncShortTask", # Deprecated
|
|
21
|
+
]
|
oagi/task/async_.py
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
# Licensed under the MIT License.
|
|
7
7
|
# -----------------------------------------------------------------------------
|
|
8
8
|
|
|
9
|
+
import warnings
|
|
10
|
+
|
|
9
11
|
from ..client import AsyncClient
|
|
10
12
|
from ..logging import get_logger
|
|
11
13
|
from ..types import Image, Step
|
|
@@ -14,7 +16,7 @@ from .base import BaseTask
|
|
|
14
16
|
logger = get_logger("async_task")
|
|
15
17
|
|
|
16
18
|
|
|
17
|
-
class
|
|
19
|
+
class AsyncActor(BaseTask):
|
|
18
20
|
"""Async base class for task automation with the OAGI API."""
|
|
19
21
|
|
|
20
22
|
def __init__(
|
|
@@ -95,3 +97,26 @@ class AsyncTask(BaseTask):
|
|
|
95
97
|
|
|
96
98
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
97
99
|
await self.close()
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class AsyncTask(AsyncActor):
|
|
103
|
+
"""Deprecated: Use AsyncActor instead.
|
|
104
|
+
|
|
105
|
+
This class is deprecated and will be removed in a future version.
|
|
106
|
+
Please use AsyncActor instead.
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
def __init__(
|
|
110
|
+
self,
|
|
111
|
+
api_key: str | None = None,
|
|
112
|
+
base_url: str | None = None,
|
|
113
|
+
model: str = "vision-model-v1",
|
|
114
|
+
temperature: float | None = None,
|
|
115
|
+
):
|
|
116
|
+
warnings.warn(
|
|
117
|
+
"AsyncTask is deprecated and will be removed in a future version. "
|
|
118
|
+
"Please use AsyncActor instead.",
|
|
119
|
+
DeprecationWarning,
|
|
120
|
+
stacklevel=2,
|
|
121
|
+
)
|
|
122
|
+
super().__init__(api_key, base_url, model, temperature)
|
oagi/task/async_short.py
CHANGED
|
@@ -6,16 +6,22 @@
|
|
|
6
6
|
# Licensed under the MIT License.
|
|
7
7
|
# -----------------------------------------------------------------------------
|
|
8
8
|
|
|
9
|
+
import warnings
|
|
10
|
+
|
|
9
11
|
from ..logging import get_logger
|
|
10
12
|
from ..types import AsyncActionHandler, AsyncImageProvider
|
|
11
|
-
from .async_ import
|
|
13
|
+
from .async_ import AsyncActor
|
|
12
14
|
from .base import BaseAutoMode
|
|
13
15
|
|
|
14
16
|
logger = get_logger("async_short_task")
|
|
15
17
|
|
|
16
18
|
|
|
17
|
-
class AsyncShortTask(
|
|
18
|
-
"""
|
|
19
|
+
class AsyncShortTask(AsyncActor, BaseAutoMode):
|
|
20
|
+
"""Deprecated: This class is deprecated and will be removed in a future version.
|
|
21
|
+
|
|
22
|
+
Async task implementation with automatic mode for short-duration tasks.
|
|
23
|
+
Please use AsyncActor directly with custom automation logic instead.
|
|
24
|
+
"""
|
|
19
25
|
|
|
20
26
|
def __init__(
|
|
21
27
|
self,
|
|
@@ -24,6 +30,12 @@ class AsyncShortTask(AsyncTask, BaseAutoMode):
|
|
|
24
30
|
model: str = "vision-model-v1",
|
|
25
31
|
temperature: float | None = None,
|
|
26
32
|
):
|
|
33
|
+
warnings.warn(
|
|
34
|
+
"AsyncShortTask is deprecated and will be removed in a future version. "
|
|
35
|
+
"Please use AsyncActor with custom automation logic instead.",
|
|
36
|
+
DeprecationWarning,
|
|
37
|
+
stacklevel=2,
|
|
38
|
+
)
|
|
27
39
|
super().__init__(
|
|
28
40
|
api_key=api_key, base_url=base_url, model=model, temperature=temperature
|
|
29
41
|
)
|
oagi/task/base.py
CHANGED
|
@@ -45,7 +45,9 @@ class BaseTask:
|
|
|
45
45
|
task_desc: Task description
|
|
46
46
|
max_steps: Maximum number of steps
|
|
47
47
|
"""
|
|
48
|
+
self.task_id = uuid4().hex
|
|
48
49
|
self.task_description = task_desc
|
|
50
|
+
self.message_history = []
|
|
49
51
|
logger.info(f"Task initialized: '{task_desc}' (max_steps: {max_steps})")
|
|
50
52
|
|
|
51
53
|
def _validate_step_preconditions(self):
|
oagi/task/short.py
CHANGED
|
@@ -6,16 +6,22 @@
|
|
|
6
6
|
# Licensed under the MIT License.
|
|
7
7
|
# -----------------------------------------------------------------------------
|
|
8
8
|
|
|
9
|
+
import warnings
|
|
10
|
+
|
|
9
11
|
from ..logging import get_logger
|
|
10
12
|
from ..types import ActionHandler, ImageProvider
|
|
11
13
|
from .base import BaseAutoMode
|
|
12
|
-
from .sync import
|
|
14
|
+
from .sync import Actor
|
|
13
15
|
|
|
14
16
|
logger = get_logger("short_task")
|
|
15
17
|
|
|
16
18
|
|
|
17
|
-
class ShortTask(
|
|
18
|
-
"""
|
|
19
|
+
class ShortTask(Actor, BaseAutoMode):
|
|
20
|
+
"""Deprecated: This class is deprecated and will be removed in a future version.
|
|
21
|
+
|
|
22
|
+
Task implementation with automatic mode for short-duration tasks.
|
|
23
|
+
Please use Actor directly with custom automation logic instead.
|
|
24
|
+
"""
|
|
19
25
|
|
|
20
26
|
def __init__(
|
|
21
27
|
self,
|
|
@@ -24,6 +30,12 @@ class ShortTask(Task, BaseAutoMode):
|
|
|
24
30
|
model: str = "vision-model-v1",
|
|
25
31
|
temperature: float | None = None,
|
|
26
32
|
):
|
|
33
|
+
warnings.warn(
|
|
34
|
+
"ShortTask is deprecated and will be removed in a future version. "
|
|
35
|
+
"Please use Actor with custom automation logic instead.",
|
|
36
|
+
DeprecationWarning,
|
|
37
|
+
stacklevel=2,
|
|
38
|
+
)
|
|
27
39
|
super().__init__(
|
|
28
40
|
api_key=api_key, base_url=base_url, model=model, temperature=temperature
|
|
29
41
|
)
|
oagi/task/sync.py
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
# Licensed under the MIT License.
|
|
7
7
|
# -----------------------------------------------------------------------------
|
|
8
8
|
|
|
9
|
+
import warnings
|
|
10
|
+
|
|
9
11
|
from ..client import SyncClient
|
|
10
12
|
from ..logging import get_logger
|
|
11
13
|
from ..types import Image, Step
|
|
@@ -14,7 +16,7 @@ from .base import BaseTask
|
|
|
14
16
|
logger = get_logger("task")
|
|
15
17
|
|
|
16
18
|
|
|
17
|
-
class
|
|
19
|
+
class Actor(BaseTask):
|
|
18
20
|
"""Base class for task automation with the OAGI API."""
|
|
19
21
|
|
|
20
22
|
def __init__(
|
|
@@ -95,3 +97,26 @@ class Task(BaseTask):
|
|
|
95
97
|
|
|
96
98
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
97
99
|
self.close()
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class Task(Actor):
|
|
103
|
+
"""Deprecated: Use Actor instead.
|
|
104
|
+
|
|
105
|
+
This class is deprecated and will be removed in a future version.
|
|
106
|
+
Please use Actor instead.
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
def __init__(
|
|
110
|
+
self,
|
|
111
|
+
api_key: str | None = None,
|
|
112
|
+
base_url: str | None = None,
|
|
113
|
+
model: str = "vision-model-v1",
|
|
114
|
+
temperature: float | None = None,
|
|
115
|
+
):
|
|
116
|
+
warnings.warn(
|
|
117
|
+
"Task is deprecated and will be removed in a future version. "
|
|
118
|
+
"Please use Actor instead.",
|
|
119
|
+
DeprecationWarning,
|
|
120
|
+
stacklevel=2,
|
|
121
|
+
)
|
|
122
|
+
super().__init__(api_key, base_url, model, temperature)
|
oagi/types/__init__.py
CHANGED
|
@@ -12,6 +12,7 @@ from .async_image_provider import AsyncImageProvider
|
|
|
12
12
|
from .image import Image
|
|
13
13
|
from .image_provider import ImageProvider
|
|
14
14
|
from .models import Action, ActionType, ImageConfig, Step
|
|
15
|
+
from .step_observer import AsyncStepObserver
|
|
15
16
|
from .url_image import URLImage
|
|
16
17
|
|
|
17
18
|
__all__ = [
|
|
@@ -24,5 +25,6 @@ __all__ = [
|
|
|
24
25
|
"AsyncActionHandler",
|
|
25
26
|
"ImageProvider",
|
|
26
27
|
"AsyncImageProvider",
|
|
28
|
+
"AsyncStepObserver",
|
|
27
29
|
"URLImage",
|
|
28
30
|
]
|
|
@@ -0,0 +1,34 @@
|
|
|
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 typing import Protocol
|
|
10
|
+
|
|
11
|
+
from .models import Action
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class AsyncStepObserver(Protocol):
|
|
15
|
+
"""Protocol for observing agent step execution.
|
|
16
|
+
|
|
17
|
+
Observers receive step information (reasoning and actions) as agents
|
|
18
|
+
execute tasks, enabling tracking, logging, or other side effects.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
async def on_step(
|
|
22
|
+
self,
|
|
23
|
+
step_num: int,
|
|
24
|
+
reasoning: str | None,
|
|
25
|
+
actions: list[Action],
|
|
26
|
+
) -> None:
|
|
27
|
+
"""Called when an agent executes a step.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
step_num: The step number (1-indexed)
|
|
31
|
+
reasoning: The reasoning/thinking for this step (if available)
|
|
32
|
+
actions: The list of actions being executed in this step
|
|
33
|
+
"""
|
|
34
|
+
...
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: oagi-core
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.1
|
|
4
4
|
Summary: Official API of OpenAGI Foundation
|
|
5
5
|
Project-URL: Homepage, https://github.com/agiopen-org/oagi
|
|
6
6
|
Author-email: OpenAGI Foundation <contact@agiopen.org>
|
|
@@ -28,6 +28,7 @@ License: MIT License
|
|
|
28
28
|
Requires-Python: >=3.10
|
|
29
29
|
Requires-Dist: httpx>=0.28.0
|
|
30
30
|
Requires-Dist: pydantic>=2.0.0
|
|
31
|
+
Requires-Dist: rich>=13.0.0
|
|
31
32
|
Provides-Extra: desktop
|
|
32
33
|
Requires-Dist: pillow>=11.3.0; extra == 'desktop'
|
|
33
34
|
Requires-Dist: pyautogui>=0.9.54; extra == 'desktop'
|
|
@@ -75,22 +76,6 @@ export OAGI_API_KEY="your-api-key"
|
|
|
75
76
|
export OAGI_BASE_URL="https://api.oagi.com" # or your server URL
|
|
76
77
|
```
|
|
77
78
|
|
|
78
|
-
### Single-Step Analysis
|
|
79
|
-
|
|
80
|
-
Analyze a screenshot and get recommended actions:
|
|
81
|
-
|
|
82
|
-
```python
|
|
83
|
-
from oagi import single_step
|
|
84
|
-
|
|
85
|
-
step = single_step(
|
|
86
|
-
task_description="Click the submit button",
|
|
87
|
-
screenshot="screenshot.png" # or bytes, or Image object
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
print(f"Actions: {step.actions}")
|
|
91
|
-
print(f"Complete: {step.is_complete}")
|
|
92
|
-
```
|
|
93
|
-
|
|
94
79
|
### Automated Task Execution
|
|
95
80
|
|
|
96
81
|
Run tasks automatically with screenshot capture and action execution:
|
|
@@ -142,9 +127,6 @@ config = ImageConfig(
|
|
|
142
127
|
height=700
|
|
143
128
|
)
|
|
144
129
|
compressed = image.transform(config)
|
|
145
|
-
|
|
146
|
-
# Use with single_step
|
|
147
|
-
step = single_step("Click button", screenshot=compressed)
|
|
148
130
|
```
|
|
149
131
|
|
|
150
132
|
### Async Support
|
|
@@ -153,16 +135,9 @@ Use async client for non-blocking operations and better concurrency:
|
|
|
153
135
|
|
|
154
136
|
```python
|
|
155
137
|
import asyncio
|
|
156
|
-
from oagi import
|
|
138
|
+
from oagi import AsyncShortTask
|
|
157
139
|
|
|
158
140
|
async def main():
|
|
159
|
-
# Single-step async analysis
|
|
160
|
-
step = await async_single_step(
|
|
161
|
-
"Find the search bar",
|
|
162
|
-
screenshot="screenshot.png"
|
|
163
|
-
)
|
|
164
|
-
print(f"Found {len(step.actions)} actions")
|
|
165
|
-
|
|
166
141
|
# Async task automation
|
|
167
142
|
task = AsyncShortTask()
|
|
168
143
|
async with task:
|
|
@@ -176,7 +151,6 @@ asyncio.run(main())
|
|
|
176
151
|
|
|
177
152
|
See the [`examples/`](examples/) directory for more usage patterns:
|
|
178
153
|
- `google_weather.py` - Basic task execution with `ShortTask`
|
|
179
|
-
- `single_step.py` - Basic single-step inference
|
|
180
154
|
- `screenshot_with_config.py` - Image compression and optimization
|
|
181
155
|
- `execute_task_auto.py` - Automated task execution
|
|
182
156
|
- `socketio_server_basic.py` - Socket.IO server example
|
|
@@ -1,33 +1,34 @@
|
|
|
1
|
-
oagi/__init__.py,sha256=
|
|
2
|
-
oagi/async_pyautogui_action_handler.py,sha256=F-lKyePCONWI03WnSxpX_QwxONbvnfdQu51wTod6mdw,1614
|
|
3
|
-
oagi/async_screenshot_maker.py,sha256=pI-dbLcYOzcO1ffgTmozAdbYJQNBPKA7hmqj1RxEmIY,1688
|
|
4
|
-
oagi/async_single_step.py,sha256=FxOSoZKKegHx0by41a4qrJDPoYZV0qZKtdTNMU8Uqz4,2955
|
|
1
|
+
oagi/__init__.py,sha256=cSqJ61OyscGJLHBCn53cS4PrCKC4DQl5xRDNkGR0TXo,3041
|
|
5
2
|
oagi/exceptions.py,sha256=Rco37GQTPYUfc2vRO3hozxPF_s8mKFDpFvBg2UKWo3Y,3066
|
|
6
|
-
oagi/logging.py,sha256=
|
|
7
|
-
oagi/pil_image.py,sha256=xs6nDRyjTCaoeIMb3GV5d4U6dbG5M5jr2TdMc57eJMo,4078
|
|
8
|
-
oagi/pyautogui_action_handler.py,sha256=N-4XcCV8f92ViuwXnpgk3ILGrlhR_8L1CKkGq_ub5gQ,9952
|
|
9
|
-
oagi/screenshot_maker.py,sha256=sVuW7jn-K4FmLhmYI-akdNI-UVcTeBzh9P1_qJhoq1s,1282
|
|
10
|
-
oagi/single_step.py,sha256=62Zip4Uql6E-ZIX6vAlrBoveKqrnABzGqHdLCzju4ag,3138
|
|
3
|
+
oagi/logging.py,sha256=YT3KCMFj5fzO98R9xlDDgfSotUuz1xRD6OZeYM2rKoo,1760
|
|
11
4
|
oagi/agent/__init__.py,sha256=JU9zuWuDzpzitsVJB4z5ddvx8RMm5nbP-bjUCL1Sfvo,834
|
|
12
|
-
oagi/agent/default.py,sha256=
|
|
13
|
-
oagi/agent/factories.py,sha256=
|
|
5
|
+
oagi/agent/default.py,sha256=FaaNTssDt5rcuEUppznATqNOTTEJFThcxbBpPChzwnU,3108
|
|
6
|
+
oagi/agent/factories.py,sha256=eE0hcXyJX4dk2nvRRlEvQPJqkBraeIaT7nXiED2lH_Q,1605
|
|
14
7
|
oagi/agent/protocol.py,sha256=IQJGiMN4yZIacrh5e9JQsoM9TyHb8wJRQR4LAk8dSA0,1615
|
|
15
8
|
oagi/agent/registry.py,sha256=4oG65E_bV47Xl6F-HX9KaVoV0pcoC1uRRDU4RT_m3uU,4841
|
|
16
9
|
oagi/agent/tasker/__init__.py,sha256=faOC5ONY8ZKr4CjofC6HYg1WKWc1UiaGB9VHy8W280M,800
|
|
17
10
|
oagi/agent/tasker/memory.py,sha256=JsJjUMpnJoKW4VFzd8FI4M-FhnEihTecL61KVgO_YBI,6051
|
|
18
11
|
oagi/agent/tasker/models.py,sha256=VzvHB5hLv6qyYcyNiojVIEDlTzeGE4Quswk4EVIbzoI,2180
|
|
19
12
|
oagi/agent/tasker/planner.py,sha256=j0Zhk6kw0LFtxkpjFhmeaTBxZfKLf3qqCNpGAuMuODw,13650
|
|
20
|
-
oagi/agent/tasker/taskee_agent.py,sha256=
|
|
21
|
-
oagi/agent/tasker/tasker_agent.py,sha256=
|
|
13
|
+
oagi/agent/tasker/taskee_agent.py,sha256=9t_uIilH2KCnkv013mIfk2VPPNKz6wJtTyjEJLvZOzQ,14093
|
|
14
|
+
oagi/agent/tasker/tasker_agent.py,sha256=d4khAfd1_Ra1KP7Y2un8BwUBeGh-Frwp2xWc7prI6F4,10954
|
|
22
15
|
oagi/cli/__init__.py,sha256=aDnJViTseShpo5fdGPTj-ELysZhmdvB6Z8mEj2D-_N4,359
|
|
23
|
-
oagi/cli/agent.py,sha256=
|
|
16
|
+
oagi/cli/agent.py,sha256=q0dLyjrzT3PkZreaXSqpHN_rH5fAB_jHyLonPy-V6hk,4453
|
|
17
|
+
oagi/cli/display.py,sha256=rkAxuHa40ZtKdmvwARev1rgyfsNyVvQ-J6RdjOZIPwc,1729
|
|
24
18
|
oagi/cli/main.py,sha256=faHns0HaQCGyylDn2YZLpjQESuEiMYjoQVoMkt8FsH4,2292
|
|
25
19
|
oagi/cli/server.py,sha256=Z1ic8r55yaeQBFRCsMNZStC1jRiJdnDGqe9On9LmFzQ,3031
|
|
20
|
+
oagi/cli/tracking.py,sha256=jPH6QDUUwnfZ8bjQU6deofBmBflTEOOCINwinQJz9OI,1147
|
|
26
21
|
oagi/cli/utils.py,sha256=QvDyoP0nU9aq4ORjnvJJAGRECmjh9WAmilKuk6zOGM8,2615
|
|
27
22
|
oagi/client/__init__.py,sha256=F9DShPUdb6vZYmN1fpM1VYzp4MWqUao_e_R1KYmM4Q4,410
|
|
28
23
|
oagi/client/async_.py,sha256=t-GPHcz6xbHx_RPFv1V_hwZ1_f-O9ONH-Ahr0w-Nz8M,11046
|
|
29
24
|
oagi/client/base.py,sha256=TlZk4LaYFlOQFmTTgTDzZ1XRAixqNO0pCwgJ7VhZOSU,17101
|
|
30
25
|
oagi/client/sync.py,sha256=QKd6nTUXtyn1Am8YlFcpsoLh1KuHhQgbMemIkb7r39g,10882
|
|
26
|
+
oagi/handler/__init__.py,sha256=Ha11L42K33K3L9S4lQ10UC0DnD5g6egtQUsJpS_tKgg,835
|
|
27
|
+
oagi/handler/async_pyautogui_action_handler.py,sha256=hQzseR1yBD0QMpgsEVNsUmuApGVAIIyGYD06BXd82Dc,1615
|
|
28
|
+
oagi/handler/async_screenshot_maker.py,sha256=8QCtUV59ozpOpvkqhUMb8QDI2qje2gsoFT1qB60tfJM,1689
|
|
29
|
+
oagi/handler/pil_image.py,sha256=yUcAoGBL-aZ0PCjSaAmQsDwtyzjldXHqXQp_OYRk6e4,4080
|
|
30
|
+
oagi/handler/pyautogui_action_handler.py,sha256=rhQAes81-hR5KoFiB4Ph6Cw5r04vYiqvZx64eA31pBw,9954
|
|
31
|
+
oagi/handler/screenshot_maker.py,sha256=j1jTW-awx3vAnb1N5_FIMBC0Z-rNVQbiBP-S6Gh5dlE,1284
|
|
31
32
|
oagi/server/__init__.py,sha256=uZx8u3vJUb87kkNzwmmVrgAgbqRu0WxyMIQCLSx56kk,452
|
|
32
33
|
oagi/server/agent_wrappers.py,sha256=4f6ZKvqy9TDA57QRHGjAQVhpHmPE5QNeewmmURg5Ajo,3288
|
|
33
34
|
oagi/server/config.py,sha256=RstTWbPwWCfW9ZRJcns3uCbdhXxuUnerKjxR1pCGbqY,1602
|
|
@@ -35,26 +36,27 @@ oagi/server/main.py,sha256=jnTxk7Prc5CzlsUnkBNJp4MOoYN-7HN_Be_m1d3COa8,4829
|
|
|
35
36
|
oagi/server/models.py,sha256=E0R80zIz-YLpwx4VQyADQEnM89ZY9bXmjuswfhCuqMc,2595
|
|
36
37
|
oagi/server/session_store.py,sha256=UI14NiApAwvZWmMOG4SvPE2WkbGkNTHToZhTXQjN2_U,3624
|
|
37
38
|
oagi/server/socketio_server.py,sha256=UeNm9bemYMZqtYWp3i09BLUyEWW6t8yTkl2pjpSeT_M,14359
|
|
38
|
-
oagi/task/__init__.py,sha256=
|
|
39
|
-
oagi/task/async_.py,sha256=
|
|
40
|
-
oagi/task/async_short.py,sha256=
|
|
41
|
-
oagi/task/base.py,sha256=
|
|
42
|
-
oagi/task/short.py,sha256=
|
|
43
|
-
oagi/task/sync.py,sha256=
|
|
44
|
-
oagi/types/__init__.py,sha256=
|
|
39
|
+
oagi/task/__init__.py,sha256=g_8_7ZLDLKuCGzyrB42OzY3gSOjd_SxzkJW3_pf-PXs,662
|
|
40
|
+
oagi/task/async_.py,sha256=IwoLy0SHLNI7k6I5Ja0WmWa8U-SDLogoM-qHFjbVISU,3924
|
|
41
|
+
oagi/task/async_short.py,sha256=LjgWotSxyuTRpNNFsAhAP0Y0raze0GHyoWqthLaPoS8,2755
|
|
42
|
+
oagi/task/base.py,sha256=Udp_Yl5GVUWR7RtayrQlwa8Yg_Nrsyal3mGEkaG8FmE,4447
|
|
43
|
+
oagi/task/short.py,sha256=mgXao6AXuR8XvHwnf3KjzsCm1PZRLzKg1R-2JmS2yoM,2564
|
|
44
|
+
oagi/task/sync.py,sha256=Ag171gJxcWkLgmna4X5Ipgf3qEDDj2JnZEQ9b-mH0mM,3787
|
|
45
|
+
oagi/types/__init__.py,sha256=Vz6JArE8XvBWlES8CVLy-Nx97gooh1OSsltBL6iaFiM,884
|
|
45
46
|
oagi/types/action_handler.py,sha256=NH8E-m5qpGqWcXzTSWfF7W0Xdp8SkzJsbhCmQ0B96cg,1075
|
|
46
47
|
oagi/types/async_action_handler.py,sha256=k1AaqSkFcXlxwW8sn-w0WFHGsIqHFLbcOPrkknmSVug,1116
|
|
47
48
|
oagi/types/async_image_provider.py,sha256=wnhRyPtTmuALt45Qore74-RCkP5yxU9sZGjvOzFqzOk,1170
|
|
48
49
|
oagi/types/image.py,sha256=KgPCCTJ6D5vHIaGZdbTE7eQEa1WlT6G9tf59ZuUCV2U,537
|
|
49
50
|
oagi/types/image_provider.py,sha256=oYFdOYznrK_VOR9egzOjw5wFM5w8EY2sY01pH0ANAgU,1112
|
|
51
|
+
oagi/types/step_observer.py,sha256=KDw7yQxA_I6T2DqElspAOMa8rBJTYFWBNHfC-9NmasM,1025
|
|
50
52
|
oagi/types/url_image.py,sha256=iOwtXj2uwY6dVtDP7uvQLPvK-aTxkdrzhw_R4C6GwBw,1334
|
|
51
53
|
oagi/types/models/__init__.py,sha256=I86Z2moM8hCog_1K1FG_uATcBmWFv_UFetLAjzPzWAY,742
|
|
52
54
|
oagi/types/models/action.py,sha256=hh6mRRSSWgrW4jpZo71zGMCOcZpV5_COu4148uG6G48,967
|
|
53
55
|
oagi/types/models/client.py,sha256=fCN18DBq5XDjNyYB8w-2dFeQ_K9ywwdyh-rXa0GToU4,1357
|
|
54
56
|
oagi/types/models/image_config.py,sha256=tl6abVg_-IAPLwpaWprgknXu7wRWriMg-AEVyUX73v0,1567
|
|
55
57
|
oagi/types/models/step.py,sha256=RSI4H_2rrUBq_xyCoWKaq7JHdJWNobtQppaKC1l0aWU,471
|
|
56
|
-
oagi_core-0.9.
|
|
57
|
-
oagi_core-0.9.
|
|
58
|
-
oagi_core-0.9.
|
|
59
|
-
oagi_core-0.9.
|
|
60
|
-
oagi_core-0.9.
|
|
58
|
+
oagi_core-0.9.1.dist-info/METADATA,sha256=zJ3xNe4fGIUHAff90wp0s_d98QsjEunz848CWcoQd2w,7543
|
|
59
|
+
oagi_core-0.9.1.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
60
|
+
oagi_core-0.9.1.dist-info/entry_points.txt,sha256=zzgsOSWX6aN3KUB0Z1it8DMxFFBJBqmZVqMVAJRjYuw,44
|
|
61
|
+
oagi_core-0.9.1.dist-info/licenses/LICENSE,sha256=sy5DLA2M29jFT4UfWsuBF9BAr3FnRkYtnAu6oDZiIf8,1075
|
|
62
|
+
oagi_core-0.9.1.dist-info/RECORD,,
|
oagi/async_single_step.py
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
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 pathlib import Path
|
|
10
|
-
|
|
11
|
-
from .task import AsyncTask
|
|
12
|
-
from .types import Image, Step
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
async def async_single_step(
|
|
16
|
-
task_description: str,
|
|
17
|
-
screenshot: str | bytes | Path | Image,
|
|
18
|
-
instruction: str | None = None,
|
|
19
|
-
api_key: str | None = None,
|
|
20
|
-
base_url: str | None = None,
|
|
21
|
-
temperature: float | None = None,
|
|
22
|
-
) -> Step:
|
|
23
|
-
"""
|
|
24
|
-
Perform a single-step inference asynchronously without maintaining task state.
|
|
25
|
-
|
|
26
|
-
This is useful for one-off analyses where you don't need to maintain
|
|
27
|
-
a conversation or task context across multiple steps.
|
|
28
|
-
|
|
29
|
-
Args:
|
|
30
|
-
task_description: Description of the task to perform
|
|
31
|
-
screenshot: Screenshot as Image, bytes, or file path
|
|
32
|
-
instruction: Optional additional instruction for the task
|
|
33
|
-
api_key: OAGI API key (uses environment variable if not provided)
|
|
34
|
-
base_url: OAGI base URL (uses environment variable if not provided)
|
|
35
|
-
temperature: Sampling temperature (0.0-2.0) for LLM inference
|
|
36
|
-
|
|
37
|
-
Returns:
|
|
38
|
-
Step: Object containing reasoning, actions, and completion status
|
|
39
|
-
|
|
40
|
-
Example:
|
|
41
|
-
>>> # Using with bytes
|
|
42
|
-
>>> import asyncio
|
|
43
|
-
>>> async def main():
|
|
44
|
-
... with open("screenshot.png", "rb") as f:
|
|
45
|
-
... screenshot_bytes = f.read()
|
|
46
|
-
... step = await async_single_step(
|
|
47
|
-
... "Click the submit button",
|
|
48
|
-
... screenshot=screenshot_bytes
|
|
49
|
-
... )
|
|
50
|
-
... print(f"Actions: {step.actions}")
|
|
51
|
-
>>> asyncio.run(main())
|
|
52
|
-
|
|
53
|
-
>>> # Using with file path
|
|
54
|
-
>>> step = await async_single_step(
|
|
55
|
-
... "Find the search box",
|
|
56
|
-
... screenshot="screenshot.png"
|
|
57
|
-
... )
|
|
58
|
-
|
|
59
|
-
>>> # Using with PILImage
|
|
60
|
-
>>> image = PILImage.from_file("screenshot.png")
|
|
61
|
-
>>> step = await async_single_step(
|
|
62
|
-
... "Click next page",
|
|
63
|
-
... screenshot=image
|
|
64
|
-
... )
|
|
65
|
-
"""
|
|
66
|
-
# Lazy import PILImage only when needed
|
|
67
|
-
from .pil_image import PILImage # noqa: PLC0415
|
|
68
|
-
|
|
69
|
-
# Handle different screenshot input types
|
|
70
|
-
if isinstance(screenshot, (str, Path)):
|
|
71
|
-
screenshot = PILImage.from_file(str(screenshot))
|
|
72
|
-
elif isinstance(screenshot, bytes):
|
|
73
|
-
screenshot = PILImage.from_bytes(screenshot)
|
|
74
|
-
|
|
75
|
-
# Create a temporary task instance
|
|
76
|
-
task = AsyncTask(api_key=api_key, base_url=base_url, temperature=temperature)
|
|
77
|
-
|
|
78
|
-
try:
|
|
79
|
-
# Initialize task and perform single step
|
|
80
|
-
await task.init_task(task_description)
|
|
81
|
-
result = await task.step(screenshot, instruction=instruction)
|
|
82
|
-
return result
|
|
83
|
-
finally:
|
|
84
|
-
# Clean up resources
|
|
85
|
-
await task.close()
|
oagi/single_step.py
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
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 pathlib import Path
|
|
10
|
-
|
|
11
|
-
from .task import Task
|
|
12
|
-
from .types import Image, Step
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def single_step(
|
|
16
|
-
task_description: str,
|
|
17
|
-
screenshot: str | bytes | Path | Image,
|
|
18
|
-
instruction: str | None = None,
|
|
19
|
-
api_key: str | None = None,
|
|
20
|
-
base_url: str | None = None,
|
|
21
|
-
temperature: float | None = None,
|
|
22
|
-
) -> Step:
|
|
23
|
-
"""
|
|
24
|
-
Perform a single-step inference without maintaining task state.
|
|
25
|
-
|
|
26
|
-
This is useful for one-off analyses where you don't need to maintain
|
|
27
|
-
a conversation or task context across multiple steps.
|
|
28
|
-
|
|
29
|
-
Args:
|
|
30
|
-
task_description: Description of the task to perform
|
|
31
|
-
screenshot: Screenshot as Image, bytes, or file path
|
|
32
|
-
instruction: Optional additional instruction for the task
|
|
33
|
-
api_key: OAGI API key (uses environment variable if not provided)
|
|
34
|
-
base_url: OAGI base URL (uses environment variable if not provided)
|
|
35
|
-
temperature: Sampling temperature (0.0-2.0) for LLM inference
|
|
36
|
-
|
|
37
|
-
Returns:
|
|
38
|
-
Step: Object containing reasoning, actions, and completion status
|
|
39
|
-
|
|
40
|
-
Example:
|
|
41
|
-
>>> # Using with bytes
|
|
42
|
-
>>> with open("screenshot.png", "rb") as f:
|
|
43
|
-
... image_bytes = f.read()
|
|
44
|
-
>>> step = single_step(
|
|
45
|
-
... task_description="Click the submit button",
|
|
46
|
-
... screenshot=image_bytes
|
|
47
|
-
... )
|
|
48
|
-
|
|
49
|
-
>>> # Using with file path
|
|
50
|
-
>>> step = single_step(
|
|
51
|
-
... task_description="Fill in the form",
|
|
52
|
-
... screenshot=Path("screenshot.png"),
|
|
53
|
-
... instruction="Use test@example.com for email"
|
|
54
|
-
... )
|
|
55
|
-
|
|
56
|
-
>>> # Using with Image object
|
|
57
|
-
>>> from oagi.types import Image
|
|
58
|
-
>>> image = Image(...)
|
|
59
|
-
>>> step = single_step(
|
|
60
|
-
... task_description="Navigate to settings",
|
|
61
|
-
... screenshot=image
|
|
62
|
-
... )
|
|
63
|
-
"""
|
|
64
|
-
# Lazy import PILImage only when needed
|
|
65
|
-
from .pil_image import PILImage # noqa: PLC0415
|
|
66
|
-
|
|
67
|
-
# Convert file paths to bytes using PILImage
|
|
68
|
-
if isinstance(screenshot, (str, Path)):
|
|
69
|
-
path = Path(screenshot) if isinstance(screenshot, str) else screenshot
|
|
70
|
-
if path.exists():
|
|
71
|
-
pil_image = PILImage.from_file(str(path))
|
|
72
|
-
screenshot_bytes = pil_image.read()
|
|
73
|
-
else:
|
|
74
|
-
raise FileNotFoundError(f"Screenshot file not found: {path}")
|
|
75
|
-
elif isinstance(screenshot, bytes):
|
|
76
|
-
screenshot_bytes = screenshot
|
|
77
|
-
elif isinstance(screenshot, Image):
|
|
78
|
-
screenshot_bytes = screenshot.read()
|
|
79
|
-
else:
|
|
80
|
-
raise ValueError(
|
|
81
|
-
f"screenshot must be Image, bytes, str, or Path, got {type(screenshot)}"
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
# Use Task to perform single step
|
|
85
|
-
with Task(api_key=api_key, base_url=base_url, temperature=temperature) as task:
|
|
86
|
-
task.init_task(task_description)
|
|
87
|
-
return task.step(screenshot_bytes, instruction=instruction)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|