oagi 0.5.0__py3-none-any.whl → 0.6.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.
Potentially problematic release.
This version of oagi might be problematic. Click here for more details.
- oagi/__init__.py +35 -20
- oagi/async_single_step.py +7 -5
- oagi/client/__init__.py +12 -0
- oagi/client/async_.py +137 -0
- oagi/client/base.py +183 -0
- oagi/client/sync.py +142 -0
- oagi/single_step.py +6 -2
- oagi/task/__init__.py +14 -0
- oagi/{async_task.py → task/async_.py} +23 -50
- oagi/{async_short_task.py → task/async_short.py} +18 -17
- oagi/task/base.py +130 -0
- oagi/{short_task.py → task/short.py} +18 -13
- oagi/{task.py → task/sync.py} +25 -49
- oagi/types/models/__init__.py +11 -1
- oagi/types/models/client.py +45 -0
- {oagi-0.5.0.dist-info → oagi-0.6.1.dist-info}/METADATA +1 -1
- oagi-0.6.1.dist-info/RECORD +35 -0
- oagi/async_client.py +0 -247
- oagi/sync_client.py +0 -297
- oagi-0.5.0.dist-info/RECORD +0 -30
- {oagi-0.5.0.dist-info → oagi-0.6.1.dist-info}/WHEEL +0 -0
- {oagi-0.5.0.dist-info → oagi-0.6.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -6,15 +6,15 @@
|
|
|
6
6
|
# Licensed under the MIT License.
|
|
7
7
|
# -----------------------------------------------------------------------------
|
|
8
8
|
|
|
9
|
-
from
|
|
10
|
-
from
|
|
11
|
-
from
|
|
12
|
-
from .
|
|
9
|
+
from ..client import AsyncClient
|
|
10
|
+
from ..logging import get_logger
|
|
11
|
+
from ..types import Image, Step
|
|
12
|
+
from .base import BaseTask, encode_screenshot_from_bytes
|
|
13
13
|
|
|
14
14
|
logger = get_logger("async_task")
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
class AsyncTask:
|
|
17
|
+
class AsyncTask(BaseTask):
|
|
18
18
|
"""Async base class for task automation with the OAGI API."""
|
|
19
19
|
|
|
20
20
|
def __init__(
|
|
@@ -22,15 +22,12 @@ class AsyncTask:
|
|
|
22
22
|
api_key: str | None = None,
|
|
23
23
|
base_url: str | None = None,
|
|
24
24
|
model: str = "vision-model-v1",
|
|
25
|
+
temperature: float | None = None,
|
|
25
26
|
):
|
|
27
|
+
super().__init__(api_key, base_url, model, temperature)
|
|
26
28
|
self.client = AsyncClient(base_url=base_url, api_key=api_key)
|
|
27
29
|
self.api_key = self.client.api_key
|
|
28
30
|
self.base_url = self.client.base_url
|
|
29
|
-
self.task_id: str | None = None
|
|
30
|
-
self.task_description: str | None = None
|
|
31
|
-
self.model = model
|
|
32
|
-
self.last_task_id: str | None = None
|
|
33
|
-
self.history_steps: int | None = None
|
|
34
31
|
|
|
35
32
|
async def init_task(
|
|
36
33
|
self,
|
|
@@ -47,47 +44,42 @@ class AsyncTask:
|
|
|
47
44
|
last_task_id: Previous task ID to retrieve history from
|
|
48
45
|
history_steps: Number of historical steps to include (default: 1)
|
|
49
46
|
"""
|
|
50
|
-
self.
|
|
51
|
-
self.last_task_id = last_task_id
|
|
52
|
-
self.history_steps = history_steps
|
|
47
|
+
self._prepare_init_task(task_desc, last_task_id, history_steps)
|
|
53
48
|
response = await self.client.create_message(
|
|
54
49
|
model=self.model,
|
|
55
50
|
screenshot="",
|
|
56
51
|
task_description=self.task_description,
|
|
57
52
|
task_id=None,
|
|
58
53
|
)
|
|
59
|
-
self.
|
|
60
|
-
logger.info(f"Async task initialized: '{task_desc}' (max_steps: {max_steps})")
|
|
61
|
-
if last_task_id:
|
|
62
|
-
logger.info(
|
|
63
|
-
f"Will include {history_steps or 1} steps from previous task: {last_task_id}"
|
|
64
|
-
)
|
|
54
|
+
self._process_init_response(response, task_desc, max_steps, prefix="Async ")
|
|
65
55
|
|
|
66
56
|
async def step(
|
|
67
|
-
self,
|
|
57
|
+
self,
|
|
58
|
+
screenshot: Image | bytes,
|
|
59
|
+
instruction: str | None = None,
|
|
60
|
+
temperature: float | None = None,
|
|
68
61
|
) -> Step:
|
|
69
62
|
"""Send screenshot to the server and get the next actions.
|
|
70
63
|
|
|
71
64
|
Args:
|
|
72
65
|
screenshot: Screenshot as Image object or raw bytes
|
|
73
66
|
instruction: Optional additional instruction for this step (only works with existing task_id)
|
|
67
|
+
temperature: Sampling temperature for this step (overrides task default if provided)
|
|
74
68
|
|
|
75
69
|
Returns:
|
|
76
70
|
Step: The actions and reasoning for this step
|
|
77
71
|
"""
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
logger.debug(f"Executing async step for task: '{self.task_description}'")
|
|
72
|
+
self._validate_step_preconditions()
|
|
73
|
+
self._log_step_execution(prefix="async ")
|
|
82
74
|
|
|
83
75
|
try:
|
|
84
76
|
# Convert Image to bytes using the protocol
|
|
85
|
-
|
|
86
|
-
screenshot_bytes = screenshot.read()
|
|
87
|
-
else:
|
|
88
|
-
screenshot_bytes = screenshot
|
|
77
|
+
screenshot_bytes = self._prepare_screenshot(screenshot)
|
|
89
78
|
screenshot_b64 = encode_screenshot_from_bytes(screenshot_bytes)
|
|
90
79
|
|
|
80
|
+
# Use provided temperature or fall back to task default
|
|
81
|
+
temp = self._get_temperature(temperature)
|
|
82
|
+
|
|
91
83
|
# Call API
|
|
92
84
|
response = await self.client.create_message(
|
|
93
85
|
model=self.model,
|
|
@@ -97,33 +89,14 @@ class AsyncTask:
|
|
|
97
89
|
instruction=instruction,
|
|
98
90
|
last_task_id=self.last_task_id if self.task_id else None,
|
|
99
91
|
history_steps=self.history_steps if self.task_id else None,
|
|
92
|
+
temperature=temp,
|
|
100
93
|
)
|
|
101
94
|
|
|
102
95
|
# Update task_id from response
|
|
103
|
-
|
|
104
|
-
if self.task_id is None:
|
|
105
|
-
logger.debug(f"Task ID assigned: {response.task_id}")
|
|
106
|
-
else:
|
|
107
|
-
logger.debug(
|
|
108
|
-
f"Task ID changed: {self.task_id} -> {response.task_id}"
|
|
109
|
-
)
|
|
110
|
-
self.task_id = response.task_id
|
|
96
|
+
self._update_task_id(response)
|
|
111
97
|
|
|
112
98
|
# Convert API response to Step
|
|
113
|
-
|
|
114
|
-
reason=response.reason,
|
|
115
|
-
actions=response.actions,
|
|
116
|
-
stop=response.is_complete,
|
|
117
|
-
)
|
|
118
|
-
|
|
119
|
-
if response.is_complete:
|
|
120
|
-
logger.info(f"Async task completed after {response.current_step} steps")
|
|
121
|
-
else:
|
|
122
|
-
logger.debug(
|
|
123
|
-
f"Async step {response.current_step} completed with {len(response.actions)} actions"
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
return result
|
|
99
|
+
return self._build_step_response(response, prefix="Async ")
|
|
127
100
|
|
|
128
101
|
except Exception as e:
|
|
129
102
|
logger.error(f"Error during async step execution: {e}")
|
|
@@ -6,14 +6,15 @@
|
|
|
6
6
|
# Licensed under the MIT License.
|
|
7
7
|
# -----------------------------------------------------------------------------
|
|
8
8
|
|
|
9
|
-
from
|
|
10
|
-
from
|
|
11
|
-
from .
|
|
9
|
+
from ..logging import get_logger
|
|
10
|
+
from ..types import AsyncActionHandler, AsyncImageProvider
|
|
11
|
+
from .async_ import AsyncTask
|
|
12
|
+
from .base import BaseAutoMode
|
|
12
13
|
|
|
13
14
|
logger = get_logger("async_short_task")
|
|
14
15
|
|
|
15
16
|
|
|
16
|
-
class AsyncShortTask(AsyncTask):
|
|
17
|
+
class AsyncShortTask(AsyncTask, BaseAutoMode):
|
|
17
18
|
"""Async task implementation with automatic mode for short-duration tasks."""
|
|
18
19
|
|
|
19
20
|
def __init__(
|
|
@@ -21,8 +22,11 @@ class AsyncShortTask(AsyncTask):
|
|
|
21
22
|
api_key: str | None = None,
|
|
22
23
|
base_url: str | None = None,
|
|
23
24
|
model: str = "vision-model-v1",
|
|
25
|
+
temperature: float | None = None,
|
|
24
26
|
):
|
|
25
|
-
super().__init__(
|
|
27
|
+
super().__init__(
|
|
28
|
+
api_key=api_key, base_url=base_url, model=model, temperature=temperature
|
|
29
|
+
)
|
|
26
30
|
|
|
27
31
|
async def auto_mode(
|
|
28
32
|
self,
|
|
@@ -32,6 +36,7 @@ class AsyncShortTask(AsyncTask):
|
|
|
32
36
|
image_provider: AsyncImageProvider = None,
|
|
33
37
|
last_task_id: str | None = None,
|
|
34
38
|
history_steps: int | None = None,
|
|
39
|
+
temperature: float | None = None,
|
|
35
40
|
) -> bool:
|
|
36
41
|
"""Run the task in automatic mode with the provided executor and image provider.
|
|
37
42
|
|
|
@@ -42,10 +47,10 @@ class AsyncShortTask(AsyncTask):
|
|
|
42
47
|
image_provider: Async provider for screenshots
|
|
43
48
|
last_task_id: Previous task ID to retrieve history from
|
|
44
49
|
history_steps: Number of historical steps to include
|
|
50
|
+
temperature: Sampling temperature for all steps (overrides task default if provided)
|
|
45
51
|
"""
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
)
|
|
52
|
+
self._log_auto_mode_start(task_desc, max_steps, prefix="async ")
|
|
53
|
+
|
|
49
54
|
await self.init_task(
|
|
50
55
|
task_desc,
|
|
51
56
|
max_steps=max_steps,
|
|
@@ -54,19 +59,15 @@ class AsyncShortTask(AsyncTask):
|
|
|
54
59
|
)
|
|
55
60
|
|
|
56
61
|
for i in range(max_steps):
|
|
57
|
-
|
|
62
|
+
self._log_auto_mode_step(i + 1, max_steps, prefix="async ")
|
|
58
63
|
image = await image_provider()
|
|
59
|
-
step = await self.step(image)
|
|
64
|
+
step = await self.step(image, temperature=temperature)
|
|
60
65
|
if executor:
|
|
61
|
-
|
|
66
|
+
self._log_auto_mode_actions(len(step.actions), prefix="async ")
|
|
62
67
|
await executor(step.actions)
|
|
63
68
|
if step.stop:
|
|
64
|
-
|
|
65
|
-
f"Async auto mode completed successfully after {i + 1} steps"
|
|
66
|
-
)
|
|
69
|
+
self._log_auto_mode_completion(i + 1, prefix="async ")
|
|
67
70
|
return True
|
|
68
71
|
|
|
69
|
-
|
|
70
|
-
f"Async auto mode reached max steps ({max_steps}) without completion"
|
|
71
|
-
)
|
|
72
|
+
self._log_auto_mode_max_steps(max_steps, prefix="async ")
|
|
72
73
|
return False
|
oagi/task/base.py
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
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
|
+
import base64
|
|
10
|
+
|
|
11
|
+
from ..logging import get_logger
|
|
12
|
+
from ..types import Image, Step
|
|
13
|
+
from ..types.models import LLMResponse
|
|
14
|
+
|
|
15
|
+
logger = get_logger("task.base")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def encode_screenshot_from_bytes(image_bytes: bytes) -> str:
|
|
19
|
+
return base64.b64encode(image_bytes).decode("utf-8")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def encode_screenshot_from_file(image_path: str) -> str:
|
|
23
|
+
with open(image_path, "rb") as f:
|
|
24
|
+
return encode_screenshot_from_bytes(f.read())
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class BaseTask:
|
|
28
|
+
"""Base class with shared task management logic for sync/async tasks."""
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
api_key: str | None,
|
|
33
|
+
base_url: str | None,
|
|
34
|
+
model: str,
|
|
35
|
+
temperature: float | None,
|
|
36
|
+
):
|
|
37
|
+
self.task_id: str | None = None
|
|
38
|
+
self.task_description: str | None = None
|
|
39
|
+
self.model = model
|
|
40
|
+
self.temperature = temperature
|
|
41
|
+
self.last_task_id: str | None = None
|
|
42
|
+
self.history_steps: int | None = None
|
|
43
|
+
# Client will be set by subclasses
|
|
44
|
+
self.api_key: str | None = None
|
|
45
|
+
self.base_url: str | None = None
|
|
46
|
+
|
|
47
|
+
def _prepare_init_task(
|
|
48
|
+
self,
|
|
49
|
+
task_desc: str,
|
|
50
|
+
last_task_id: str | None = None,
|
|
51
|
+
history_steps: int | None = None,
|
|
52
|
+
):
|
|
53
|
+
self.task_description = task_desc
|
|
54
|
+
self.last_task_id = last_task_id
|
|
55
|
+
self.history_steps = history_steps
|
|
56
|
+
|
|
57
|
+
def _process_init_response(
|
|
58
|
+
self, response: LLMResponse, task_desc: str, max_steps: int, prefix: str = ""
|
|
59
|
+
):
|
|
60
|
+
self.task_id = response.task_id # Reset task_id for new task
|
|
61
|
+
logger.info(f"{prefix}Task initialized: '{task_desc}' (max_steps: {max_steps})")
|
|
62
|
+
if self.last_task_id:
|
|
63
|
+
logger.info(
|
|
64
|
+
f"Will include {self.history_steps or 1} steps from previous task: {self.last_task_id}"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
def _validate_step_preconditions(self):
|
|
68
|
+
if not self.task_description:
|
|
69
|
+
raise ValueError("Task description must be set. Call init_task() first.")
|
|
70
|
+
|
|
71
|
+
def _prepare_screenshot(self, screenshot: Image | bytes) -> bytes:
|
|
72
|
+
if isinstance(screenshot, Image):
|
|
73
|
+
return screenshot.read()
|
|
74
|
+
return screenshot
|
|
75
|
+
|
|
76
|
+
def _get_temperature(self, temperature: float | None) -> float | None:
|
|
77
|
+
return temperature if temperature is not None else self.temperature
|
|
78
|
+
|
|
79
|
+
def _update_task_id(self, response: LLMResponse):
|
|
80
|
+
if self.task_id != response.task_id:
|
|
81
|
+
if self.task_id is None:
|
|
82
|
+
logger.debug(f"Task ID assigned: {response.task_id}")
|
|
83
|
+
else:
|
|
84
|
+
logger.debug(f"Task ID changed: {self.task_id} -> {response.task_id}")
|
|
85
|
+
self.task_id = response.task_id
|
|
86
|
+
|
|
87
|
+
def _build_step_response(self, response: LLMResponse, prefix: str = "") -> Step:
|
|
88
|
+
result = Step(
|
|
89
|
+
reason=response.reason,
|
|
90
|
+
actions=response.actions,
|
|
91
|
+
stop=response.is_complete,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
if response.is_complete:
|
|
95
|
+
logger.info(f"{prefix}Task completed after {response.current_step} steps")
|
|
96
|
+
else:
|
|
97
|
+
logger.debug(
|
|
98
|
+
f"{prefix}Step {response.current_step} completed with {len(response.actions)} actions"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
return result
|
|
102
|
+
|
|
103
|
+
def _log_step_execution(self, prefix: str = ""):
|
|
104
|
+
logger.debug(f"Executing {prefix}step for task: '{self.task_description}'")
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class BaseAutoMode:
|
|
108
|
+
"""Base class with shared auto_mode logic for ShortTask implementations."""
|
|
109
|
+
|
|
110
|
+
def _log_auto_mode_start(self, task_desc: str, max_steps: int, prefix: str = ""):
|
|
111
|
+
logger.info(
|
|
112
|
+
f"Starting {prefix}auto mode for task: '{task_desc}' (max_steps: {max_steps})"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
def _log_auto_mode_step(self, step_num: int, max_steps: int, prefix: str = ""):
|
|
116
|
+
logger.debug(f"{prefix.capitalize()}auto mode step {step_num}/{max_steps}")
|
|
117
|
+
|
|
118
|
+
def _log_auto_mode_actions(self, action_count: int, prefix: str = ""):
|
|
119
|
+
verb = "asynchronously" if "async" in prefix else ""
|
|
120
|
+
logger.debug(f"Executing {action_count} actions {verb}".strip())
|
|
121
|
+
|
|
122
|
+
def _log_auto_mode_completion(self, steps: int, prefix: str = ""):
|
|
123
|
+
logger.info(
|
|
124
|
+
f"{prefix.capitalize()}auto mode completed successfully after {steps} steps"
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
def _log_auto_mode_max_steps(self, max_steps: int, prefix: str = ""):
|
|
128
|
+
logger.warning(
|
|
129
|
+
f"{prefix.capitalize()}auto mode reached max steps ({max_steps}) without completion"
|
|
130
|
+
)
|
|
@@ -6,14 +6,15 @@
|
|
|
6
6
|
# Licensed under the MIT License.
|
|
7
7
|
# -----------------------------------------------------------------------------
|
|
8
8
|
|
|
9
|
-
from
|
|
10
|
-
from
|
|
11
|
-
from .
|
|
9
|
+
from ..logging import get_logger
|
|
10
|
+
from ..types import ActionHandler, ImageProvider
|
|
11
|
+
from .base import BaseAutoMode
|
|
12
|
+
from .sync import Task
|
|
12
13
|
|
|
13
14
|
logger = get_logger("short_task")
|
|
14
15
|
|
|
15
16
|
|
|
16
|
-
class ShortTask(Task):
|
|
17
|
+
class ShortTask(Task, BaseAutoMode):
|
|
17
18
|
"""Task implementation with automatic mode for short-duration tasks."""
|
|
18
19
|
|
|
19
20
|
def __init__(
|
|
@@ -21,8 +22,11 @@ class ShortTask(Task):
|
|
|
21
22
|
api_key: str | None = None,
|
|
22
23
|
base_url: str | None = None,
|
|
23
24
|
model: str = "vision-model-v1",
|
|
25
|
+
temperature: float | None = None,
|
|
24
26
|
):
|
|
25
|
-
super().__init__(
|
|
27
|
+
super().__init__(
|
|
28
|
+
api_key=api_key, base_url=base_url, model=model, temperature=temperature
|
|
29
|
+
)
|
|
26
30
|
|
|
27
31
|
def auto_mode(
|
|
28
32
|
self,
|
|
@@ -32,6 +36,7 @@ class ShortTask(Task):
|
|
|
32
36
|
image_provider: ImageProvider = None,
|
|
33
37
|
last_task_id: str | None = None,
|
|
34
38
|
history_steps: int | None = None,
|
|
39
|
+
temperature: float | None = None,
|
|
35
40
|
) -> bool:
|
|
36
41
|
"""Run the task in automatic mode with the provided executor and image provider.
|
|
37
42
|
|
|
@@ -42,10 +47,10 @@ class ShortTask(Task):
|
|
|
42
47
|
image_provider: Provider for screenshots
|
|
43
48
|
last_task_id: Previous task ID to retrieve history from
|
|
44
49
|
history_steps: Number of historical steps to include
|
|
50
|
+
temperature: Sampling temperature for all steps (overrides task default if provided)
|
|
45
51
|
"""
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
)
|
|
52
|
+
self._log_auto_mode_start(task_desc, max_steps)
|
|
53
|
+
|
|
49
54
|
self.init_task(
|
|
50
55
|
task_desc,
|
|
51
56
|
max_steps=max_steps,
|
|
@@ -54,15 +59,15 @@ class ShortTask(Task):
|
|
|
54
59
|
)
|
|
55
60
|
|
|
56
61
|
for i in range(max_steps):
|
|
57
|
-
|
|
62
|
+
self._log_auto_mode_step(i + 1, max_steps)
|
|
58
63
|
image = image_provider()
|
|
59
|
-
step = self.step(image)
|
|
64
|
+
step = self.step(image, temperature=temperature)
|
|
60
65
|
if executor:
|
|
61
|
-
|
|
66
|
+
self._log_auto_mode_actions(len(step.actions))
|
|
62
67
|
executor(step.actions)
|
|
63
68
|
if step.stop:
|
|
64
|
-
|
|
69
|
+
self._log_auto_mode_completion(i + 1)
|
|
65
70
|
return True
|
|
66
71
|
|
|
67
|
-
|
|
72
|
+
self._log_auto_mode_max_steps(max_steps)
|
|
68
73
|
return False
|
oagi/{task.py → task/sync.py}
RENAMED
|
@@ -6,14 +6,15 @@
|
|
|
6
6
|
# Licensed under the MIT License.
|
|
7
7
|
# -----------------------------------------------------------------------------
|
|
8
8
|
|
|
9
|
-
from
|
|
10
|
-
from
|
|
11
|
-
from
|
|
9
|
+
from ..client import SyncClient
|
|
10
|
+
from ..logging import get_logger
|
|
11
|
+
from ..types import Image, Step
|
|
12
|
+
from .base import BaseTask, encode_screenshot_from_bytes
|
|
12
13
|
|
|
13
14
|
logger = get_logger("task")
|
|
14
15
|
|
|
15
16
|
|
|
16
|
-
class Task:
|
|
17
|
+
class Task(BaseTask):
|
|
17
18
|
"""Base class for task automation with the OAGI API."""
|
|
18
19
|
|
|
19
20
|
def __init__(
|
|
@@ -21,15 +22,12 @@ class Task:
|
|
|
21
22
|
api_key: str | None = None,
|
|
22
23
|
base_url: str | None = None,
|
|
23
24
|
model: str = "vision-model-v1",
|
|
25
|
+
temperature: float | None = None,
|
|
24
26
|
):
|
|
27
|
+
super().__init__(api_key, base_url, model, temperature)
|
|
25
28
|
self.client = SyncClient(base_url=base_url, api_key=api_key)
|
|
26
29
|
self.api_key = self.client.api_key
|
|
27
30
|
self.base_url = self.client.base_url
|
|
28
|
-
self.task_id: str | None = None
|
|
29
|
-
self.task_description: str | None = None
|
|
30
|
-
self.model = model
|
|
31
|
-
self.last_task_id: str | None = None
|
|
32
|
-
self.history_steps: int | None = None
|
|
33
31
|
|
|
34
32
|
def init_task(
|
|
35
33
|
self,
|
|
@@ -46,45 +44,42 @@ class Task:
|
|
|
46
44
|
last_task_id: Previous task ID to retrieve history from
|
|
47
45
|
history_steps: Number of historical steps to include (default: 1)
|
|
48
46
|
"""
|
|
49
|
-
self.
|
|
50
|
-
self.last_task_id = last_task_id
|
|
51
|
-
self.history_steps = history_steps
|
|
47
|
+
self._prepare_init_task(task_desc, last_task_id, history_steps)
|
|
52
48
|
response = self.client.create_message(
|
|
53
49
|
model=self.model,
|
|
54
50
|
screenshot="",
|
|
55
51
|
task_description=self.task_description,
|
|
56
52
|
task_id=None,
|
|
57
53
|
)
|
|
58
|
-
self.
|
|
59
|
-
logger.info(f"Task initialized: '{task_desc}' (max_steps: {max_steps})")
|
|
60
|
-
if last_task_id:
|
|
61
|
-
logger.info(
|
|
62
|
-
f"Will include {history_steps or 1} steps from previous task: {last_task_id}"
|
|
63
|
-
)
|
|
54
|
+
self._process_init_response(response, task_desc, max_steps)
|
|
64
55
|
|
|
65
|
-
def step(
|
|
56
|
+
def step(
|
|
57
|
+
self,
|
|
58
|
+
screenshot: Image | bytes,
|
|
59
|
+
instruction: str | None = None,
|
|
60
|
+
temperature: float | None = None,
|
|
61
|
+
) -> Step:
|
|
66
62
|
"""Send screenshot to the server and get the next actions.
|
|
67
63
|
|
|
68
64
|
Args:
|
|
69
65
|
screenshot: Screenshot as Image object or raw bytes
|
|
70
66
|
instruction: Optional additional instruction for this step (only works with existing task_id)
|
|
67
|
+
temperature: Sampling temperature for this step (overrides task default if provided)
|
|
71
68
|
|
|
72
69
|
Returns:
|
|
73
70
|
Step: The actions and reasoning for this step
|
|
74
71
|
"""
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
logger.debug(f"Executing step for task: '{self.task_description}'")
|
|
72
|
+
self._validate_step_preconditions()
|
|
73
|
+
self._log_step_execution()
|
|
79
74
|
|
|
80
75
|
try:
|
|
81
76
|
# Convert Image to bytes using the protocol
|
|
82
|
-
|
|
83
|
-
screenshot_bytes = screenshot.read()
|
|
84
|
-
else:
|
|
85
|
-
screenshot_bytes = screenshot
|
|
77
|
+
screenshot_bytes = self._prepare_screenshot(screenshot)
|
|
86
78
|
screenshot_b64 = encode_screenshot_from_bytes(screenshot_bytes)
|
|
87
79
|
|
|
80
|
+
# Use provided temperature or fall back to task default
|
|
81
|
+
temp = self._get_temperature(temperature)
|
|
82
|
+
|
|
88
83
|
# Call API
|
|
89
84
|
response = self.client.create_message(
|
|
90
85
|
model=self.model,
|
|
@@ -94,33 +89,14 @@ class Task:
|
|
|
94
89
|
instruction=instruction,
|
|
95
90
|
last_task_id=self.last_task_id if self.task_id else None,
|
|
96
91
|
history_steps=self.history_steps if self.task_id else None,
|
|
92
|
+
temperature=temp,
|
|
97
93
|
)
|
|
98
94
|
|
|
99
95
|
# Update task_id from response
|
|
100
|
-
|
|
101
|
-
if self.task_id is None:
|
|
102
|
-
logger.debug(f"Task ID assigned: {response.task_id}")
|
|
103
|
-
else:
|
|
104
|
-
logger.debug(
|
|
105
|
-
f"Task ID changed: {self.task_id} -> {response.task_id}"
|
|
106
|
-
)
|
|
107
|
-
self.task_id = response.task_id
|
|
96
|
+
self._update_task_id(response)
|
|
108
97
|
|
|
109
98
|
# Convert API response to Step
|
|
110
|
-
|
|
111
|
-
reason=response.reason,
|
|
112
|
-
actions=response.actions,
|
|
113
|
-
stop=response.is_complete,
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
if response.is_complete:
|
|
117
|
-
logger.info(f"Task completed after {response.current_step} steps")
|
|
118
|
-
else:
|
|
119
|
-
logger.debug(
|
|
120
|
-
f"Step {response.current_step} completed with {len(response.actions)} actions"
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
return result
|
|
99
|
+
return self._build_step_response(response)
|
|
124
100
|
|
|
125
101
|
except Exception as e:
|
|
126
102
|
logger.error(f"Error during step execution: {e}")
|
oagi/types/models/__init__.py
CHANGED
|
@@ -7,7 +7,17 @@
|
|
|
7
7
|
# -----------------------------------------------------------------------------
|
|
8
8
|
|
|
9
9
|
from .action import Action, ActionType
|
|
10
|
+
from .client import ErrorDetail, ErrorResponse, LLMResponse, Usage
|
|
10
11
|
from .image_config import ImageConfig
|
|
11
12
|
from .step import Step
|
|
12
13
|
|
|
13
|
-
__all__ = [
|
|
14
|
+
__all__ = [
|
|
15
|
+
"Action",
|
|
16
|
+
"ActionType",
|
|
17
|
+
"ErrorDetail",
|
|
18
|
+
"ErrorResponse",
|
|
19
|
+
"ImageConfig",
|
|
20
|
+
"LLMResponse",
|
|
21
|
+
"Step",
|
|
22
|
+
"Usage",
|
|
23
|
+
]
|
|
@@ -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 pydantic import BaseModel
|
|
10
|
+
|
|
11
|
+
from .action import Action
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Usage(BaseModel):
|
|
15
|
+
prompt_tokens: int
|
|
16
|
+
completion_tokens: int
|
|
17
|
+
total_tokens: int
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ErrorDetail(BaseModel):
|
|
21
|
+
"""Detailed error information."""
|
|
22
|
+
|
|
23
|
+
code: str
|
|
24
|
+
message: str
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ErrorResponse(BaseModel):
|
|
28
|
+
"""Standard error response format."""
|
|
29
|
+
|
|
30
|
+
error: ErrorDetail | None
|
|
31
|
+
|
|
32
|
+
|
|
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
|
+
current_step: int
|
|
41
|
+
is_complete: bool
|
|
42
|
+
actions: list[Action]
|
|
43
|
+
reason: str | None = None
|
|
44
|
+
usage: Usage
|
|
45
|
+
error: ErrorDetail | None = None
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
oagi/__init__.py,sha256=Mr_V_lXIZa7n7jrxBMxXAWIaGVi7MblDfcrRsX4IrZI,2605
|
|
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
|
|
5
|
+
oagi/exceptions.py,sha256=VMwVS8ouE9nHhBpN3AZMYt5_U2kGcihWaTnBhoQLquo,1662
|
|
6
|
+
oagi/logging.py,sha256=CWe89mA5MKTipIvfrqSYkv2CAFNBSwHMDQMDkG_g64g,1350
|
|
7
|
+
oagi/pil_image.py,sha256=Zp7YNwyE_AT25ZEFsWKbzMxbO8JOQsJ1Espph5ye8k8,3804
|
|
8
|
+
oagi/pyautogui_action_handler.py,sha256=8IFbU4p907L4b3TV3Eeh0-c-pYL2lYw-_qf1r8TtPTw,9811
|
|
9
|
+
oagi/screenshot_maker.py,sha256=sVuW7jn-K4FmLhmYI-akdNI-UVcTeBzh9P1_qJhoq1s,1282
|
|
10
|
+
oagi/single_step.py,sha256=62Zip4Uql6E-ZIX6vAlrBoveKqrnABzGqHdLCzju4ag,3138
|
|
11
|
+
oagi/client/__init__.py,sha256=F9DShPUdb6vZYmN1fpM1VYzp4MWqUao_e_R1KYmM4Q4,410
|
|
12
|
+
oagi/client/async_.py,sha256=okeFWLqqaVSZF5AD4WZ2_4jVPeZ_SZefe9yrmkF_lX0,4940
|
|
13
|
+
oagi/client/base.py,sha256=qc2Le28MNpRFvjlO0U0z1rjzqCh9-6YZmO5uOoZO_u8,6538
|
|
14
|
+
oagi/client/sync.py,sha256=yCsgJble5rMAqJjfGqVUgjKt9fhNUmfgnqU6wzNWqH8,4898
|
|
15
|
+
oagi/task/__init__.py,sha256=kvB8ENQ3iII9Uzqa9SSIxBv5JFKv44j2T6J-_Qc8A-4,494
|
|
16
|
+
oagi/task/async_.py,sha256=JS_2ZW1gE8jZd1ZmwyHg8GB0OuiV0a19lIs9psb4Qc4,3996
|
|
17
|
+
oagi/task/async_short.py,sha256=gLPRIb3hekT8YnIy0Vo1EYRo1Pn65KdQLSP26g17gJU,2655
|
|
18
|
+
oagi/task/base.py,sha256=fm7G5e-NH29lII0tPwNjmP8OSwj0dnu_eOetc1lJgjw,4705
|
|
19
|
+
oagi/task/short.py,sha256=W3DOZs66a0YO8Yi5v72BnFIMRWjYkpq-WoNk4h_lYPw,2479
|
|
20
|
+
oagi/task/sync.py,sha256=Scsz0GUvqxZjFHxLLrl2swABAuGDMdST-XLLa34sjZA,3866
|
|
21
|
+
oagi/types/__init__.py,sha256=YXxL-30f92qAf9U6LZuVCtKFG-Pi3xahKedaNxyrxFE,766
|
|
22
|
+
oagi/types/action_handler.py,sha256=NH8E-m5qpGqWcXzTSWfF7W0Xdp8SkzJsbhCmQ0B96cg,1075
|
|
23
|
+
oagi/types/async_action_handler.py,sha256=k1AaqSkFcXlxwW8sn-w0WFHGsIqHFLbcOPrkknmSVug,1116
|
|
24
|
+
oagi/types/async_image_provider.py,sha256=wnhRyPtTmuALt45Qore74-RCkP5yxU9sZGjvOzFqzOk,1170
|
|
25
|
+
oagi/types/image.py,sha256=KgPCCTJ6D5vHIaGZdbTE7eQEa1WlT6G9tf59ZuUCV2U,537
|
|
26
|
+
oagi/types/image_provider.py,sha256=oYFdOYznrK_VOR9egzOjw5wFM5w8EY2sY01pH0ANAgU,1112
|
|
27
|
+
oagi/types/models/__init__.py,sha256=mpJpyko5EN_WUeh3eitI7ZerHlP50yKdAG7TqW7Gr24,625
|
|
28
|
+
oagi/types/models/action.py,sha256=hh6mRRSSWgrW4jpZo71zGMCOcZpV5_COu4148uG6G48,967
|
|
29
|
+
oagi/types/models/client.py,sha256=a6aFwoGG5tBNg3Qr_Dd_TYsg6CpKvA-4tU6b6dCQkoU,983
|
|
30
|
+
oagi/types/models/image_config.py,sha256=tl6abVg_-IAPLwpaWprgknXu7wRWriMg-AEVyUX73v0,1567
|
|
31
|
+
oagi/types/models/step.py,sha256=RSI4H_2rrUBq_xyCoWKaq7JHdJWNobtQppaKC1l0aWU,471
|
|
32
|
+
oagi-0.6.1.dist-info/METADATA,sha256=0Ia8gwi3rh-f1voZm31Kvn28wP5RPURbvSQWyS-O4EY,4799
|
|
33
|
+
oagi-0.6.1.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
34
|
+
oagi-0.6.1.dist-info/licenses/LICENSE,sha256=sy5DLA2M29jFT4UfWsuBF9BAr3FnRkYtnAu6oDZiIf8,1075
|
|
35
|
+
oagi-0.6.1.dist-info/RECORD,,
|