oagi-core 0.10.1__tar.gz → 0.10.3__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.1 → oagi_core-0.10.3}/PKG-INFO +1 -1
- {oagi_core-0.10.1 → oagi_core-0.10.3}/metapackage/pyproject.toml +2 -2
- {oagi_core-0.10.1 → oagi_core-0.10.3}/metapackage/uv.lock +47 -5
- {oagi_core-0.10.1 → oagi_core-0.10.3}/pyproject.toml +1 -1
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/agent/default.py +16 -3
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/agent/factories.py +26 -10
- oagi_core-0.10.3/src/oagi/agent/observer/exporters.py +336 -0
- oagi_core-0.10.3/src/oagi/agent/observer/report_template.html +455 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/agent/tasker/planner.py +2 -1
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/agent/tasker/taskee_agent.py +19 -4
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/agent/tasker/tasker_agent.py +15 -4
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/cli/agent.py +35 -12
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/cli/display.py +2 -1
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/cli/server.py +1 -1
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/cli/utils.py +4 -3
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/client/async_.py +19 -6
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/client/base.py +14 -16
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/client/sync.py +19 -6
- oagi_core-0.10.3/src/oagi/constants.py +43 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/handler/pyautogui_action_handler.py +14 -23
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/server/config.py +6 -3
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/server/models.py +5 -3
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/server/session_store.py +6 -4
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/server/socketio_server.py +22 -20
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/task/async_.py +4 -3
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/task/async_short.py +3 -2
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/task/base.py +2 -1
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/task/short.py +3 -2
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/task/sync.py +4 -3
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/types/__init__.py +12 -1
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/types/models/__init__.py +10 -1
- oagi_core-0.10.3/src/oagi/types/models/action.py +84 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/tests/conftest.py +4 -3
- oagi_core-0.10.3/tests/test_action_parsing.py +98 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/tests/test_actor.py +6 -5
- {oagi_core-0.10.1 → oagi_core-0.10.3}/tests/test_agent_registry.py +12 -11
- {oagi_core-0.10.1 → oagi_core-0.10.3}/tests/test_async_client.py +25 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/tests/test_cli.py +2 -1
- {oagi_core-0.10.1 → oagi_core-0.10.3}/tests/test_observer.py +12 -9
- {oagi_core-0.10.1 → oagi_core-0.10.3}/tests/test_server/test_session_store.py +7 -6
- {oagi_core-0.10.1 → oagi_core-0.10.3}/tests/test_sync_client.py +24 -2
- {oagi_core-0.10.1 → oagi_core-0.10.3}/tests/test_taskee_agent.py +2 -1
- {oagi_core-0.10.1 → oagi_core-0.10.3}/uv.lock +1 -1
- oagi_core-0.10.1/src/oagi/agent/observer/exporters.py +0 -445
- oagi_core-0.10.1/src/oagi/types/models/action.py +0 -33
- {oagi_core-0.10.1 → oagi_core-0.10.3}/.github/workflows/ci.yml +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/.github/workflows/release.yml +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/.gitignore +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/.python-version +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/CONTRIBUTING.md +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/LICENSE +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/Makefile +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/README.md +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/examples/async_google_weather.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/examples/execute_task_auto.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/examples/execute_task_manual.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/examples/google_weather.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/examples/screenshot_with_config.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/examples/tasker_agent_example.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/__init__.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/agent/__init__.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/agent/observer/__init__.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/agent/observer/agent_observer.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/agent/observer/events.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/agent/observer/protocol.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/agent/protocol.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/agent/registry.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/agent/tasker/__init__.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/agent/tasker/memory.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/agent/tasker/models.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/cli/__init__.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/cli/main.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/cli/tracking.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/client/__init__.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/exceptions.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/handler/__init__.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/handler/_macos.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/handler/async_pyautogui_action_handler.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/handler/async_screenshot_maker.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/handler/pil_image.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/handler/screenshot_maker.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/logging.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/server/__init__.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/server/agent_wrappers.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/server/main.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/task/__init__.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/types/action_handler.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/types/async_action_handler.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/types/async_image_provider.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/types/image.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/types/image_provider.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/types/models/client.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/types/models/image_config.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/types/models/step.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/types/step_observer.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/src/oagi/types/url.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/tests/__init__.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/tests/test_agent/test_agent_wrappers.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/tests/test_agent/test_default_agent.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/tests/test_async_actor.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/tests/test_async_handlers.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/tests/test_logging.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/tests/test_mac_double_click.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/tests/test_pil_image.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/tests/test_planner.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/tests/test_planner_memory.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/tests/test_pyautogui_action_handler.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/tests/test_screenshot_maker.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/tests/test_server/__init__.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/tests/test_server/test_config.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/tests/test_server/test_socketio_integration.py +0 -0
- {oagi_core-0.10.1 → oagi_core-0.10.3}/tests/test_tasker_agent.py +0 -0
|
@@ -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.3"
|
|
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.3",
|
|
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.3"
|
|
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.10.
|
|
407
|
+
requires-dist = [{ name = "oagi-core", extras = ["desktop", "server"], specifier = "==0.10.2" }]
|
|
408
408
|
|
|
409
409
|
[[package]]
|
|
410
410
|
name = "oagi-core"
|
|
411
|
-
version = "0.10.
|
|
411
|
+
version = "0.10.2"
|
|
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/07/2f/11b7e37049b2faa1e1147a75624c024a93c32287f243c213ca96fc096452/oagi_core-0.10.2.tar.gz", hash = "sha256:2d7fb47031cdc2155ea5ea9c06b4b58c0d70091de95a36fa4dedf5d710a09862", size = 267130, upload-time = "2025-11-26T13:50:36.977Z" }
|
|
419
419
|
wheels = [
|
|
420
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
420
|
+
{ url = "https://files.pythonhosted.org/packages/d0/c8/a1d95327afe6237eaf474d74ba66461e51baaf5948d458cb12be62dbf8a8/oagi_core-0.10.2-py3-none-any.whl", hash = "sha256:3b9dd3ade24a1c605d671ed7f35efcd00f729be603a63173c7f92f8177d56750", size = 87220, upload-time = "2025-11-26T13:50:35.872Z" },
|
|
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"
|
|
@@ -6,9 +6,16 @@
|
|
|
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
|
|
13
|
+
from ..constants import (
|
|
14
|
+
DEFAULT_MAX_STEPS,
|
|
15
|
+
DEFAULT_STEP_DELAY,
|
|
16
|
+
DEFAULT_TEMPERATURE,
|
|
17
|
+
MODEL_ACTOR,
|
|
18
|
+
)
|
|
12
19
|
from ..types import (
|
|
13
20
|
ActionEvent,
|
|
14
21
|
AsyncActionHandler,
|
|
@@ -35,10 +42,11 @@ class AsyncDefaultAgent:
|
|
|
35
42
|
self,
|
|
36
43
|
api_key: str | None = None,
|
|
37
44
|
base_url: str | None = None,
|
|
38
|
-
model: str =
|
|
39
|
-
max_steps: int =
|
|
40
|
-
temperature: float | None =
|
|
45
|
+
model: str = MODEL_ACTOR,
|
|
46
|
+
max_steps: int = DEFAULT_MAX_STEPS,
|
|
47
|
+
temperature: float | None = DEFAULT_TEMPERATURE,
|
|
41
48
|
step_observer: AsyncObserver | None = None,
|
|
49
|
+
step_delay: float = DEFAULT_STEP_DELAY,
|
|
42
50
|
):
|
|
43
51
|
self.api_key = api_key
|
|
44
52
|
self.base_url = base_url
|
|
@@ -46,6 +54,7 @@ class AsyncDefaultAgent:
|
|
|
46
54
|
self.max_steps = max_steps
|
|
47
55
|
self.temperature = temperature
|
|
48
56
|
self.step_observer = step_observer
|
|
57
|
+
self.step_delay = step_delay
|
|
49
58
|
|
|
50
59
|
async def execute(
|
|
51
60
|
self,
|
|
@@ -113,6 +122,10 @@ class AsyncDefaultAgent:
|
|
|
113
122
|
)
|
|
114
123
|
)
|
|
115
124
|
|
|
125
|
+
# Wait after actions before next screenshot
|
|
126
|
+
if self.step_delay > 0:
|
|
127
|
+
await asyncio.sleep(self.step_delay)
|
|
128
|
+
|
|
116
129
|
# Check if task is complete
|
|
117
130
|
if step.stop:
|
|
118
131
|
logger.info(f"Task completed successfully after {step_num} steps")
|
|
@@ -6,6 +6,16 @@
|
|
|
6
6
|
# Licensed under the MIT License.
|
|
7
7
|
# -----------------------------------------------------------------------------
|
|
8
8
|
from oagi.agent.tasker import TaskerAgent
|
|
9
|
+
from oagi.constants import (
|
|
10
|
+
DEFAULT_MAX_STEPS,
|
|
11
|
+
DEFAULT_MAX_STEPS_TASKER,
|
|
12
|
+
DEFAULT_MAX_STEPS_THINKER,
|
|
13
|
+
DEFAULT_REFLECTION_INTERVAL_TASKER,
|
|
14
|
+
DEFAULT_STEP_DELAY,
|
|
15
|
+
DEFAULT_TEMPERATURE_LOW,
|
|
16
|
+
MODEL_ACTOR,
|
|
17
|
+
MODEL_THINKER,
|
|
18
|
+
)
|
|
9
19
|
from oagi.types import AsyncStepObserver
|
|
10
20
|
|
|
11
21
|
from .default import AsyncDefaultAgent
|
|
@@ -17,10 +27,11 @@ from .registry import async_agent_register
|
|
|
17
27
|
def create_default_agent(
|
|
18
28
|
api_key: str | None = None,
|
|
19
29
|
base_url: str | None = None,
|
|
20
|
-
model: str =
|
|
21
|
-
max_steps: int =
|
|
22
|
-
temperature: float =
|
|
30
|
+
model: str = MODEL_ACTOR,
|
|
31
|
+
max_steps: int = DEFAULT_MAX_STEPS,
|
|
32
|
+
temperature: float = DEFAULT_TEMPERATURE_LOW,
|
|
23
33
|
step_observer: AsyncStepObserver | None = None,
|
|
34
|
+
step_delay: float = DEFAULT_STEP_DELAY,
|
|
24
35
|
) -> AsyncAgent:
|
|
25
36
|
return AsyncDefaultAgent(
|
|
26
37
|
api_key=api_key,
|
|
@@ -29,6 +40,7 @@ def create_default_agent(
|
|
|
29
40
|
max_steps=max_steps,
|
|
30
41
|
temperature=temperature,
|
|
31
42
|
step_observer=step_observer,
|
|
43
|
+
step_delay=step_delay,
|
|
32
44
|
)
|
|
33
45
|
|
|
34
46
|
|
|
@@ -36,10 +48,11 @@ def create_default_agent(
|
|
|
36
48
|
def create_thinker_agent(
|
|
37
49
|
api_key: str | None = None,
|
|
38
50
|
base_url: str | None = None,
|
|
39
|
-
model: str =
|
|
40
|
-
max_steps: int =
|
|
41
|
-
temperature: float =
|
|
51
|
+
model: str = MODEL_THINKER,
|
|
52
|
+
max_steps: int = DEFAULT_MAX_STEPS_THINKER,
|
|
53
|
+
temperature: float = DEFAULT_TEMPERATURE_LOW,
|
|
42
54
|
step_observer: AsyncStepObserver | None = None,
|
|
55
|
+
step_delay: float = DEFAULT_STEP_DELAY,
|
|
43
56
|
) -> AsyncAgent:
|
|
44
57
|
return AsyncDefaultAgent(
|
|
45
58
|
api_key=api_key,
|
|
@@ -48,6 +61,7 @@ def create_thinker_agent(
|
|
|
48
61
|
max_steps=max_steps,
|
|
49
62
|
temperature=temperature,
|
|
50
63
|
step_observer=step_observer,
|
|
64
|
+
step_delay=step_delay,
|
|
51
65
|
)
|
|
52
66
|
|
|
53
67
|
|
|
@@ -55,11 +69,12 @@ def create_thinker_agent(
|
|
|
55
69
|
def create_planner_agent(
|
|
56
70
|
api_key: str | None = None,
|
|
57
71
|
base_url: str | None = None,
|
|
58
|
-
model: str =
|
|
59
|
-
max_steps: int =
|
|
60
|
-
temperature: float =
|
|
61
|
-
reflection_interval: int =
|
|
72
|
+
model: str = MODEL_ACTOR,
|
|
73
|
+
max_steps: int = DEFAULT_MAX_STEPS_TASKER,
|
|
74
|
+
temperature: float = DEFAULT_TEMPERATURE_LOW,
|
|
75
|
+
reflection_interval: int = DEFAULT_REFLECTION_INTERVAL_TASKER,
|
|
62
76
|
step_observer: AsyncStepObserver | None = None,
|
|
77
|
+
step_delay: float = DEFAULT_STEP_DELAY,
|
|
63
78
|
) -> AsyncAgent:
|
|
64
79
|
tasker = TaskerAgent(
|
|
65
80
|
api_key=api_key,
|
|
@@ -69,6 +84,7 @@ def create_planner_agent(
|
|
|
69
84
|
temperature=temperature,
|
|
70
85
|
reflection_interval=reflection_interval,
|
|
71
86
|
step_observer=step_observer,
|
|
87
|
+
step_delay=step_delay,
|
|
72
88
|
)
|
|
73
89
|
# tasker.set_task()
|
|
74
90
|
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))
|