From 487a4e08ff320ce0344b9d3695ad9c63fa68b857 Mon Sep 17 00:00:00 2001 From: "molecule-ai[bot]" <276602405+molecule-ai[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 16:03:50 +0000 Subject: [PATCH] refactor(#741): extract medo.py from builtin_tools to plugins/molecule-medo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Baidu MeDo hackathon integration was sitting in builtin_tools/ as dead code — not imported by any loader but shipped with every workspace image, misleadingly suggesting it was a core builtin. Changes: - Move builtin_tools/medo.py → plugins/molecule-medo/skills/medo-tools/scripts/medo.py (git detects this as a rename — no code changes, identical tool surface) - Add plugins/molecule-medo/plugin.yaml (manifest: name, version, runtimes, tags) - Add plugins/molecule-medo/skills/medo-tools/SKILL.md (frontmatter + setup docs) - Move workspace-template/tests/test_medo.py → plugins/molecule-medo/tests/test_medo.py (update _MEDO_PATH to resolve from plugin root; add conftest.py for langchain mock) - Update .gitignore: change /plugins/ blanket ignore to /plugins/* so this plugin can be tracked until it gets its own standalone repo Acceptance criteria met: - builtin_tools/medo.py removed from core - plugins/molecule-medo/ created with identical tool surface (9/9 tests pass) - cd workspace-template && pytest → 1021 passed, 2 xfailed (no regression) - MEDO_API_KEY was never in default provisioning (.env.example / config.py clean) Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 4 ++- plugins/molecule-medo/plugin.yaml | 6 +++++ .../molecule-medo/skills/medo-tools/SKILL.md | 27 +++++++++++++++++++ .../skills/medo-tools/scripts}/medo.py | 2 +- plugins/molecule-medo/tests/conftest.py | 21 +++++++++++++++ .../molecule-medo}/tests/test_medo.py | 24 +++++++---------- 6 files changed, 68 insertions(+), 16 deletions(-) create mode 100644 plugins/molecule-medo/plugin.yaml create mode 100644 plugins/molecule-medo/skills/medo-tools/SKILL.md rename {workspace-template/builtin_tools => plugins/molecule-medo/skills/medo-tools/scripts}/medo.py (98%) create mode 100644 plugins/molecule-medo/tests/conftest.py rename {workspace-template => plugins/molecule-medo}/tests/test_medo.py (73%) diff --git a/.gitignore b/.gitignore index a3a4a2a1..a5374468 100644 --- a/.gitignore +++ b/.gitignore @@ -125,5 +125,7 @@ org-templates/**/.auth-token # Cloned-via-manifest dirs — populated locally by scripts/clone-manifest.sh, # tracked in their own standalone repos. Never commit to core. /org-templates/ -/plugins/ +/plugins/* +# Exception: molecule-medo lives here until it gets its own standalone repo. +!/plugins/molecule-medo/ /workspace-configs-templates/ diff --git a/plugins/molecule-medo/plugin.yaml b/plugins/molecule-medo/plugin.yaml new file mode 100644 index 00000000..74adce13 --- /dev/null +++ b/plugins/molecule-medo/plugin.yaml @@ -0,0 +1,6 @@ +name: molecule-medo +version: 0.1.0 +description: Baidu MeDo no-code AI platform integration (hackathon / China-region) +author: Molecule AI +tags: [hackathon, baidu, medo, china] +runtimes: [claude_code, deepagents, langgraph] diff --git a/plugins/molecule-medo/skills/medo-tools/SKILL.md b/plugins/molecule-medo/skills/medo-tools/SKILL.md new file mode 100644 index 00000000..a8fdd8c8 --- /dev/null +++ b/plugins/molecule-medo/skills/medo-tools/SKILL.md @@ -0,0 +1,27 @@ +--- +name: MeDo Tools +description: > + Create, update, and publish applications on Baidu MeDo (摩搭), a no-code AI + application builder. Used in the Molecule AI hackathon integration (May 2026). +tags: [hackathon, baidu, medo, china, no-code] +examples: + - "Create a chatbot app on MeDo called 'Customer Support'" + - "Update the content of my MeDo app abc123" + - "Publish my MeDo app to production" +--- + +# MeDo Tools + +Provides three tools for interacting with the Baidu MeDo no-code platform: + +- **create_medo_app** — Scaffold a new application from a template (blank, chatbot, form, dashboard). +- **update_medo_app** — Push content or configuration changes to an existing application. +- **publish_medo_app** — Publish a draft application to production or staging. + +## Setup + +Set `MEDO_API_KEY` as a workspace secret. Optionally override the base URL via `MEDO_BASE_URL` +(default: `https://api.moda.baidu.com/v1`). + +When `MEDO_API_KEY` is absent the tools run in mock mode and return stub responses — safe for +local development and testing. diff --git a/workspace-template/builtin_tools/medo.py b/plugins/molecule-medo/skills/medo-tools/scripts/medo.py similarity index 98% rename from workspace-template/builtin_tools/medo.py rename to plugins/molecule-medo/skills/medo-tools/scripts/medo.py index 0c824f91..ddf53271 100644 --- a/workspace-template/builtin_tools/medo.py +++ b/plugins/molecule-medo/skills/medo-tools/scripts/medo.py @@ -1,4 +1,4 @@ -"""MeDo builtin tools — Baidu MeDo no-code AI platform integration. +"""MeDo tools — Baidu MeDo no-code AI platform integration. MeDo (摩搭, moda.baidu.com) is Baidu's no-code AI application builder used in the Molecule AI hackathon integration (May 2026). Three core operations: diff --git a/plugins/molecule-medo/tests/conftest.py b/plugins/molecule-medo/tests/conftest.py new file mode 100644 index 00000000..413c2298 --- /dev/null +++ b/plugins/molecule-medo/tests/conftest.py @@ -0,0 +1,21 @@ +"""Minimal conftest for molecule-medo plugin tests. + +langchain_core is a declared dependency of workspace-template (>=0.3.0) and +is expected to be present in the test environment. If it is absent, mock it +so the @tool decorator in medo.py is a no-op and the tests can still run. +""" + +import sys +from types import ModuleType + + +def _mock_langchain_if_missing(): + if "langchain_core" not in sys.modules: + lc_mod = ModuleType("langchain_core") + lc_tools_mod = ModuleType("langchain_core.tools") + lc_tools_mod.tool = lambda f: f # @tool becomes identity decorator + sys.modules["langchain_core"] = lc_mod + sys.modules["langchain_core.tools"] = lc_tools_mod + + +_mock_langchain_if_missing() diff --git a/workspace-template/tests/test_medo.py b/plugins/molecule-medo/tests/test_medo.py similarity index 73% rename from workspace-template/tests/test_medo.py rename to plugins/molecule-medo/tests/test_medo.py index 1dfe09b1..301e8d7b 100644 --- a/workspace-template/tests/test_medo.py +++ b/plugins/molecule-medo/tests/test_medo.py @@ -1,16 +1,11 @@ -"""Tests for workspace-template/builtin_tools/medo.py. +"""Tests for plugins/molecule-medo/skills/medo-tools/scripts/medo.py. All tests exercise the mock backend (no MEDO_API_KEY required). -NOTE: conftest.py mocks builtin_tools with __path__=[] and mocks -langchain_core.tools.tool as a no-op (lambda f: f) so adapters can be -imported without heavy deps. Consequence: direct package import of -builtin_tools.medo is blocked (empty __path__ prevents filesystem -lookup), and @tool returns the raw async function rather than a LangChain -StructuredTool — so .ainvoke() is unavailable. - -Fix: load medo.py via importlib (bypasses the mock package root) and -call functions directly, not via .ainvoke(). +NOTE: @tool is a LangChain decorator that returns a StructuredTool rather than +the raw async function. conftest.py mocks langchain_core.tools.tool as an +identity decorator so that calling the functions directly (without .ainvoke()) +works in tests — matching the original test approach. """ import importlib.util @@ -19,14 +14,15 @@ from pathlib import Path import pytest -ROOT = Path(__file__).resolve().parents[1] -_MEDO_PATH = ROOT / "builtin_tools" / "medo.py" +# plugin root: plugins/molecule-medo/ +_PLUGIN_ROOT = Path(__file__).resolve().parents[1] +_MEDO_PATH = _PLUGIN_ROOT / "skills" / "medo-tools" / "scripts" / "medo.py" def _load_medo(): - spec = importlib.util.spec_from_file_location("builtin_tools.medo", _MEDO_PATH) + spec = importlib.util.spec_from_file_location("medo_plugin_tools", _MEDO_PATH) mod = importlib.util.module_from_spec(spec) - sys.modules["builtin_tools.medo"] = mod # register before exec to handle self-refs + sys.modules["medo_plugin_tools"] = mod # register before exec to handle self-refs spec.loader.exec_module(mod) return mod