diff --git a/CHANGELOG.md b/CHANGELOG.md index fa2589b..e8607a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,11 @@ found and `AppConfig.resources_dir` names the folder; the startup log reports `Configuration: `. **Upgrade note:** an application that kept `resources/` in the working directory with its application file elsewhere passes `--config`, or moves the folder next to the file. +2. **The LLM helper graduates from an example to an extension: `extensions/llm-helper/`.** It is no longer a sample to copy + but a function host an installation runs to give its engines an LLM, so it moves out of `examples/`, which keeps the + demo app. The files, the routes, the contract and the configuration keys are unchanged; the sdist ships the new folder. + **Upgrade note:** start it as `mercury-serve extensions/llm-helper/llm_helper.py` (or + `python extensions/llm-helper/llm_helper.py`); a script or a deployment that names the old path changes it. ## Version 4.12.21, 10/7/2026 diff --git a/docs/test-reports/llm-helper-certification.md b/docs/test-reports/llm-helper-certification.md index 0823dc8..dfcba78 100644 --- a/docs/test-reports/llm-helper-certification.md +++ b/docs/test-reports/llm-helper-certification.md @@ -14,7 +14,7 @@ keywords: [llm helper, llm.chat, llm.stream, claude, anthropic, streaming, certi pairs, the three layers, the progressive-rendering evidence and the findings — is the [engine report](https://accenture.github.io/mercury-composable/test-reports/llm-helper-certification/), which the Rust engine's docs carry too. The helper is -[`examples/llm-helper`](https://github.com/Accenture/mercury-python/tree/main/examples/llm-helper); its README holds the contract.* +[`extensions/llm-helper`](https://github.com/Accenture/mercury-python/tree/main/extensions/llm-helper); its README holds the contract.* ## Verified without a credential @@ -100,7 +100,7 @@ model generation. The full account of the surfaces and the gates is Finding 7 of pip install -e '.[dev]' pytest tests/test_llm_helper.py # token-free export ANTHROPIC_API_KEY=... # live: the credential reaches the helper only -mercury-serve examples/llm-helper/llm_helper.py -Dlog.format=compact -Dllm.log.batches=true +mercury-serve extensions/llm-helper/llm_helper.py -Dlog.format=compact -Dllm.log.batches=true ``` The commands for the engines, the deploy folder and the `curl` calls for each layer are in the diff --git a/examples/demo-app/README.md b/examples/demo-app/README.md index 4763534..54cb243 100644 --- a/examples/demo-app/README.md +++ b/examples/demo-app/README.md @@ -3,7 +3,7 @@ The minimal polyglot function host: seven small functions that show what a Mercury engine can reach over Event-over-HTTP and what a function host does with a call. Run it to watch a Python function answer an engine, and copy it as the starting point for your own app. It never calls a model and needs -no credential; the LLM routes are a separate app, [`llm-helper`](../llm-helper/README.md). +no credential; the LLM routes are a separate app, [`llm-helper`](../../extensions/llm-helper/README.md). ```text caller ── REST ──> Java or Rust engine ── Event-over-HTTP ──> demo app @@ -26,7 +26,7 @@ Either way the app reads `resources/application.yml` next to `demo_app.py`, whic command runs from. The app listens on port 8086 (`rest.server.port` in `resources/application.yml`; `-Drest.server.port=8090` -overrides it). The [LLM helper](../llm-helper/README.md) defaults to 8086 as well, so give one of the two +overrides it). The [LLM helper](../../extensions/llm-helper/README.md) defaults to 8086 as well, so give one of the two another port when you run both on one machine. Map a route from the engine's `event-over-http.yaml`: ```yaml diff --git a/examples/llm-helper/README.md b/extensions/llm-helper/README.md similarity index 98% rename from examples/llm-helper/README.md rename to extensions/llm-helper/README.md index 6b52aca..1450b41 100644 --- a/examples/llm-helper/README.md +++ b/extensions/llm-helper/README.md @@ -20,8 +20,8 @@ speaks the same contract. Both run one shared vector file, so they cannot drift ```bash pip install 'mercury-composable[llm]' # the SDK is this app's one dependency export ANTHROPIC_API_KEY=... # from the environment, never from a config file -mercury-serve examples/llm-helper/llm_helper.py -python examples/llm-helper/llm_helper.py # the same without mercury-serve +mercury-serve extensions/llm-helper/llm_helper.py +python extensions/llm-helper/llm_helper.py # the same without mercury-serve ``` Either way the helper reads `resources/application.yml` next to `llm_helper.py`, whichever directory the @@ -33,7 +33,7 @@ AWS credential chain, and a region is required: ```bash pip install 'mercury-composable[llm,bedrock]' export AWS_REGION=us-east-1 # or llm.aws.region; the AWS credential chain signs each call -mercury-serve examples/llm-helper/llm_helper.py -Dllm.backend=bedrock +mercury-serve extensions/llm-helper/llm_helper.py -Dllm.backend=bedrock ``` If the account is enabled for Bedrock's classic runtime and not for its Anthropic-shaped endpoint (the default @@ -41,7 +41,7 @@ surface answers `403 ... not available for this account` on a model the console' surface and name the model as an inference profile id, exactly as the account lists it: ```bash -mercury-serve examples/llm-helper/llm_helper.py -Dllm.backend=bedrock -Dllm.aws.api=runtime \ +mercury-serve extensions/llm-helper/llm_helper.py -Dllm.backend=bedrock -Dllm.aws.api=runtime \ -Dllm.model=us.anthropic.claude-haiku-4-5-20251001-v1:0 ``` diff --git a/examples/llm-helper/llm_helper.py b/extensions/llm-helper/llm_helper.py similarity index 99% rename from examples/llm-helper/llm_helper.py rename to extensions/llm-helper/llm_helper.py index 4495d12..90df2de 100644 --- a/examples/llm-helper/llm_helper.py +++ b/extensions/llm-helper/llm_helper.py @@ -6,7 +6,7 @@ control flow while the LLM advises within it. Run: pip install 'mercury-composable[llm]' - mercury-serve examples/llm-helper/llm_helper.py + mercury-serve extensions/llm-helper/llm_helper.py The credential comes from the environment, never from a config file: ANTHROPIC_API_KEY for the Claude API, the AWS credential chain (or AWS_BEARER_TOKEN_BEDROCK) for AWS Bedrock. diff --git a/examples/llm-helper/resources/application.yml b/extensions/llm-helper/resources/application.yml similarity index 97% rename from examples/llm-helper/resources/application.yml rename to extensions/llm-helper/resources/application.yml index fe1e322..f9ac7af 100644 --- a/examples/llm-helper/resources/application.yml +++ b/extensions/llm-helper/resources/application.yml @@ -3,7 +3,7 @@ # resources folder next to the application file (this one), never from the working directory. # Any key can be overridden at # run time with the engines' -D syntax, e.g.: -# mercury-serve examples/llm-helper/llm_helper.py -Dllm.model=claude-haiku-4-5 +# mercury-serve extensions/llm-helper/llm_helper.py -Dllm.model=claude-haiku-4-5 application.name: 'llm-helper' info.app.description: 'Mercury Composable LLM helper (llm.chat / llm.stream on the Anthropic SDK)' diff --git a/memory/instructions.md b/memory/instructions.md index 63a5a8c..237c4be 100644 --- a/memory/instructions.md +++ b/memory/instructions.md @@ -27,8 +27,9 @@ the engines by design. The legacy pre-composable language pack lives in git hist `cli.py` (`mercury-serve`); `py.typed` ships type information. - `tests/` — pytest suite; `tests/vectors/vectors.json` holds the **golden conformance vectors shared with the Java and Rust engines** (the wire-compatibility proof). -- `examples/demo-app/demo_app.py` — the minimal runnable function app (README and `resources/` beside it); - `examples/llm-helper/llm_helper.py` — the LLM helper app (`llm.chat`, `llm.stream`, `llm.health` on the Anthropic SDK). +- `examples/demo-app/demo_app.py` — the minimal runnable function app (README and `resources/` beside it). +- `extensions/llm-helper/llm_helper.py` — the LLM helper (`llm.chat`, `llm.stream`, `llm.health` on the + Anthropic SDK), graduated from `examples/` to an extension on 2026-11-02 (Eric). - Root `README.md` — the consumer-facing guide (quick start, function contract, configuration, wire compatibility); the root `AGENTS.md` fork routes consumers there. diff --git a/pyproject.toml b/pyproject.toml index 74f170d..fcce8bf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,7 @@ Repository = "https://github.com/Accenture/mercury-python" Changelog = "https://github.com/Accenture/mercury-python/blob/main/CHANGELOG.md" [project.optional-dependencies] -# the LLM helper app (examples/llm-helper) - the package itself stays SDK-free +# the LLM helper (extensions/llm-helper) - the package itself stays SDK-free llm = ["anthropic>=1,<2"] # AWS Bedrock as the helper's backend (llm.backend: bedrock) - boto3 signs each call with the # AWS credential chain; the Claude API install stays lean without it @@ -58,6 +58,7 @@ only-include = [ "src/mercury_composable", "tests", "examples", + "extensions", "README.md", "CHANGELOG.md", "LICENSE.txt", @@ -81,7 +82,7 @@ extend-select = ["I"] # by agent-memory and excluded. typeCheckingMode = "standard" reportMissingParameterType = "error" -include = ["src", "examples", "tests"] +include = ["src", "examples", "extensions", "tests"] exclude = ["agent-skills", ".venv"] [tool.pytest.ini_options] diff --git a/system/AGENTS.md b/system/AGENTS.md index 79396d9..fe8405c 100644 --- a/system/AGENTS.md +++ b/system/AGENTS.md @@ -34,6 +34,7 @@ wrapper's docs cover only the Python function surface. |------|------| | `src/` | the `mercury_composable` package — Event API host, `PostOffice`, envelope codec, actuators | | `examples/` | runnable reference functions | +| `extensions/` | function hosts that add a capability to an installation — the LLM helper (`llm.chat`, `llm.stream`) | | `docs/` | the MkDocs guide site source | | `tests/` | unit and integration tests | diff --git a/tests/test_llm_helper.py b/tests/test_llm_helper.py index eb954b4..44f5c34 100644 --- a/tests/test_llm_helper.py +++ b/tests/test_llm_helper.py @@ -1,4 +1,4 @@ -"""The LLM helper (examples/llm-helper/llm_helper.py) - token-free. +"""The LLM helper (extensions/llm-helper/llm_helper.py) - token-free. One shared contract file, tests/vectors/llm-helper-vectors.json (byte-identical in mercury-nodejs), drives every case below against a fake of the Anthropic SDK: the exact SDK @@ -29,7 +29,7 @@ from mercury_composable import AppException _TESTS = Path(__file__).resolve().parent -_HELPER = _TESTS.parent / "examples" / "llm-helper" / "llm_helper.py" +_HELPER = _TESTS.parent / "extensions" / "llm-helper" / "llm_helper.py" _VECTORS = _TESTS / "vectors" / "llm-helper-vectors.json" # the Node.js twin pins the same digest: change the file in both packs or in neither _VECTORS_SHA256 = "7bfffbf36c52488c246f250f36afc4e8012f119fd56c93e9de1cbe93bf5988b6"