From 1022e8f2fe7c1d569312039e8b6642e7bb140fd9 Mon Sep 17 00:00:00 2001 From: chuanxu742 Date: Fri, 4 Sep 2026 17:52:54 +0100 Subject: [PATCH] feat(browser-act): enable ecommerce packs --- .../ecommerce-listing/channel.manifest.json | 15 ++++++ .../channel.manifest.json | 14 ++++++ .../ecommerce-reviews/channel.manifest.json | 15 ++++++ .../channel.manifest.json | 14 ++++++ .../goofish-item-detail/channel.manifest.json | 15 ++++++ .../goofish-search-list/channel.manifest.json | 14 ++++++ .../channel.manifest.json | 14 ++++++ .../channel.manifest.json | 14 ++++++ .../taobao-shop-catalog/channel.manifest.json | 14 ++++++ .../integration/test_browser_act_packs_api.py | 9 ++++ .../test_ecommerce_manifests.py | 50 +++++++++++++++++++ 11 files changed, 188 insertions(+) create mode 100644 backend/browser_act_packs/ecommerce/ecommerce-listing/channel.manifest.json create mode 100644 backend/browser_act_packs/ecommerce/ecommerce-product-detail/channel.manifest.json create mode 100644 backend/browser_act_packs/ecommerce/ecommerce-reviews/channel.manifest.json create mode 100644 backend/browser_act_packs/ecommerce/ecommerce-seller-info/channel.manifest.json create mode 100644 backend/browser_act_packs/ecommerce/goofish-item-detail/channel.manifest.json create mode 100644 backend/browser_act_packs/ecommerce/goofish-search-list/channel.manifest.json create mode 100644 backend/browser_act_packs/ecommerce/taobao-product-detail/channel.manifest.json create mode 100644 backend/browser_act_packs/ecommerce/taobao-product-reviews/channel.manifest.json create mode 100644 backend/browser_act_packs/ecommerce/taobao-shop-catalog/channel.manifest.json create mode 100644 tests/unit/browser_act_packs/test_ecommerce_manifests.py diff --git a/backend/browser_act_packs/ecommerce/ecommerce-listing/channel.manifest.json b/backend/browser_act_packs/ecommerce/ecommerce-listing/channel.manifest.json new file mode 100644 index 00000000..6a1d7008 --- /dev/null +++ b/backend/browser_act_packs/ecommerce/ecommerce-listing/channel.manifest.json @@ -0,0 +1,15 @@ +{ + "domain": "ecommerce", + "capability": "ecommerce-listing", + "param_schema": [ + {"name": "url", "required": true}, + {"name": "max_results", "required": false, "default": "20"} + ], + "steps": [ + {"op": "navigate", "url_template": "{url}"}, + {"op": "wait", "wait_mode": "stable"}, + {"op": "eval_script", "script": "scripts/extract-listing.py", "args": ["--max-results", "{max_results}"]} + ], + "pagination": {"mode": "none"}, + "success": {"min_count": 1, "required_field": "url"} +} diff --git a/backend/browser_act_packs/ecommerce/ecommerce-product-detail/channel.manifest.json b/backend/browser_act_packs/ecommerce/ecommerce-product-detail/channel.manifest.json new file mode 100644 index 00000000..3504edb4 --- /dev/null +++ b/backend/browser_act_packs/ecommerce/ecommerce-product-detail/channel.manifest.json @@ -0,0 +1,14 @@ +{ + "domain": "ecommerce", + "capability": "ecommerce-product-detail", + "param_schema": [ + {"name": "url", "required": true} + ], + "steps": [ + {"op": "navigate", "url_template": "{url}"}, + {"op": "wait", "wait_mode": "stable"}, + {"op": "eval_script", "script": "scripts/extract-product.py", "args": []} + ], + "pagination": {"mode": "none"}, + "success": {"min_count": 1, "required_field": "name"} +} diff --git a/backend/browser_act_packs/ecommerce/ecommerce-reviews/channel.manifest.json b/backend/browser_act_packs/ecommerce/ecommerce-reviews/channel.manifest.json new file mode 100644 index 00000000..0f3a197c --- /dev/null +++ b/backend/browser_act_packs/ecommerce/ecommerce-reviews/channel.manifest.json @@ -0,0 +1,15 @@ +{ + "domain": "ecommerce", + "capability": "ecommerce-reviews", + "param_schema": [ + {"name": "url", "required": true}, + {"name": "max_reviews", "required": false, "default": "20"} + ], + "steps": [ + {"op": "navigate", "url_template": "{url}"}, + {"op": "wait", "wait_mode": "stable"}, + {"op": "eval_script", "script": "scripts/extract-reviews.py", "args": ["--max-reviews", "{max_reviews}"]} + ], + "pagination": {"mode": "none"}, + "success": {"min_count": 1, "required_field": "body"} +} diff --git a/backend/browser_act_packs/ecommerce/ecommerce-seller-info/channel.manifest.json b/backend/browser_act_packs/ecommerce/ecommerce-seller-info/channel.manifest.json new file mode 100644 index 00000000..66a5c60c --- /dev/null +++ b/backend/browser_act_packs/ecommerce/ecommerce-seller-info/channel.manifest.json @@ -0,0 +1,14 @@ +{ + "domain": "ecommerce", + "capability": "ecommerce-seller-info", + "param_schema": [ + {"name": "url", "required": true} + ], + "steps": [ + {"op": "navigate", "url_template": "{url}"}, + {"op": "wait", "wait_mode": "stable"}, + {"op": "eval_script", "script": "scripts/extract-seller.py", "args": []} + ], + "pagination": {"mode": "none"}, + "success": {"min_count": 1, "required_field": "name"} +} diff --git a/backend/browser_act_packs/ecommerce/goofish-item-detail/channel.manifest.json b/backend/browser_act_packs/ecommerce/goofish-item-detail/channel.manifest.json new file mode 100644 index 00000000..f8a70634 --- /dev/null +++ b/backend/browser_act_packs/ecommerce/goofish-item-detail/channel.manifest.json @@ -0,0 +1,15 @@ +{ + "domain": "ecommerce", + "capability": "goofish-item-detail", + "param_schema": [ + {"name": "item_id", "required": true}, + {"name": "category_id", "required": false, "default": ""} + ], + "steps": [ + {"op": "navigate", "url_template": "https://www.goofish.com/item?id={item_id}&categoryId={category_id}"}, + {"op": "wait", "wait_mode": "stable"}, + {"op": "eval_script", "script": "scripts/extract-item-detail.py", "args": []} + ], + "pagination": {"mode": "none"}, + "success": {"min_count": 1, "required_field": "item_id"} +} diff --git a/backend/browser_act_packs/ecommerce/goofish-search-list/channel.manifest.json b/backend/browser_act_packs/ecommerce/goofish-search-list/channel.manifest.json new file mode 100644 index 00000000..edf0cf49 --- /dev/null +++ b/backend/browser_act_packs/ecommerce/goofish-search-list/channel.manifest.json @@ -0,0 +1,14 @@ +{ + "domain": "ecommerce", + "capability": "goofish-search-list", + "param_schema": [ + {"name": "keyword", "required": true} + ], + "steps": [ + {"op": "navigate", "url_template": "https://www.goofish.com/search?q={keyword}"}, + {"op": "wait", "wait_mode": "stable"}, + {"op": "eval_script", "script": "scripts/extract-search-items.py", "args": []} + ], + "pagination": {"mode": "none"}, + "success": {"min_count": 1, "required_field": "item_id"} +} diff --git a/backend/browser_act_packs/ecommerce/taobao-product-detail/channel.manifest.json b/backend/browser_act_packs/ecommerce/taobao-product-detail/channel.manifest.json new file mode 100644 index 00000000..6a30f35c --- /dev/null +++ b/backend/browser_act_packs/ecommerce/taobao-product-detail/channel.manifest.json @@ -0,0 +1,14 @@ +{ + "domain": "ecommerce", + "capability": "taobao-product-detail", + "param_schema": [ + {"name": "item_id", "required": true} + ], + "steps": [ + {"op": "navigate", "url_template": "https://item.taobao.com/item.htm?id={item_id}"}, + {"op": "wait", "wait_mode": "stable"}, + {"op": "eval_script", "script": "scripts/extract-product.py", "args": ["{item_id}"]} + ], + "pagination": {"mode": "none"}, + "success": {"min_count": 1, "required_field": "title"} +} diff --git a/backend/browser_act_packs/ecommerce/taobao-product-reviews/channel.manifest.json b/backend/browser_act_packs/ecommerce/taobao-product-reviews/channel.manifest.json new file mode 100644 index 00000000..2496d520 --- /dev/null +++ b/backend/browser_act_packs/ecommerce/taobao-product-reviews/channel.manifest.json @@ -0,0 +1,14 @@ +{ + "domain": "ecommerce", + "capability": "taobao-product-reviews", + "param_schema": [ + {"name": "item_id", "required": true} + ], + "steps": [ + {"op": "navigate", "url_template": "https://item.taobao.com/item.htm?id={item_id}"}, + {"op": "wait", "wait_mode": "stable"}, + {"op": "eval_script", "script": "scripts/extract-reviews.py", "args": ["{item_id}"]} + ], + "pagination": {"mode": "none"}, + "success": {"min_count": 1, "required_field": "username"} +} diff --git a/backend/browser_act_packs/ecommerce/taobao-shop-catalog/channel.manifest.json b/backend/browser_act_packs/ecommerce/taobao-shop-catalog/channel.manifest.json new file mode 100644 index 00000000..f70c8089 --- /dev/null +++ b/backend/browser_act_packs/ecommerce/taobao-shop-catalog/channel.manifest.json @@ -0,0 +1,14 @@ +{ + "domain": "ecommerce", + "capability": "taobao-shop-catalog", + "param_schema": [ + {"name": "shop_id", "required": true} + ], + "steps": [ + {"op": "navigate", "url_template": "https://shop{shop_id}.taobao.com/category.htm?search=y&pageNo={page}"}, + {"op": "wait", "wait_mode": "stable", "selector": "[id^=\"item_id_\"]"}, + {"op": "eval_script", "script": "scripts/extract-catalog.py", "args": ["{shop_id}", "--page", "{page}"]} + ], + "pagination": {"mode": "url_page", "url_template": "https://shop{shop_id}.taobao.com/category.htm?search=y&pageNo={page}", "page_param": "pageNo", "stop_when": "result_count < 1"}, + "success": {"min_count": 1, "required_field": "itemId"} +} diff --git a/tests/integration/test_browser_act_packs_api.py b/tests/integration/test_browser_act_packs_api.py index 5f0d7a09..69bec1e6 100644 --- a/tests/integration/test_browser_act_packs_api.py +++ b/tests/integration/test_browser_act_packs_api.py @@ -7,7 +7,16 @@ import pytest SEEDED_WITH_MANIFEST = { + "ecommerce/ecommerce-listing", + "ecommerce/ecommerce-product-detail", + "ecommerce/ecommerce-reviews", + "ecommerce/ecommerce-seller-info", + "ecommerce/goofish-item-detail", + "ecommerce/goofish-search-list", "ecommerce/taobao-keyword-search", + "ecommerce/taobao-product-detail", + "ecommerce/taobao-product-reviews", + "ecommerce/taobao-shop-catalog", "search-research/google-search-serp", } diff --git a/tests/unit/browser_act_packs/test_ecommerce_manifests.py b/tests/unit/browser_act_packs/test_ecommerce_manifests.py new file mode 100644 index 00000000..74e523a0 --- /dev/null +++ b/tests/unit/browser_act_packs/test_ecommerce_manifests.py @@ -0,0 +1,50 @@ +"""Contract checks for executable e-commerce Browser Act packs.""" + +from pathlib import Path + +import pytest + +from backend.browser_act_packs.catalog import PackCatalog +from backend.browser_act_packs.manifest import load_manifest + +_PACKS = { + "ecommerce-listing": ("scripts/extract-listing.py", "url"), + "ecommerce-product-detail": ("scripts/extract-product.py", "name"), + "ecommerce-reviews": ("scripts/extract-reviews.py", "body"), + "ecommerce-seller-info": ("scripts/extract-seller.py", "name"), + "goofish-item-detail": ("scripts/extract-item-detail.py", "item_id"), + "goofish-search-list": ("scripts/extract-search-items.py", "item_id"), + "taobao-product-detail": ("scripts/extract-product.py", "title"), + "taobao-product-reviews": ("scripts/extract-reviews.py", "username"), + "taobao-shop-catalog": ("scripts/extract-catalog.py", "itemId"), +} + + +@pytest.mark.parametrize("capability, expected", sorted(_PACKS.items())) +def test_ecommerce_pack_manifest_is_executable( + capability: str, expected: tuple[str, str] +) -> None: + script_name, required_field = expected + pack_dir = PackCatalog().root / "ecommerce" / capability + manifest = load_manifest(pack_dir / "channel.manifest.json") + + assert manifest.domain == "ecommerce" + assert manifest.capability == capability + assert manifest.success.required_field == required_field + eval_steps = [step for step in manifest.steps if step.op == "eval_script"] + assert len(eval_steps) == 1 + assert eval_steps[0].script == script_name + assert (pack_dir / script_name).is_file() + + +def test_ecommerce_manifests_use_only_supported_step_operations() -> None: + supported = {"navigate", "wait", "eval_script", "click", "input"} + for capability in _PACKS: + manifest_path = ( + Path(PackCatalog().root) + / "ecommerce" + / capability + / "channel.manifest.json" + ) + manifest = load_manifest(manifest_path) + assert {step.op for step in manifest.steps} <= supported