oagi-core 0.10.0__tar.gz → 0.10.2__tar.gz
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_core-0.10.0 → oagi_core-0.10.2}/PKG-INFO +2 -1
- {oagi_core-0.10.0 → oagi_core-0.10.2}/examples/tasker_agent_example.py +1 -7
- {oagi_core-0.10.0 → oagi_core-0.10.2}/metapackage/pyproject.toml +2 -2
- {oagi_core-0.10.0 → oagi_core-0.10.2}/metapackage/uv.lock +47 -5
- {oagi_core-0.10.0 → oagi_core-0.10.2}/pyproject.toml +2 -1
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/default.py +7 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/factories.py +6 -0
- oagi_core-0.10.2/src/oagi/agent/observer/exporters.py +336 -0
- oagi_core-0.10.2/src/oagi/agent/observer/report_template.html +455 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/tasker/__init__.py +0 -2
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/tasker/memory.py +3 -27
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/tasker/models.py +0 -7
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/tasker/planner.py +2 -11
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/tasker/taskee_agent.py +8 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/tasker/tasker_agent.py +7 -17
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/cli/agent.py +108 -1
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/client/async_.py +0 -3
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/client/base.py +0 -4
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/client/sync.py +0 -3
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/handler/pyautogui_action_handler.py +20 -24
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/server/socketio_server.py +20 -19
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/types/__init__.py +12 -1
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/types/models/__init__.py +10 -1
- oagi_core-0.10.2/src/oagi/types/models/action.py +84 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/types/models/client.py +7 -3
- oagi_core-0.10.2/tests/test_action_parsing.py +98 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_observer.py +12 -9
- {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_planner.py +5 -6
- {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_planner_memory.py +0 -13
- {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_taskee_agent.py +5 -5
- {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_tasker_agent.py +10 -23
- {oagi_core-0.10.0 → oagi_core-0.10.2}/uv.lock +71 -28
- oagi_core-0.10.0/src/oagi/agent/observer/exporters.py +0 -445
- oagi_core-0.10.0/src/oagi/types/models/action.py +0 -33
- {oagi_core-0.10.0 → oagi_core-0.10.2}/.github/workflows/ci.yml +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/.github/workflows/release.yml +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/.gitignore +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/.python-version +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/CONTRIBUTING.md +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/LICENSE +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/Makefile +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/README.md +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/examples/async_google_weather.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/examples/execute_task_auto.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/examples/execute_task_manual.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/examples/google_weather.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/examples/screenshot_with_config.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/__init__.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/__init__.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/observer/__init__.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/observer/agent_observer.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/observer/events.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/observer/protocol.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/protocol.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/registry.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/cli/__init__.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/cli/display.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/cli/main.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/cli/server.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/cli/tracking.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/cli/utils.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/client/__init__.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/exceptions.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/handler/__init__.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/handler/_macos.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/handler/async_pyautogui_action_handler.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/handler/async_screenshot_maker.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/handler/pil_image.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/handler/screenshot_maker.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/logging.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/server/__init__.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/server/agent_wrappers.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/server/config.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/server/main.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/server/models.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/server/session_store.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/task/__init__.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/task/async_.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/task/async_short.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/task/base.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/task/short.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/task/sync.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/types/action_handler.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/types/async_action_handler.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/types/async_image_provider.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/types/image.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/types/image_provider.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/types/models/image_config.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/types/models/step.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/types/step_observer.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/types/url.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/__init__.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/conftest.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_actor.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_agent/test_agent_wrappers.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_agent/test_default_agent.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_agent_registry.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_async_actor.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_async_client.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_async_handlers.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_cli.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_logging.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_mac_double_click.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_pil_image.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_pyautogui_action_handler.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_screenshot_maker.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_server/__init__.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_server/test_config.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_server/test_session_store.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_server/test_socketio_integration.py +0 -0
- {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_sync_client.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: oagi-core
|
|
3
|
-
Version: 0.10.
|
|
3
|
+
Version: 0.10.2
|
|
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>
|
|
@@ -32,6 +32,7 @@ Requires-Dist: rich>=13.0.0
|
|
|
32
32
|
Provides-Extra: desktop
|
|
33
33
|
Requires-Dist: pillow>=11.3.0; extra == 'desktop'
|
|
34
34
|
Requires-Dist: pyautogui>=0.9.54; extra == 'desktop'
|
|
35
|
+
Requires-Dist: pyobjc-framework-applicationservices>=9.0; (sys_platform == 'darwin') and extra == 'desktop'
|
|
35
36
|
Requires-Dist: pyobjc-framework-quartz>=9.0; (sys_platform == 'darwin') and extra == 'desktop'
|
|
36
37
|
Provides-Extra: server
|
|
37
38
|
Requires-Dist: fastapi[standard]>=0.115.0; extra == 'server'
|
|
@@ -27,7 +27,7 @@ async def main():
|
|
|
27
27
|
tasker = TaskerAgent(
|
|
28
28
|
api_key=os.getenv("OAGI_API_KEY"),
|
|
29
29
|
base_url=os.getenv("OAGI_BASE_URL", "https://api.agiopen.org"),
|
|
30
|
-
model="
|
|
30
|
+
model="sft-bigs1-1027-s2-1113-mixoc-1107-32b",
|
|
31
31
|
max_steps=30,
|
|
32
32
|
temperature=0.5,
|
|
33
33
|
step_observer=observer,
|
|
@@ -42,16 +42,10 @@ async def main():
|
|
|
42
42
|
"Click on the official Python.org website link",
|
|
43
43
|
]
|
|
44
44
|
|
|
45
|
-
# Define deliverables to achieve
|
|
46
|
-
deliverables = [
|
|
47
|
-
"Python.org website is opened",
|
|
48
|
-
]
|
|
49
|
-
|
|
50
45
|
# Set the task
|
|
51
46
|
tasker.set_task(
|
|
52
47
|
task=task_description,
|
|
53
48
|
todos=todos,
|
|
54
|
-
deliverables=deliverables,
|
|
55
49
|
)
|
|
56
50
|
|
|
57
51
|
image_provider = AsyncScreenshotMaker()
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "oagi"
|
|
7
|
-
version = "0.10.
|
|
7
|
+
version = "0.10.2"
|
|
8
8
|
description = "Official API of OpenAGI Foundation (metapackage with all features)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -16,7 +16,7 @@ authors = [
|
|
|
16
16
|
requires-python = ">= 3.10"
|
|
17
17
|
|
|
18
18
|
dependencies = [
|
|
19
|
-
"oagi-core[desktop,server]==0.10.
|
|
19
|
+
"oagi-core[desktop,server]==0.10.2",
|
|
20
20
|
]
|
|
21
21
|
|
|
22
22
|
[project.urls]
|
|
@@ -397,33 +397,34 @@ sdist = { url = "https://files.pythonhosted.org/packages/28/fa/b2ba8229b9381e8f6
|
|
|
397
397
|
|
|
398
398
|
[[package]]
|
|
399
399
|
name = "oagi"
|
|
400
|
-
version = "0.10.
|
|
400
|
+
version = "0.10.2"
|
|
401
401
|
source = { editable = "." }
|
|
402
402
|
dependencies = [
|
|
403
403
|
{ name = "oagi-core", extra = ["desktop", "server"] },
|
|
404
404
|
]
|
|
405
405
|
|
|
406
406
|
[package.metadata]
|
|
407
|
-
requires-dist = [{ name = "oagi-core", extras = ["desktop", "server"], specifier = "==0.
|
|
407
|
+
requires-dist = [{ name = "oagi-core", extras = ["desktop", "server"], specifier = "==0.10.1" }]
|
|
408
408
|
|
|
409
409
|
[[package]]
|
|
410
410
|
name = "oagi-core"
|
|
411
|
-
version = "0.
|
|
411
|
+
version = "0.10.1"
|
|
412
412
|
source = { registry = "https://pypi.org/simple" }
|
|
413
413
|
dependencies = [
|
|
414
414
|
{ name = "httpx" },
|
|
415
415
|
{ name = "pydantic" },
|
|
416
416
|
{ name = "rich" },
|
|
417
417
|
]
|
|
418
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
418
|
+
sdist = { url = "https://files.pythonhosted.org/packages/9c/2d/83e5e2efe2591858e2aceaed4329d995c78cc9602b6a9fd9331096559351/oagi_core-0.10.1.tar.gz", hash = "sha256:264b88fb5b1e24f7284af3e82243d846817f6b1d0be108280491383e671e5b8d", size = 261938, upload-time = "2025-11-25T13:19:31.714Z" }
|
|
419
419
|
wheels = [
|
|
420
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
420
|
+
{ url = "https://files.pythonhosted.org/packages/54/bb/3242e763f93360eeb6a5a5b1e61845cd3e27a485c442aeb9a918f6e4f3f6/oagi_core-0.10.1-py3-none-any.whl", hash = "sha256:5438e4310fab1aee3ffbe3d43963f04fc10d3747d24897b89f34df850bb76f89", size = 83830, upload-time = "2025-11-25T13:19:30.237Z" },
|
|
421
421
|
]
|
|
422
422
|
|
|
423
423
|
[package.optional-dependencies]
|
|
424
424
|
desktop = [
|
|
425
425
|
{ name = "pillow" },
|
|
426
426
|
{ name = "pyautogui" },
|
|
427
|
+
{ name = "pyobjc-framework-applicationservices", marker = "sys_platform == 'darwin'" },
|
|
427
428
|
{ name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
|
|
428
429
|
]
|
|
429
430
|
server = [
|
|
@@ -741,6 +742,27 @@ wheels = [
|
|
|
741
742
|
{ url = "https://files.pythonhosted.org/packages/62/50/dc076965c96c7f0de25c0a32b7f8aa98133ed244deaeeacfc758783f1f30/pyobjc_core-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:453b191df1a4b80e756445b935491b974714456ae2cbae816840bd96f86db882", size = 712204, upload-time = "2025-11-14T09:35:24.148Z" },
|
|
742
743
|
]
|
|
743
744
|
|
|
745
|
+
[[package]]
|
|
746
|
+
name = "pyobjc-framework-applicationservices"
|
|
747
|
+
version = "12.1"
|
|
748
|
+
source = { registry = "https://pypi.org/simple" }
|
|
749
|
+
dependencies = [
|
|
750
|
+
{ name = "pyobjc-core" },
|
|
751
|
+
{ name = "pyobjc-framework-cocoa" },
|
|
752
|
+
{ name = "pyobjc-framework-coretext" },
|
|
753
|
+
{ name = "pyobjc-framework-quartz" },
|
|
754
|
+
]
|
|
755
|
+
sdist = { url = "https://files.pythonhosted.org/packages/be/6a/d4e613c8e926a5744fc47a9e9fea08384a510dc4f27d844f7ad7a2d793bd/pyobjc_framework_applicationservices-12.1.tar.gz", hash = "sha256:c06abb74f119bc27aeb41bf1aef8102c0ae1288aec1ac8665ea186a067a8945b", size = 103247, upload-time = "2025-11-14T10:08:52.18Z" }
|
|
756
|
+
wheels = [
|
|
757
|
+
{ url = "https://files.pythonhosted.org/packages/52/9d/3cf36e7b08832e71f5d48ddfa1047865cf2dfc53df8c0f2a82843ea9507a/pyobjc_framework_applicationservices-12.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c4fd1b008757182b9e2603a63c6ffa930cc412fab47294ec64260ab3f8ec695d", size = 32791, upload-time = "2025-11-14T09:36:05.576Z" },
|
|
758
|
+
{ url = "https://files.pythonhosted.org/packages/17/86/d07eff705ff909a0ffa96d14fc14026e9fc9dd716233648c53dfd5056b8e/pyobjc_framework_applicationservices-12.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bdddd492eeac6d14ff2f5bd342aba29e30dffa72a2d358c08444da22129890e2", size = 32784, upload-time = "2025-11-14T09:36:08.755Z" },
|
|
759
|
+
{ url = "https://files.pythonhosted.org/packages/37/a7/55fa88def5c02732c4b747606ff1cbce6e1f890734bbd00f5596b21eaa02/pyobjc_framework_applicationservices-12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c8f6e2fb3b3e9214ab4864ef04eee18f592b46a986c86ea0113448b310520532", size = 32835, upload-time = "2025-11-14T09:36:11.855Z" },
|
|
760
|
+
{ url = "https://files.pythonhosted.org/packages/fc/21/79e42ee836f1010f5fe9e97d2817a006736bd287c15a3674c399190a2e77/pyobjc_framework_applicationservices-12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bd1f4dbb38234a24ae6819f5e22485cf7dd3dd4074ff3bf9a9fdb4c01a3b4a38", size = 32859, upload-time = "2025-11-14T09:36:15.208Z" },
|
|
761
|
+
{ url = "https://files.pythonhosted.org/packages/66/3a/0f1d4dcf2345e875e5ea9761d5a70969e241d24089133d21f008dde596f5/pyobjc_framework_applicationservices-12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:8a5d2845249b6a85ba9e320a9848468c3f8cd6f59605a9a43f406a7810eaa830", size = 33115, upload-time = "2025-11-14T09:36:18.384Z" },
|
|
762
|
+
{ url = "https://files.pythonhosted.org/packages/40/44/3196b40fec68b4413c92875311f17ccf4c3ff7d2e53676f8fc18ad29bd18/pyobjc_framework_applicationservices-12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:f43c9a24ad97a9121276d4d571aa04a924282c80d7291cfb3b29839c3e2013a8", size = 32997, upload-time = "2025-11-14T09:36:21.58Z" },
|
|
763
|
+
{ url = "https://files.pythonhosted.org/packages/fd/bb/dab21d2210d3ef7dd0616df7e8ea89b5d8d62444133a25f76e649a947168/pyobjc_framework_applicationservices-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:1f72e20009a4ebfd5ed5b23dc11c1528ad6b55cc63ee71952ddb2a5e5f1cb7da", size = 33238, upload-time = "2025-11-14T09:36:24.751Z" },
|
|
764
|
+
]
|
|
765
|
+
|
|
744
766
|
[[package]]
|
|
745
767
|
name = "pyobjc-framework-cocoa"
|
|
746
768
|
version = "12.1"
|
|
@@ -759,6 +781,26 @@ wheels = [
|
|
|
759
781
|
{ url = "https://files.pythonhosted.org/packages/58/27/b457b7b37089cad692c8aada90119162dfb4c4a16f513b79a8b2b022b33b/pyobjc_framework_cocoa-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:6ba1dc1bfa4da42d04e93d2363491275fb2e2be5c20790e561c8a9e09b8cf2cc", size = 388970, upload-time = "2025-11-14T09:42:53.964Z" },
|
|
760
782
|
]
|
|
761
783
|
|
|
784
|
+
[[package]]
|
|
785
|
+
name = "pyobjc-framework-coretext"
|
|
786
|
+
version = "12.1"
|
|
787
|
+
source = { registry = "https://pypi.org/simple" }
|
|
788
|
+
dependencies = [
|
|
789
|
+
{ name = "pyobjc-core" },
|
|
790
|
+
{ name = "pyobjc-framework-cocoa" },
|
|
791
|
+
{ name = "pyobjc-framework-quartz" },
|
|
792
|
+
]
|
|
793
|
+
sdist = { url = "https://files.pythonhosted.org/packages/29/da/682c9c92a39f713bd3c56e7375fa8f1b10ad558ecb075258ab6f1cdd4a6d/pyobjc_framework_coretext-12.1.tar.gz", hash = "sha256:e0adb717738fae395dc645c9e8a10bb5f6a4277e73cba8fa2a57f3b518e71da5", size = 90124, upload-time = "2025-11-14T10:14:38.596Z" }
|
|
794
|
+
wheels = [
|
|
795
|
+
{ url = "https://files.pythonhosted.org/packages/27/1c/ddecc72a672d681476c668bcedcfb8ade16383c028eac566ac7458fb91ef/pyobjc_framework_coretext-12.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1c8315dcef6699c2953461d97117fe81402f7c29cff36d2950dacce028a362fd", size = 29987, upload-time = "2025-11-14T09:46:58.028Z" },
|
|
796
|
+
{ url = "https://files.pythonhosted.org/packages/f0/81/7b8efc41e743adfa2d74b92dec263c91bcebfb188d2a8f5eea1886a195ff/pyobjc_framework_coretext-12.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4f6742ba5b0bb7629c345e99eff928fbfd9e9d3d667421ac1a2a43bdb7ba9833", size = 29990, upload-time = "2025-11-14T09:47:01.206Z" },
|
|
797
|
+
{ url = "https://files.pythonhosted.org/packages/cd/0f/ddf45bf0e3ba4fbdc7772de4728fd97ffc34a0b5a15e1ab1115b202fe4ae/pyobjc_framework_coretext-12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d246fa654bdbf43bae3969887d58f0b336c29b795ad55a54eb76397d0e62b93c", size = 30108, upload-time = "2025-11-14T09:47:04.228Z" },
|
|
798
|
+
{ url = "https://files.pythonhosted.org/packages/20/a2/a3974e3e807c68e23a9d7db66fc38ac54f7ecd2b7a9237042006699a76e1/pyobjc_framework_coretext-12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7cbb2c28580e6704ce10b9a991ccd9563a22b3a75f67c36cf612544bd8b21b5f", size = 30110, upload-time = "2025-11-14T09:47:07.518Z" },
|
|
799
|
+
{ url = "https://files.pythonhosted.org/packages/0f/5d/85e059349e9cfbd57269a1f11f56747b3ff5799a3bcbd95485f363c623d8/pyobjc_framework_coretext-12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:14100d1e39efb30f57869671fb6fce8d668f80c82e25e7930fb364866e5c0dab", size = 30697, upload-time = "2025-11-14T09:47:10.932Z" },
|
|
800
|
+
{ url = "https://files.pythonhosted.org/packages/ef/c3/adf9d306e9ead108167ab7a974ab7d171dbacf31c72fad63e12585f58023/pyobjc_framework_coretext-12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:782a1a9617ea267c05226e9cd81a8dec529969a607fe1e037541ee1feb9524e9", size = 30095, upload-time = "2025-11-14T09:47:13.893Z" },
|
|
801
|
+
{ url = "https://files.pythonhosted.org/packages/bd/ca/6321295f47a47b0fca7de7e751ddc0ddc360413f4e506335fe9b0f0fb085/pyobjc_framework_coretext-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:7afe379c5a870fa3e66e6f65231c3c1732d9ccd2cd2a4904b2cd5178c9e3c562", size = 30702, upload-time = "2025-11-14T09:47:17.292Z" },
|
|
802
|
+
]
|
|
803
|
+
|
|
762
804
|
[[package]]
|
|
763
805
|
name = "pyobjc-framework-quartz"
|
|
764
806
|
version = "12.1"
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "oagi-core"
|
|
7
|
-
version = "0.10.
|
|
7
|
+
version = "0.10.2"
|
|
8
8
|
description = "Official API of OpenAGI Foundation"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { file = "LICENSE" }
|
|
@@ -31,6 +31,7 @@ desktop = [
|
|
|
31
31
|
"pillow>=11.3.0",
|
|
32
32
|
"pyautogui>=0.9.54",
|
|
33
33
|
"pyobjc-framework-Quartz>=9.0; sys_platform == 'darwin'",
|
|
34
|
+
"pyobjc-framework-ApplicationServices>=9.0; sys_platform == 'darwin'",
|
|
34
35
|
]
|
|
35
36
|
server = [
|
|
36
37
|
"fastapi[standard]>=0.115.0",
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
# Licensed under the MIT License.
|
|
7
7
|
# -----------------------------------------------------------------------------
|
|
8
8
|
|
|
9
|
+
import asyncio
|
|
9
10
|
import logging
|
|
10
11
|
|
|
11
12
|
from .. import AsyncActor
|
|
@@ -39,6 +40,7 @@ class AsyncDefaultAgent:
|
|
|
39
40
|
max_steps: int = 20,
|
|
40
41
|
temperature: float | None = 0.5,
|
|
41
42
|
step_observer: AsyncObserver | None = None,
|
|
43
|
+
step_delay: float = 0.3,
|
|
42
44
|
):
|
|
43
45
|
self.api_key = api_key
|
|
44
46
|
self.base_url = base_url
|
|
@@ -46,6 +48,7 @@ class AsyncDefaultAgent:
|
|
|
46
48
|
self.max_steps = max_steps
|
|
47
49
|
self.temperature = temperature
|
|
48
50
|
self.step_observer = step_observer
|
|
51
|
+
self.step_delay = step_delay
|
|
49
52
|
|
|
50
53
|
async def execute(
|
|
51
54
|
self,
|
|
@@ -113,6 +116,10 @@ class AsyncDefaultAgent:
|
|
|
113
116
|
)
|
|
114
117
|
)
|
|
115
118
|
|
|
119
|
+
# Wait after actions before next screenshot
|
|
120
|
+
if self.step_delay > 0:
|
|
121
|
+
await asyncio.sleep(self.step_delay)
|
|
122
|
+
|
|
116
123
|
# Check if task is complete
|
|
117
124
|
if step.stop:
|
|
118
125
|
logger.info(f"Task completed successfully after {step_num} steps")
|
|
@@ -21,6 +21,7 @@ def create_default_agent(
|
|
|
21
21
|
max_steps: int = 20,
|
|
22
22
|
temperature: float = 0.1,
|
|
23
23
|
step_observer: AsyncStepObserver | None = None,
|
|
24
|
+
step_delay: float = 0.3,
|
|
24
25
|
) -> AsyncAgent:
|
|
25
26
|
return AsyncDefaultAgent(
|
|
26
27
|
api_key=api_key,
|
|
@@ -29,6 +30,7 @@ def create_default_agent(
|
|
|
29
30
|
max_steps=max_steps,
|
|
30
31
|
temperature=temperature,
|
|
31
32
|
step_observer=step_observer,
|
|
33
|
+
step_delay=step_delay,
|
|
32
34
|
)
|
|
33
35
|
|
|
34
36
|
|
|
@@ -40,6 +42,7 @@ def create_thinker_agent(
|
|
|
40
42
|
max_steps: int = 100,
|
|
41
43
|
temperature: float = 0.1,
|
|
42
44
|
step_observer: AsyncStepObserver | None = None,
|
|
45
|
+
step_delay: float = 0.3,
|
|
43
46
|
) -> AsyncAgent:
|
|
44
47
|
return AsyncDefaultAgent(
|
|
45
48
|
api_key=api_key,
|
|
@@ -48,6 +51,7 @@ def create_thinker_agent(
|
|
|
48
51
|
max_steps=max_steps,
|
|
49
52
|
temperature=temperature,
|
|
50
53
|
step_observer=step_observer,
|
|
54
|
+
step_delay=step_delay,
|
|
51
55
|
)
|
|
52
56
|
|
|
53
57
|
|
|
@@ -60,6 +64,7 @@ def create_planner_agent(
|
|
|
60
64
|
temperature: float = 0.1,
|
|
61
65
|
reflection_interval: int = 20,
|
|
62
66
|
step_observer: AsyncStepObserver | None = None,
|
|
67
|
+
step_delay: float = 0.3,
|
|
63
68
|
) -> AsyncAgent:
|
|
64
69
|
tasker = TaskerAgent(
|
|
65
70
|
api_key=api_key,
|
|
@@ -69,6 +74,7 @@ def create_planner_agent(
|
|
|
69
74
|
temperature=temperature,
|
|
70
75
|
reflection_interval=reflection_interval,
|
|
71
76
|
step_observer=step_observer,
|
|
77
|
+
step_delay=step_delay,
|
|
72
78
|
)
|
|
73
79
|
# tasker.set_task()
|
|
74
80
|
return tasker
|
|
@@ -0,0 +1,336 @@
|
|
|
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
|
+
import json
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from ...types import (
|
|
14
|
+
Action,
|
|
15
|
+
ActionEvent,
|
|
16
|
+
ActionType,
|
|
17
|
+
ImageEvent,
|
|
18
|
+
LogEvent,
|
|
19
|
+
ObserverEvent,
|
|
20
|
+
PlanEvent,
|
|
21
|
+
SplitEvent,
|
|
22
|
+
StepEvent,
|
|
23
|
+
parse_coords,
|
|
24
|
+
parse_drag_coords,
|
|
25
|
+
parse_scroll,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _parse_action_coords(action: Action) -> dict | None:
|
|
30
|
+
"""Parse coordinates from action argument for cursor indicators.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Dict with coordinates based on action type, or None if not applicable.
|
|
34
|
+
- Click types: {"type": "click", "x": int, "y": int}
|
|
35
|
+
- Drag: {"type": "drag", "x1": int, "y1": int, "x2": int, "y2": int}
|
|
36
|
+
- Scroll: {"type": "scroll", "x": int, "y": int, "direction": str}
|
|
37
|
+
"""
|
|
38
|
+
arg = action.argument.strip("()")
|
|
39
|
+
|
|
40
|
+
match action.type:
|
|
41
|
+
case (
|
|
42
|
+
ActionType.CLICK
|
|
43
|
+
| ActionType.LEFT_DOUBLE
|
|
44
|
+
| ActionType.LEFT_TRIPLE
|
|
45
|
+
| ActionType.RIGHT_SINGLE
|
|
46
|
+
):
|
|
47
|
+
coords = parse_coords(arg)
|
|
48
|
+
if coords:
|
|
49
|
+
return {"type": "click", "x": coords[0], "y": coords[1]}
|
|
50
|
+
case ActionType.DRAG:
|
|
51
|
+
coords = parse_drag_coords(arg)
|
|
52
|
+
if coords:
|
|
53
|
+
return {
|
|
54
|
+
"type": "drag",
|
|
55
|
+
"x1": coords[0],
|
|
56
|
+
"y1": coords[1],
|
|
57
|
+
"x2": coords[2],
|
|
58
|
+
"y2": coords[3],
|
|
59
|
+
}
|
|
60
|
+
case ActionType.SCROLL:
|
|
61
|
+
result = parse_scroll(arg)
|
|
62
|
+
if result:
|
|
63
|
+
return {
|
|
64
|
+
"type": "scroll",
|
|
65
|
+
"x": result[0],
|
|
66
|
+
"y": result[1],
|
|
67
|
+
"direction": result[2],
|
|
68
|
+
}
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def export_to_markdown(
|
|
73
|
+
events: list[ObserverEvent],
|
|
74
|
+
path: str,
|
|
75
|
+
images_dir: str | None = None,
|
|
76
|
+
) -> None:
|
|
77
|
+
"""Export events to a Markdown file.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
events: List of events to export.
|
|
81
|
+
path: Path to the output Markdown file.
|
|
82
|
+
images_dir: Directory to save images. If None, images are not saved.
|
|
83
|
+
"""
|
|
84
|
+
output_path = Path(path)
|
|
85
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
86
|
+
|
|
87
|
+
if images_dir:
|
|
88
|
+
images_path = Path(images_dir)
|
|
89
|
+
images_path.mkdir(parents=True, exist_ok=True)
|
|
90
|
+
|
|
91
|
+
lines: list[str] = ["# Agent Execution Report\n"]
|
|
92
|
+
image_counter = 0
|
|
93
|
+
|
|
94
|
+
for event in events:
|
|
95
|
+
timestamp = event.timestamp.strftime("%H:%M:%S")
|
|
96
|
+
|
|
97
|
+
match event:
|
|
98
|
+
case StepEvent():
|
|
99
|
+
lines.append(f"\n## Step {event.step_num}\n")
|
|
100
|
+
lines.append(f"**Time:** {timestamp}\n")
|
|
101
|
+
|
|
102
|
+
if isinstance(event.image, bytes):
|
|
103
|
+
if images_dir:
|
|
104
|
+
image_counter += 1
|
|
105
|
+
image_filename = f"step_{event.step_num}.png"
|
|
106
|
+
image_path = Path(images_dir) / image_filename
|
|
107
|
+
image_path.write_bytes(event.image)
|
|
108
|
+
rel_path = Path(images_dir).name / Path(image_filename)
|
|
109
|
+
lines.append(f"\n\n")
|
|
110
|
+
else:
|
|
111
|
+
lines.append(
|
|
112
|
+
f"\n*[Screenshot captured - {len(event.image)} bytes]*\n"
|
|
113
|
+
)
|
|
114
|
+
elif isinstance(event.image, str):
|
|
115
|
+
lines.append(f"\n**Screenshot URL:** {event.image}\n")
|
|
116
|
+
|
|
117
|
+
if event.step.reason:
|
|
118
|
+
lines.append(f"\n**Reasoning:**\n> {event.step.reason}\n")
|
|
119
|
+
|
|
120
|
+
if event.step.actions:
|
|
121
|
+
lines.append("\n**Planned Actions:**\n")
|
|
122
|
+
for action in event.step.actions:
|
|
123
|
+
count_str = (
|
|
124
|
+
f" (x{action.count})"
|
|
125
|
+
if action.count and action.count > 1
|
|
126
|
+
else ""
|
|
127
|
+
)
|
|
128
|
+
lines.append(
|
|
129
|
+
f"- `{action.type.value}`: {action.argument}{count_str}\n"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
if event.step.stop:
|
|
133
|
+
lines.append("\n**Status:** Task Complete\n")
|
|
134
|
+
|
|
135
|
+
case ActionEvent():
|
|
136
|
+
lines.append(f"\n### Actions Executed ({timestamp})\n")
|
|
137
|
+
if event.error:
|
|
138
|
+
lines.append(f"\n**Error:** {event.error}\n")
|
|
139
|
+
else:
|
|
140
|
+
lines.append("\n**Result:** Success\n")
|
|
141
|
+
|
|
142
|
+
case LogEvent():
|
|
143
|
+
lines.append(f"\n> **Log ({timestamp}):** {event.message}\n")
|
|
144
|
+
|
|
145
|
+
case SplitEvent():
|
|
146
|
+
if event.label:
|
|
147
|
+
lines.append(f"\n---\n\n### {event.label}\n")
|
|
148
|
+
else:
|
|
149
|
+
lines.append("\n---\n")
|
|
150
|
+
|
|
151
|
+
case ImageEvent():
|
|
152
|
+
pass
|
|
153
|
+
|
|
154
|
+
case PlanEvent():
|
|
155
|
+
phase_titles = {
|
|
156
|
+
"initial": "Initial Planning",
|
|
157
|
+
"reflection": "Reflection",
|
|
158
|
+
"summary": "Summary",
|
|
159
|
+
}
|
|
160
|
+
phase_title = phase_titles.get(event.phase, event.phase.capitalize())
|
|
161
|
+
lines.append(f"\n### {phase_title} ({timestamp})\n")
|
|
162
|
+
|
|
163
|
+
if event.image:
|
|
164
|
+
if isinstance(event.image, bytes):
|
|
165
|
+
if images_dir:
|
|
166
|
+
image_counter += 1
|
|
167
|
+
image_filename = f"plan_{event.phase}_{image_counter}.png"
|
|
168
|
+
image_path = Path(images_dir) / image_filename
|
|
169
|
+
image_path.write_bytes(event.image)
|
|
170
|
+
rel_path = Path(images_dir).name / Path(image_filename)
|
|
171
|
+
lines.append(f"\n\n")
|
|
172
|
+
else:
|
|
173
|
+
lines.append(
|
|
174
|
+
f"\n*[Screenshot captured - {len(event.image)} bytes]*\n"
|
|
175
|
+
)
|
|
176
|
+
elif isinstance(event.image, str):
|
|
177
|
+
lines.append(f"\n**Screenshot URL:** {event.image}\n")
|
|
178
|
+
|
|
179
|
+
if event.reasoning:
|
|
180
|
+
lines.append(f"\n**Reasoning:**\n> {event.reasoning}\n")
|
|
181
|
+
|
|
182
|
+
if event.result:
|
|
183
|
+
lines.append(f"\n**Result:** {event.result}\n")
|
|
184
|
+
|
|
185
|
+
output_path.write_text("".join(lines))
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def _convert_events_for_html(events: list[ObserverEvent]) -> list[dict]:
|
|
189
|
+
"""Convert events to JSON-serializable format for HTML template."""
|
|
190
|
+
result = []
|
|
191
|
+
|
|
192
|
+
for event in events:
|
|
193
|
+
timestamp = event.timestamp.strftime("%H:%M:%S")
|
|
194
|
+
|
|
195
|
+
match event:
|
|
196
|
+
case StepEvent():
|
|
197
|
+
# Collect action coordinates for cursor indicators
|
|
198
|
+
action_coords = []
|
|
199
|
+
actions_list = []
|
|
200
|
+
if event.step.actions:
|
|
201
|
+
for action in event.step.actions:
|
|
202
|
+
coords = _parse_action_coords(action)
|
|
203
|
+
if coords:
|
|
204
|
+
action_coords.append(coords)
|
|
205
|
+
actions_list.append(
|
|
206
|
+
{
|
|
207
|
+
"type": action.type.value,
|
|
208
|
+
"argument": action.argument,
|
|
209
|
+
"count": action.count or 1,
|
|
210
|
+
}
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
# Handle image
|
|
214
|
+
image_data = None
|
|
215
|
+
if isinstance(event.image, bytes):
|
|
216
|
+
image_data = base64.b64encode(event.image).decode("utf-8")
|
|
217
|
+
elif isinstance(event.image, str):
|
|
218
|
+
image_data = event.image
|
|
219
|
+
|
|
220
|
+
result.append(
|
|
221
|
+
{
|
|
222
|
+
"event_type": "step",
|
|
223
|
+
"timestamp": timestamp,
|
|
224
|
+
"step_num": event.step_num,
|
|
225
|
+
"image": image_data,
|
|
226
|
+
"action_coords": action_coords,
|
|
227
|
+
"reason": event.step.reason,
|
|
228
|
+
"actions": actions_list,
|
|
229
|
+
"stop": event.step.stop,
|
|
230
|
+
}
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
case ActionEvent():
|
|
234
|
+
result.append(
|
|
235
|
+
{
|
|
236
|
+
"event_type": "action",
|
|
237
|
+
"timestamp": timestamp,
|
|
238
|
+
"error": event.error,
|
|
239
|
+
}
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
case LogEvent():
|
|
243
|
+
result.append(
|
|
244
|
+
{
|
|
245
|
+
"event_type": "log",
|
|
246
|
+
"timestamp": timestamp,
|
|
247
|
+
"message": event.message,
|
|
248
|
+
}
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
case SplitEvent():
|
|
252
|
+
result.append(
|
|
253
|
+
{
|
|
254
|
+
"event_type": "split",
|
|
255
|
+
"timestamp": timestamp,
|
|
256
|
+
"label": event.label,
|
|
257
|
+
}
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
case ImageEvent():
|
|
261
|
+
pass
|
|
262
|
+
|
|
263
|
+
case PlanEvent():
|
|
264
|
+
image_data = None
|
|
265
|
+
if isinstance(event.image, bytes):
|
|
266
|
+
image_data = base64.b64encode(event.image).decode("utf-8")
|
|
267
|
+
elif isinstance(event.image, str):
|
|
268
|
+
image_data = event.image
|
|
269
|
+
|
|
270
|
+
result.append(
|
|
271
|
+
{
|
|
272
|
+
"event_type": "plan",
|
|
273
|
+
"timestamp": timestamp,
|
|
274
|
+
"phase": event.phase,
|
|
275
|
+
"image": image_data,
|
|
276
|
+
"reasoning": event.reasoning,
|
|
277
|
+
"result": event.result,
|
|
278
|
+
}
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
return result
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def export_to_html(events: list[ObserverEvent], path: str) -> None:
|
|
285
|
+
"""Export events to a self-contained HTML file.
|
|
286
|
+
|
|
287
|
+
Args:
|
|
288
|
+
events: List of events to export.
|
|
289
|
+
path: Path to the output HTML file.
|
|
290
|
+
"""
|
|
291
|
+
output_path = Path(path)
|
|
292
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
293
|
+
|
|
294
|
+
# Load template
|
|
295
|
+
template_path = Path(__file__).parent / "report_template.html"
|
|
296
|
+
template = template_path.read_text()
|
|
297
|
+
|
|
298
|
+
# Convert events to JSON
|
|
299
|
+
events_data = _convert_events_for_html(events)
|
|
300
|
+
events_json = json.dumps(events_data)
|
|
301
|
+
|
|
302
|
+
# Replace placeholder
|
|
303
|
+
html_content = template.replace("{EVENTS_DATA}", events_json)
|
|
304
|
+
|
|
305
|
+
output_path.write_text(html_content)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def export_to_json(events: list[ObserverEvent], path: str) -> None:
|
|
309
|
+
"""Export events to a JSON file.
|
|
310
|
+
|
|
311
|
+
Args:
|
|
312
|
+
events: List of events to export.
|
|
313
|
+
path: Path to the output JSON file.
|
|
314
|
+
"""
|
|
315
|
+
output_path = Path(path)
|
|
316
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
317
|
+
|
|
318
|
+
# Convert events to JSON-serializable format
|
|
319
|
+
json_events = []
|
|
320
|
+
for event in events:
|
|
321
|
+
# Handle bytes images before model_dump to avoid UTF-8 decode error
|
|
322
|
+
if isinstance(event, (StepEvent, ImageEvent, PlanEvent)) and isinstance(
|
|
323
|
+
getattr(event, "image", None), bytes
|
|
324
|
+
):
|
|
325
|
+
# Dump without json mode first, then handle bytes manually
|
|
326
|
+
event_dict = event.model_dump()
|
|
327
|
+
event_dict["image"] = base64.b64encode(event.image).decode("utf-8")
|
|
328
|
+
event_dict["image_encoding"] = "base64"
|
|
329
|
+
# Convert datetime to string
|
|
330
|
+
if "timestamp" in event_dict:
|
|
331
|
+
event_dict["timestamp"] = event_dict["timestamp"].isoformat()
|
|
332
|
+
else:
|
|
333
|
+
event_dict = event.model_dump(mode="json")
|
|
334
|
+
json_events.append(event_dict)
|
|
335
|
+
|
|
336
|
+
output_path.write_text(json.dumps(json_events, indent=2, default=str))
|