oagi-core 0.10.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 +148 -0
- oagi/agent/__init__.py +33 -0
- oagi/agent/default.py +124 -0
- oagi/agent/factories.py +74 -0
- oagi/agent/observer/__init__.py +38 -0
- oagi/agent/observer/agent_observer.py +99 -0
- oagi/agent/observer/events.py +28 -0
- oagi/agent/observer/exporters.py +445 -0
- oagi/agent/observer/protocol.py +12 -0
- oagi/agent/protocol.py +55 -0
- oagi/agent/registry.py +155 -0
- oagi/agent/tasker/__init__.py +33 -0
- oagi/agent/tasker/memory.py +160 -0
- oagi/agent/tasker/models.py +77 -0
- oagi/agent/tasker/planner.py +408 -0
- oagi/agent/tasker/taskee_agent.py +512 -0
- oagi/agent/tasker/tasker_agent.py +324 -0
- oagi/cli/__init__.py +11 -0
- oagi/cli/agent.py +281 -0
- oagi/cli/display.py +56 -0
- oagi/cli/main.py +77 -0
- oagi/cli/server.py +94 -0
- oagi/cli/tracking.py +55 -0
- oagi/cli/utils.py +89 -0
- oagi/client/__init__.py +12 -0
- oagi/client/async_.py +290 -0
- oagi/client/base.py +457 -0
- oagi/client/sync.py +293 -0
- oagi/exceptions.py +118 -0
- oagi/handler/__init__.py +24 -0
- oagi/handler/_macos.py +55 -0
- oagi/handler/async_pyautogui_action_handler.py +44 -0
- oagi/handler/async_screenshot_maker.py +47 -0
- oagi/handler/pil_image.py +102 -0
- oagi/handler/pyautogui_action_handler.py +291 -0
- oagi/handler/screenshot_maker.py +41 -0
- oagi/logging.py +55 -0
- oagi/server/__init__.py +13 -0
- oagi/server/agent_wrappers.py +98 -0
- oagi/server/config.py +46 -0
- oagi/server/main.py +157 -0
- oagi/server/models.py +98 -0
- oagi/server/session_store.py +116 -0
- oagi/server/socketio_server.py +405 -0
- oagi/task/__init__.py +21 -0
- oagi/task/async_.py +101 -0
- oagi/task/async_short.py +76 -0
- oagi/task/base.py +157 -0
- oagi/task/short.py +76 -0
- oagi/task/sync.py +99 -0
- oagi/types/__init__.py +50 -0
- oagi/types/action_handler.py +30 -0
- oagi/types/async_action_handler.py +30 -0
- oagi/types/async_image_provider.py +38 -0
- oagi/types/image.py +17 -0
- oagi/types/image_provider.py +35 -0
- oagi/types/models/__init__.py +32 -0
- oagi/types/models/action.py +33 -0
- oagi/types/models/client.py +68 -0
- oagi/types/models/image_config.py +47 -0
- oagi/types/models/step.py +17 -0
- oagi/types/step_observer.py +93 -0
- oagi/types/url.py +3 -0
- oagi_core-0.10.1.dist-info/METADATA +245 -0
- oagi_core-0.10.1.dist-info/RECORD +68 -0
- oagi_core-0.10.1.dist-info/WHEEL +4 -0
- oagi_core-0.10.1.dist-info/entry_points.txt +2 -0
- oagi_core-0.10.1.dist-info/licenses/LICENSE +21 -0
oagi/__init__.py
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
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
|
+
import importlib
|
|
9
|
+
from typing import TYPE_CHECKING
|
|
10
|
+
|
|
11
|
+
from oagi.client import AsyncClient, SyncClient
|
|
12
|
+
from oagi.exceptions import (
|
|
13
|
+
APIError,
|
|
14
|
+
AuthenticationError,
|
|
15
|
+
ConfigurationError,
|
|
16
|
+
NetworkError,
|
|
17
|
+
NotFoundError,
|
|
18
|
+
OAGIError,
|
|
19
|
+
RateLimitError,
|
|
20
|
+
RequestTimeoutError,
|
|
21
|
+
ServerError,
|
|
22
|
+
ValidationError,
|
|
23
|
+
check_optional_dependency,
|
|
24
|
+
)
|
|
25
|
+
from oagi.task import Actor, AsyncActor, AsyncShortTask, AsyncTask, ShortTask, Task
|
|
26
|
+
from oagi.types import ImageConfig
|
|
27
|
+
from oagi.types.models import (
|
|
28
|
+
ErrorDetail,
|
|
29
|
+
ErrorResponse,
|
|
30
|
+
GenerateResponse,
|
|
31
|
+
LLMResponse,
|
|
32
|
+
UploadFileResponse,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Lazy imports for optional dependency modules
|
|
36
|
+
# Format: name -> (module_path, package_to_check, extra_name)
|
|
37
|
+
# package_to_check is None if no optional dependency is required
|
|
38
|
+
_LAZY_IMPORTS_DATA: dict[str, tuple[str, str | None, str | None]] = {
|
|
39
|
+
# Desktop handlers (require pyautogui/PIL)
|
|
40
|
+
"AsyncPyautoguiActionHandler": (
|
|
41
|
+
"oagi.handler.async_pyautogui_action_handler",
|
|
42
|
+
"pyautogui",
|
|
43
|
+
"desktop",
|
|
44
|
+
),
|
|
45
|
+
"AsyncScreenshotMaker": ("oagi.handler.async_screenshot_maker", "PIL", "desktop"),
|
|
46
|
+
"PILImage": ("oagi.handler.pil_image", "PIL", "desktop"),
|
|
47
|
+
"PyautoguiActionHandler": (
|
|
48
|
+
"oagi.handler.pyautogui_action_handler",
|
|
49
|
+
"pyautogui",
|
|
50
|
+
"desktop",
|
|
51
|
+
),
|
|
52
|
+
"PyautoguiConfig": (
|
|
53
|
+
"oagi.handler.pyautogui_action_handler",
|
|
54
|
+
"pyautogui",
|
|
55
|
+
"desktop",
|
|
56
|
+
),
|
|
57
|
+
"ScreenshotMaker": ("oagi.handler.screenshot_maker", "PIL", "desktop"),
|
|
58
|
+
# Agent modules (lazy to avoid circular imports)
|
|
59
|
+
"AsyncDefaultAgent": ("oagi.agent.default", None, None),
|
|
60
|
+
"TaskerAgent": ("oagi.agent.tasker", None, None),
|
|
61
|
+
"AsyncAgentObserver": ("oagi.agent.observer.agent_observer", None, None),
|
|
62
|
+
# Server modules (require server dependencies)
|
|
63
|
+
"create_app": ("oagi.server.main", "socketio", "server"),
|
|
64
|
+
"ServerConfig": ("oagi.server.config", "pydantic_settings", "server"),
|
|
65
|
+
"sio": ("oagi.server.socketio_server", "socketio", "server"),
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if TYPE_CHECKING:
|
|
69
|
+
from oagi.agent.default import AsyncDefaultAgent
|
|
70
|
+
from oagi.agent.observer.agent_observer import AsyncAgentObserver
|
|
71
|
+
from oagi.agent.tasker import TaskerAgent
|
|
72
|
+
from oagi.handler.async_pyautogui_action_handler import AsyncPyautoguiActionHandler
|
|
73
|
+
from oagi.handler.async_screenshot_maker import AsyncScreenshotMaker
|
|
74
|
+
from oagi.handler.pil_image import PILImage
|
|
75
|
+
from oagi.handler.pyautogui_action_handler import (
|
|
76
|
+
PyautoguiActionHandler,
|
|
77
|
+
PyautoguiConfig,
|
|
78
|
+
)
|
|
79
|
+
from oagi.handler.screenshot_maker import ScreenshotMaker
|
|
80
|
+
from oagi.server.config import ServerConfig
|
|
81
|
+
from oagi.server.main import create_app
|
|
82
|
+
from oagi.server.socketio_server import sio
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def __getattr__(name: str):
|
|
86
|
+
"""Lazy import for optional dependency modules with helpful error messages."""
|
|
87
|
+
if name in _LAZY_IMPORTS_DATA:
|
|
88
|
+
module_path, package, extra = _LAZY_IMPORTS_DATA[name]
|
|
89
|
+
if package is not None:
|
|
90
|
+
check_optional_dependency(package, name, extra)
|
|
91
|
+
module = importlib.import_module(module_path)
|
|
92
|
+
return getattr(module, name)
|
|
93
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def __dir__() -> list[str]:
|
|
97
|
+
"""Return all public names including lazy imports."""
|
|
98
|
+
return sorted(set(globals().keys()) | set(_LAZY_IMPORTS_DATA.keys()))
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
__all__ = [
|
|
102
|
+
# Core sync classes
|
|
103
|
+
"Actor",
|
|
104
|
+
"AsyncActor",
|
|
105
|
+
"Task", # Deprecated: Use Actor instead
|
|
106
|
+
"ShortTask", # Deprecated
|
|
107
|
+
"SyncClient",
|
|
108
|
+
# Core async classes
|
|
109
|
+
"AsyncTask", # Deprecated: Use AsyncActor instead
|
|
110
|
+
"AsyncShortTask", # Deprecated
|
|
111
|
+
"AsyncClient",
|
|
112
|
+
# Agent classes
|
|
113
|
+
"AsyncDefaultAgent",
|
|
114
|
+
"TaskerAgent",
|
|
115
|
+
"AsyncAgentObserver",
|
|
116
|
+
# Configuration
|
|
117
|
+
"ImageConfig",
|
|
118
|
+
# Response models
|
|
119
|
+
"LLMResponse",
|
|
120
|
+
"GenerateResponse",
|
|
121
|
+
"UploadFileResponse",
|
|
122
|
+
"ErrorResponse",
|
|
123
|
+
"ErrorDetail",
|
|
124
|
+
# Exceptions
|
|
125
|
+
"OAGIError",
|
|
126
|
+
"APIError",
|
|
127
|
+
"AuthenticationError",
|
|
128
|
+
"ConfigurationError",
|
|
129
|
+
"NetworkError",
|
|
130
|
+
"NotFoundError",
|
|
131
|
+
"RateLimitError",
|
|
132
|
+
"ServerError",
|
|
133
|
+
"RequestTimeoutError",
|
|
134
|
+
"ValidationError",
|
|
135
|
+
# Lazy imports - Image classes
|
|
136
|
+
"PILImage",
|
|
137
|
+
# Lazy imports - Handler classes
|
|
138
|
+
"PyautoguiActionHandler",
|
|
139
|
+
"PyautoguiConfig",
|
|
140
|
+
"ScreenshotMaker",
|
|
141
|
+
# Lazy imports - Async handler classes
|
|
142
|
+
"AsyncPyautoguiActionHandler",
|
|
143
|
+
"AsyncScreenshotMaker",
|
|
144
|
+
# Lazy imports - Server modules (optional)
|
|
145
|
+
"create_app",
|
|
146
|
+
"ServerConfig",
|
|
147
|
+
"sio",
|
|
148
|
+
]
|
oagi/agent/__init__.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
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 factories to trigger registration
|
|
10
|
+
from . import factories # noqa: F401
|
|
11
|
+
from .default import AsyncDefaultAgent
|
|
12
|
+
from .observer import AsyncAgentObserver
|
|
13
|
+
from .protocol import Agent, AsyncAgent
|
|
14
|
+
from .registry import (
|
|
15
|
+
async_agent_register,
|
|
16
|
+
create_agent,
|
|
17
|
+
get_agent_factory,
|
|
18
|
+
list_agent_modes,
|
|
19
|
+
)
|
|
20
|
+
from .tasker import TaskeeAgent, TaskerAgent
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"Agent",
|
|
24
|
+
"AsyncAgent",
|
|
25
|
+
"AsyncAgentObserver",
|
|
26
|
+
"AsyncDefaultAgent",
|
|
27
|
+
"TaskerAgent",
|
|
28
|
+
"TaskeeAgent",
|
|
29
|
+
"async_agent_register",
|
|
30
|
+
"create_agent",
|
|
31
|
+
"get_agent_factory",
|
|
32
|
+
"list_agent_modes",
|
|
33
|
+
]
|
oagi/agent/default.py
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
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 logging
|
|
10
|
+
|
|
11
|
+
from .. import AsyncActor
|
|
12
|
+
from ..types import (
|
|
13
|
+
ActionEvent,
|
|
14
|
+
AsyncActionHandler,
|
|
15
|
+
AsyncImageProvider,
|
|
16
|
+
AsyncObserver,
|
|
17
|
+
Image,
|
|
18
|
+
StepEvent,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _serialize_image(image: Image | str) -> bytes | str:
|
|
25
|
+
"""Convert an image to bytes or keep URL as string."""
|
|
26
|
+
if isinstance(image, str):
|
|
27
|
+
return image
|
|
28
|
+
return image.read()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class AsyncDefaultAgent:
|
|
32
|
+
"""Default asynchronous agent implementation using OAGI client."""
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
api_key: str | None = None,
|
|
37
|
+
base_url: str | None = None,
|
|
38
|
+
model: str = "lux-actor-1",
|
|
39
|
+
max_steps: int = 20,
|
|
40
|
+
temperature: float | None = 0.5,
|
|
41
|
+
step_observer: AsyncObserver | None = None,
|
|
42
|
+
):
|
|
43
|
+
self.api_key = api_key
|
|
44
|
+
self.base_url = base_url
|
|
45
|
+
self.model = model
|
|
46
|
+
self.max_steps = max_steps
|
|
47
|
+
self.temperature = temperature
|
|
48
|
+
self.step_observer = step_observer
|
|
49
|
+
|
|
50
|
+
async def execute(
|
|
51
|
+
self,
|
|
52
|
+
instruction: str,
|
|
53
|
+
action_handler: AsyncActionHandler,
|
|
54
|
+
image_provider: AsyncImageProvider,
|
|
55
|
+
) -> bool:
|
|
56
|
+
async with AsyncActor(
|
|
57
|
+
api_key=self.api_key, base_url=self.base_url, model=self.model
|
|
58
|
+
) as self.actor:
|
|
59
|
+
logger.info(f"Starting async task execution: {instruction}")
|
|
60
|
+
await self.actor.init_task(instruction, max_steps=self.max_steps)
|
|
61
|
+
|
|
62
|
+
for i in range(self.max_steps):
|
|
63
|
+
step_num = i + 1
|
|
64
|
+
logger.debug(f"Executing step {step_num}/{self.max_steps}")
|
|
65
|
+
|
|
66
|
+
# Capture current state
|
|
67
|
+
image = await image_provider()
|
|
68
|
+
|
|
69
|
+
# Get next step from OAGI
|
|
70
|
+
step = await self.actor.step(image, temperature=self.temperature)
|
|
71
|
+
|
|
72
|
+
# Log reasoning
|
|
73
|
+
if step.reason:
|
|
74
|
+
logger.info(f"Step {step_num}: {step.reason}")
|
|
75
|
+
|
|
76
|
+
# Emit step event
|
|
77
|
+
if self.step_observer:
|
|
78
|
+
await self.step_observer.on_event(
|
|
79
|
+
StepEvent(
|
|
80
|
+
step_num=step_num,
|
|
81
|
+
image=_serialize_image(image),
|
|
82
|
+
step=step,
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Execute actions if any
|
|
87
|
+
if step.actions:
|
|
88
|
+
logger.info(f"Actions ({len(step.actions)}):")
|
|
89
|
+
for action in step.actions:
|
|
90
|
+
count_suffix = (
|
|
91
|
+
f" x{action.count}"
|
|
92
|
+
if action.count and action.count > 1
|
|
93
|
+
else ""
|
|
94
|
+
)
|
|
95
|
+
logger.info(
|
|
96
|
+
f" [{action.type.value}] {action.argument}{count_suffix}"
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
error = None
|
|
100
|
+
try:
|
|
101
|
+
await action_handler(step.actions)
|
|
102
|
+
except Exception as e:
|
|
103
|
+
error = str(e)
|
|
104
|
+
raise
|
|
105
|
+
|
|
106
|
+
# Emit action event
|
|
107
|
+
if self.step_observer:
|
|
108
|
+
await self.step_observer.on_event(
|
|
109
|
+
ActionEvent(
|
|
110
|
+
step_num=step_num,
|
|
111
|
+
actions=step.actions,
|
|
112
|
+
error=error,
|
|
113
|
+
)
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Check if task is complete
|
|
117
|
+
if step.stop:
|
|
118
|
+
logger.info(f"Task completed successfully after {step_num} steps")
|
|
119
|
+
return True
|
|
120
|
+
|
|
121
|
+
logger.warning(
|
|
122
|
+
f"Task reached max steps ({self.max_steps}) without completion"
|
|
123
|
+
)
|
|
124
|
+
return False
|
oagi/agent/factories.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
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.agent.tasker import TaskerAgent
|
|
9
|
+
from oagi.types import AsyncStepObserver
|
|
10
|
+
|
|
11
|
+
from .default import AsyncDefaultAgent
|
|
12
|
+
from .protocol import AsyncAgent
|
|
13
|
+
from .registry import async_agent_register
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@async_agent_register(mode="actor")
|
|
17
|
+
def create_default_agent(
|
|
18
|
+
api_key: str | None = None,
|
|
19
|
+
base_url: str | None = None,
|
|
20
|
+
model: str = "lux-actor-1",
|
|
21
|
+
max_steps: int = 20,
|
|
22
|
+
temperature: float = 0.1,
|
|
23
|
+
step_observer: AsyncStepObserver | None = None,
|
|
24
|
+
) -> AsyncAgent:
|
|
25
|
+
return AsyncDefaultAgent(
|
|
26
|
+
api_key=api_key,
|
|
27
|
+
base_url=base_url,
|
|
28
|
+
model=model,
|
|
29
|
+
max_steps=max_steps,
|
|
30
|
+
temperature=temperature,
|
|
31
|
+
step_observer=step_observer,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@async_agent_register(mode="thinker")
|
|
36
|
+
def create_thinker_agent(
|
|
37
|
+
api_key: str | None = None,
|
|
38
|
+
base_url: str | None = None,
|
|
39
|
+
model: str = "lux-thinker-1",
|
|
40
|
+
max_steps: int = 100,
|
|
41
|
+
temperature: float = 0.1,
|
|
42
|
+
step_observer: AsyncStepObserver | None = None,
|
|
43
|
+
) -> AsyncAgent:
|
|
44
|
+
return AsyncDefaultAgent(
|
|
45
|
+
api_key=api_key,
|
|
46
|
+
base_url=base_url,
|
|
47
|
+
model=model,
|
|
48
|
+
max_steps=max_steps,
|
|
49
|
+
temperature=temperature,
|
|
50
|
+
step_observer=step_observer,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@async_agent_register(mode="tasker")
|
|
55
|
+
def create_planner_agent(
|
|
56
|
+
api_key: str | None = None,
|
|
57
|
+
base_url: str | None = None,
|
|
58
|
+
model: str = "lux-actor-1",
|
|
59
|
+
max_steps: int = 30,
|
|
60
|
+
temperature: float = 0.1,
|
|
61
|
+
reflection_interval: int = 20,
|
|
62
|
+
step_observer: AsyncStepObserver | None = None,
|
|
63
|
+
) -> AsyncAgent:
|
|
64
|
+
tasker = TaskerAgent(
|
|
65
|
+
api_key=api_key,
|
|
66
|
+
base_url=base_url,
|
|
67
|
+
model=model,
|
|
68
|
+
max_steps=max_steps,
|
|
69
|
+
temperature=temperature,
|
|
70
|
+
reflection_interval=reflection_interval,
|
|
71
|
+
step_observer=step_observer,
|
|
72
|
+
)
|
|
73
|
+
# tasker.set_task()
|
|
74
|
+
return tasker
|
|
@@ -0,0 +1,38 @@
|
|
|
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 ...types import (
|
|
10
|
+
ActionEvent,
|
|
11
|
+
AsyncObserver,
|
|
12
|
+
BaseEvent,
|
|
13
|
+
ImageEvent,
|
|
14
|
+
LogEvent,
|
|
15
|
+
ObserverEvent,
|
|
16
|
+
PlanEvent,
|
|
17
|
+
SplitEvent,
|
|
18
|
+
StepEvent,
|
|
19
|
+
)
|
|
20
|
+
from .agent_observer import AsyncAgentObserver, ExportFormat
|
|
21
|
+
from .exporters import export_to_html, export_to_json, export_to_markdown
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"ActionEvent",
|
|
25
|
+
"AsyncAgentObserver",
|
|
26
|
+
"AsyncObserver",
|
|
27
|
+
"BaseEvent",
|
|
28
|
+
"ExportFormat",
|
|
29
|
+
"ImageEvent",
|
|
30
|
+
"LogEvent",
|
|
31
|
+
"ObserverEvent",
|
|
32
|
+
"PlanEvent",
|
|
33
|
+
"SplitEvent",
|
|
34
|
+
"StepEvent",
|
|
35
|
+
"export_to_html",
|
|
36
|
+
"export_to_json",
|
|
37
|
+
"export_to_markdown",
|
|
38
|
+
]
|
|
@@ -0,0 +1,99 @@
|
|
|
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 enum import Enum
|
|
10
|
+
|
|
11
|
+
from ...types import LogEvent, ObserverEvent, SplitEvent
|
|
12
|
+
from .exporters import export_to_html, export_to_json, export_to_markdown
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ExportFormat(str, Enum):
|
|
16
|
+
"""Supported export formats."""
|
|
17
|
+
|
|
18
|
+
MARKDOWN = "markdown"
|
|
19
|
+
HTML = "html"
|
|
20
|
+
JSON = "json"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class AsyncAgentObserver:
|
|
24
|
+
"""Records agent execution events and exports to various formats.
|
|
25
|
+
|
|
26
|
+
This class implements the AsyncObserver protocol and provides
|
|
27
|
+
functionality for recording events during agent execution and
|
|
28
|
+
exporting them to Markdown or HTML formats.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(self) -> None:
|
|
32
|
+
self.events: list[ObserverEvent] = []
|
|
33
|
+
|
|
34
|
+
async def on_event(self, event: ObserverEvent) -> None:
|
|
35
|
+
"""Record an event.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
event: The event to record.
|
|
39
|
+
"""
|
|
40
|
+
self.events.append(event)
|
|
41
|
+
|
|
42
|
+
def add_log(self, message: str) -> None:
|
|
43
|
+
"""Add a custom log message.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
message: The log message to add.
|
|
47
|
+
"""
|
|
48
|
+
self.events.append(LogEvent(message=message))
|
|
49
|
+
|
|
50
|
+
def add_split(self, label: str = "") -> None:
|
|
51
|
+
"""Add a visual separator.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
label: Optional label for the separator.
|
|
55
|
+
"""
|
|
56
|
+
self.events.append(SplitEvent(label=label))
|
|
57
|
+
|
|
58
|
+
def clear(self) -> None:
|
|
59
|
+
"""Clear all recorded events."""
|
|
60
|
+
self.events.clear()
|
|
61
|
+
|
|
62
|
+
def get_events_by_step(self, step_num: int) -> list[ObserverEvent]:
|
|
63
|
+
"""Get all events for a specific step.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
step_num: The step number to filter by.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
List of events for the specified step.
|
|
70
|
+
"""
|
|
71
|
+
return [
|
|
72
|
+
event
|
|
73
|
+
for event in self.events
|
|
74
|
+
if hasattr(event, "step_num") and event.step_num == step_num
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
def export(
|
|
78
|
+
self,
|
|
79
|
+
format: ExportFormat | str,
|
|
80
|
+
path: str,
|
|
81
|
+
images_dir: str | None = None,
|
|
82
|
+
) -> None:
|
|
83
|
+
"""Export recorded events to a file.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
format: Export format (markdown, html, json)
|
|
87
|
+
path: Path to the output file.
|
|
88
|
+
images_dir: Directory to save images (markdown only).
|
|
89
|
+
"""
|
|
90
|
+
if isinstance(format, str):
|
|
91
|
+
format = ExportFormat(format.lower())
|
|
92
|
+
|
|
93
|
+
match format:
|
|
94
|
+
case ExportFormat.MARKDOWN:
|
|
95
|
+
export_to_markdown(self.events, path, images_dir)
|
|
96
|
+
case ExportFormat.HTML:
|
|
97
|
+
export_to_html(self.events, path)
|
|
98
|
+
case ExportFormat.JSON:
|
|
99
|
+
export_to_json(self.events, path)
|
|
@@ -0,0 +1,28 @@
|
|
|
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
|
+
# Re-export from types for convenience
|
|
10
|
+
from ...types import (
|
|
11
|
+
ActionEvent,
|
|
12
|
+
BaseEvent,
|
|
13
|
+
ImageEvent,
|
|
14
|
+
LogEvent,
|
|
15
|
+
ObserverEvent,
|
|
16
|
+
SplitEvent,
|
|
17
|
+
StepEvent,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"ActionEvent",
|
|
22
|
+
"BaseEvent",
|
|
23
|
+
"ImageEvent",
|
|
24
|
+
"LogEvent",
|
|
25
|
+
"ObserverEvent",
|
|
26
|
+
"SplitEvent",
|
|
27
|
+
"StepEvent",
|
|
28
|
+
]
|