Skip to content

Commit 3f0fc5f

Browse files
authored
Merge pull request #249 from Integration-Automation/feat/s3-store-batch
Add S3-compatible artifact store
2 parents a2bf011 + 0296020 commit 3f0fc5f

16 files changed

Lines changed: 484 additions & 1 deletion

File tree

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
## Table of Contents
1515

16+
- [What's new (2026-06-20) — S3-Compatible Artifact Store](#whats-new-2026-06-20--s3-compatible-artifact-store)
1617
- [What's new (2026-06-20) — Fuzzy String Matching & Dedupe](#whats-new-2026-06-20--fuzzy-string-matching--dedupe)
1718
- [What's new (2026-06-19) — Video Step-Overlay Report](#whats-new-2026-06-19--video-step-overlay-report)
1819
- [What's new (2026-06-19) — Agent Observability (GenAI OpenTelemetry Spans)](#whats-new-2026-06-19--agent-observability-genai-opentelemetry-spans)
@@ -93,6 +94,12 @@
9394

9495
---
9596

97+
## What's new (2026-06-20) — S3-Compatible Artifact Store
98+
99+
Push run artifacts to object storage. Full reference: [`docs/source/Eng/doc/new_features/v41_features_doc.rst`](docs/source/Eng/doc/new_features/v41_features_doc.rst).
100+
101+
- **`S3ArtifactStore`** (`AC_s3_upload` / `AC_s3_download` / `AC_s3_list` / `AC_s3_delete`, `ac_*`): upload/download/list/delete reports, screenshots, and recordings against any S3-compatible bucket (AWS S3, MinIO, R2). `boto3` is an **optional** `[s3]` extra and the client is **injectable**, so the store's logic — and the executor path — are fully unit-tested with a fake client (no boto3/network); the live AWS path is honestly noted as CI-unverifiable. The whole API is relative to the store `prefix`. A module-level default store backs the commands.
102+
96103
## What's new (2026-06-20) — Fuzzy String Matching & Dedupe
97104

98105
Match noisy OCR/UI text robustly. Full reference: [`docs/source/Eng/doc/new_features/v40_features_doc.rst`](docs/source/Eng/doc/new_features/v40_features_doc.rst).

README/README_zh-CN.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
## 目录
1414

15+
- [本次更新 (2026-06-20) — S3 兼容成品存储](#本次更新-2026-06-20--s3-兼容成品存储)
1516
- [本次更新 (2026-06-20) — 模糊字符串匹配与去重](#本次更新-2026-06-20--模糊字符串匹配与去重)
1617
- [本次更新 (2026-06-19) — 视频步骤叠加报告](#本次更新-2026-06-19--视频步骤叠加报告)
1718
- [本次更新 (2026-06-19) — Agent 可观测性(GenAI OpenTelemetry Spans)](#本次更新-2026-06-19--agent-可观测性genai-opentelemetry-spans)
@@ -92,6 +93,12 @@
9293

9394
---
9495

96+
## 本次更新 (2026-06-20) — S3 兼容成品存储
97+
98+
将运行成品推送到对象存储。完整参考:[`docs/source/Zh/doc/new_features/v41_features_doc.rst`](../docs/source/Zh/doc/new_features/v41_features_doc.rst)
99+
100+
- **`S3ArtifactStore`**(`AC_s3_upload` / `AC_s3_download` / `AC_s3_list` / `AC_s3_delete``ac_*`):对任何 S3 兼容存储桶(AWS S3、MinIO、R2)上传/下载/列出/删除报告、屏幕截图与录像。`boto3`**可选** `[s3]` extra,且 client **可注入**,因此存储体逻辑(含 executor 路径)以假 client 完整单元测试(无 boto3/网络);实际 AWS 路径诚实标注为 CI 无法验证。整个 API 相对于存储体 `prefix`。模块级的默认存储体支撑这些指令。
101+
95102
## 本次更新 (2026-06-20) — 模糊字符串匹配与去重
96103

97104
稳健匹配含噪声的 OCR/UI 文本。完整参考:[`docs/source/Zh/doc/new_features/v40_features_doc.rst`](../docs/source/Zh/doc/new_features/v40_features_doc.rst)

README/README_zh-TW.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
## 目錄
1414

15+
- [本次更新 (2026-06-20) — S3 相容成品儲存](#本次更新-2026-06-20--s3-相容成品儲存)
1516
- [本次更新 (2026-06-20) — 模糊字串比對與去重](#本次更新-2026-06-20--模糊字串比對與去重)
1617
- [本次更新 (2026-06-19) — 影片步驟疊加報告](#本次更新-2026-06-19--影片步驟疊加報告)
1718
- [本次更新 (2026-06-19) — Agent 可觀測性(GenAI OpenTelemetry Spans)](#本次更新-2026-06-19--agent-可觀測性genai-opentelemetry-spans)
@@ -92,6 +93,12 @@
9293

9394
---
9495

96+
## 本次更新 (2026-06-20) — S3 相容成品儲存
97+
98+
將執行成品推送到物件儲存。完整參考:[`docs/source/Zh/doc/new_features/v41_features_doc.rst`](../docs/source/Zh/doc/new_features/v41_features_doc.rst)
99+
100+
- **`S3ArtifactStore`**(`AC_s3_upload` / `AC_s3_download` / `AC_s3_list` / `AC_s3_delete``ac_*`):對任何 S3 相容儲存桶(AWS S3、MinIO、R2)上傳/下載/列出/刪除報告、螢幕截圖與錄影。`boto3`**選用** `[s3]` extra,且 client **可注入**,因此儲存體邏輯(含 executor 路徑)以假 client 完整單元測試(無 boto3/網路);實際 AWS 路徑誠實標註為 CI 無法驗證。整個 API 相對於儲存體 `prefix`。模組層級的預設儲存體支撐這些指令。
101+
95102
## 本次更新 (2026-06-20) — 模糊字串比對與去重
96103

97104
穩健比對含雜訊的 OCR/UI 文字。完整參考:[`docs/source/Zh/doc/new_features/v40_features_doc.rst`](../docs/source/Zh/doc/new_features/v40_features_doc.rst)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
S3-Compatible Artifact Store
2+
============================
3+
4+
Reports, screenshots, and screen recordings produced by a run are usually worth
5+
keeping off the runner. ``S3ArtifactStore`` uploads, downloads, lists, and
6+
deletes them against any S3-compatible bucket (AWS S3, MinIO, Cloudflare R2, …).
7+
8+
``boto3`` is an **optional** dependency (``pip install je_auto_control[s3]``):
9+
the S3 client is *injectable*, so the store's logic is fully unit-testable with a
10+
fake client and ``boto3`` is imported only when no client is supplied. The whole
11+
API is relative to the store's configured ``prefix`` — ``upload`` returns a
12+
store-relative key that ``download`` / ``delete`` / ``url`` accept unchanged, and
13+
``list`` strips the prefix back off. Imports no ``PySide6``.
14+
15+
Headless API
16+
------------
17+
18+
.. code-block:: python
19+
20+
from je_auto_control import S3ArtifactStore
21+
22+
store = S3ArtifactStore("my-bucket", prefix="runs/42") # boto3 client lazily built
23+
key = store.upload("report.html") # -> "report.html" (relative)
24+
store.url(key) # -> "s3://my-bucket/runs/42/report.html"
25+
store.download(key, "local/report.html")
26+
store.list() # -> ["report.html", ...]
27+
store.delete(key)
28+
29+
For tests or non-AWS backends, pass your own client:
30+
``S3ArtifactStore("bucket", client=my_client)``.
31+
32+
.. note::
33+
34+
The live AWS path requires ``boto3`` plus credentials and is therefore not
35+
exercised in CI; the store's logic is validated against a fake S3 client.
36+
37+
Executor commands
38+
-----------------
39+
40+
A module-level default store — configured once with
41+
``configure_default_store(bucket, client=None, prefix="")`` — backs the
42+
executor/MCP commands:
43+
44+
================================ ===================================================
45+
Command Effect
46+
================================ ===================================================
47+
``AC_s3_upload`` Upload a local artifact; returns ``{key}``.
48+
``AC_s3_download`` Download an object to a local path.
49+
``AC_s3_list`` List object keys (optional extra ``prefix``).
50+
``AC_s3_delete`` Delete an object.
51+
================================ ===================================================
52+
53+
The same operations are exposed as MCP tools (``ac_s3_upload`` /
54+
``ac_s3_download`` / ``ac_s3_list`` / ``ac_s3_delete``) and as Script Builder
55+
commands under **Tools**.

docs/source/Eng/eng_index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ Comprehensive guides for all AutoControl features.
6363
doc/new_features/v38_features_doc
6464
doc/new_features/v39_features_doc
6565
doc/new_features/v40_features_doc
66+
doc/new_features/v41_features_doc
6667
doc/ocr_backends/ocr_backends_doc
6768
doc/observability/observability_doc
6869
doc/operations_layer/operations_layer_doc
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
S3 相容成品儲存
2+
===============
3+
4+
一次執行產生的報告、螢幕截圖與螢幕錄影,通常值得存放到 runner 之外。
5+
``S3ArtifactStore`` 可對任何 S3 相容儲存桶(AWS S3、MinIO、Cloudflare R2…)上傳、下
6+
載、列出與刪除這些成品。
7+
8+
``boto3`` 為**選用**相依(``pip install je_auto_control[s3]``):S3 client *可注入*,因
9+
此儲存體的邏輯可用假 client 完整單元測試,且僅在未提供 client 時才匯入 ``boto3``。整個
10+
API 皆相對於儲存體設定的 ``prefix`` —— ``upload`` 回傳儲存體相對鍵,``download`` /
11+
``delete`` / ``url`` 原樣接受,而 ``list`` 則會把 prefix 去除。不匯入 ``PySide6``。
12+
13+
無頭 API
14+
--------
15+
16+
.. code-block:: python
17+
18+
from je_auto_control import S3ArtifactStore
19+
20+
store = S3ArtifactStore("my-bucket", prefix="runs/42") # boto3 client 延遲建立
21+
key = store.upload("report.html") # -> "report.html"(相對)
22+
store.url(key) # -> "s3://my-bucket/runs/42/report.html"
23+
store.download(key, "local/report.html")
24+
store.list() # -> ["report.html", ...]
25+
store.delete(key)
26+
27+
測試或非 AWS 後端可傳入自己的 client:``S3ArtifactStore("bucket", client=my_client)``。
28+
29+
.. note::
30+
31+
實際的 AWS 路徑需要 ``boto3`` 與憑證,因此不會在 CI 中執行;儲存體邏輯以假 S3
32+
client 驗證。
33+
34+
執行器指令
35+
----------
36+
37+
模組層級的預設儲存體 —— 以 ``configure_default_store(bucket, client=None, prefix="")``
38+
設定一次 —— 支撐 executor/MCP 指令:
39+
40+
================================ ===================================================
41+
指令 效果
42+
================================ ===================================================
43+
``AC_s3_upload`` 上傳本機成品;回傳 ``{key}``。
44+
``AC_s3_download`` 將物件下載到本機路徑。
45+
``AC_s3_list`` 列出物件鍵(可加 ``prefix``)。
46+
``AC_s3_delete`` 刪除物件。
47+
================================ ===================================================
48+
49+
相同操作亦提供為 MCP 工具(``ac_s3_upload`` / ``ac_s3_download`` / ``ac_s3_list`` /
50+
``ac_s3_delete``),以及 Script Builder 中 **Tools** 分類下的指令。

docs/source/Zh/zh_index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ AutoControl 所有功能的完整使用指南。
6363
doc/new_features/v38_features_doc
6464
doc/new_features/v39_features_doc
6565
doc/new_features/v40_features_doc
66+
doc/new_features/v41_features_doc
6667
doc/ocr_backends/ocr_backends_doc
6768
doc/observability/observability_doc
6869
doc/operations_layer/operations_layer_doc

je_auto_control/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,11 @@
234234
from je_auto_control.utils.fuzzy import (
235235
fuzzy_best_match, fuzzy_dedupe, fuzzy_matches, fuzzy_ratio,
236236
)
237+
# S3-compatible artifact store (optional boto3, injectable client)
238+
from je_auto_control.utils.artifact_store import (
239+
S3ArtifactStore, configure_default_store, get_default_store,
240+
set_default_store,
241+
)
237242
# Background popup/interrupt watchdog (unattended automation)
238243
from je_auto_control.utils.watchdog import (
239244
PopupWatchdog, WatchdogRule, default_popup_watchdog,
@@ -681,6 +686,8 @@ def start_autocontrol_gui(*args, **kwargs):
681686
"VideoStep", "build_overlay_plan", "render_overlay_frame",
682687
"write_step_video",
683688
"fuzzy_best_match", "fuzzy_dedupe", "fuzzy_matches", "fuzzy_ratio",
689+
"S3ArtifactStore", "configure_default_store", "get_default_store",
690+
"set_default_store",
684691
# MCP server
685692
"AuditLogger", "HttpMCPServer", "MCPContent", "MCPPrompt",
686693
"MCPPromptArgument", "MCPResource", "MCPServer", "MCPTool",

je_auto_control/gui/script_builder/command_schema.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -902,6 +902,32 @@ def _add_misc_specs(specs: List[CommandSpec]) -> None:
902902
),
903903
description="Collapse near-duplicate strings (JSON list).",
904904
))
905+
specs.append(CommandSpec(
906+
"AC_s3_upload", "Tools", "S3: Upload Artifact",
907+
fields=(
908+
FieldSpec("local_path", FieldType.FILE_PATH),
909+
FieldSpec("key", FieldType.STRING, optional=True),
910+
),
911+
description="Upload a file to the configured default S3 store.",
912+
))
913+
specs.append(CommandSpec(
914+
"AC_s3_download", "Tools", "S3: Download Artifact",
915+
fields=(
916+
FieldSpec("key", FieldType.STRING),
917+
FieldSpec("local_path", FieldType.STRING),
918+
),
919+
description="Download an object from the default S3 store.",
920+
))
921+
specs.append(CommandSpec(
922+
"AC_s3_list", "Tools", "S3: List Artifacts",
923+
fields=(FieldSpec("prefix", FieldType.STRING, optional=True),),
924+
description="List object keys in the default S3 store.",
925+
))
926+
specs.append(CommandSpec(
927+
"AC_s3_delete", "Tools", "S3: Delete Artifact",
928+
fields=(FieldSpec("key", FieldType.STRING),),
929+
description="Delete an object from the default S3 store.",
930+
))
905931
specs.append(CommandSpec(
906932
"AC_generate_sop", "Report", "Generate SOP Document",
907933
fields=(
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
"""S3-compatible artifact store for reports, screenshots, and recordings."""
2+
from je_auto_control.utils.artifact_store.s3_store import (
3+
S3ArtifactStore, configure_default_store, get_default_store,
4+
set_default_store,
5+
)
6+
7+
__all__ = [
8+
"S3ArtifactStore", "configure_default_store", "get_default_store",
9+
"set_default_store",
10+
]

0 commit comments

Comments
 (0)