hermes-mai-image 0.2.0__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.
@@ -0,0 +1,10 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: github-actions
4
+ directory: /
5
+ schedule:
6
+ interval: weekly
7
+ - package-ecosystem: pip
8
+ directory: /
9
+ schedule:
10
+ interval: weekly
@@ -0,0 +1,66 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ test:
14
+ runs-on: ubuntu-latest
15
+ strategy:
16
+ matrix:
17
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
18
+ steps:
19
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
20
+ - name: Set up Python
21
+ uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
22
+ with:
23
+ python-version: ${{ matrix.python-version }}
24
+ allow-prereleases: true
25
+ - name: Install test dependencies
26
+ run: python -m pip install -e '.[test,lint]'
27
+ - name: Lint
28
+ run: ruff check src tests
29
+ - name: Run tests
30
+ run: python -m pytest -q
31
+ - name: Compile Python
32
+ run: python -m compileall -q src tests
33
+ - name: Build wheel
34
+ run: python -m pip wheel --no-deps --wheel-dir dist .
35
+ - name: Verify plugin.yaml is in the wheel
36
+ run: python -m zipfile -l dist/*.whl | grep plugin.yaml
37
+
38
+ catalog-validation:
39
+ # Validates the plugin against Hermes' own plugin validator, which is the gate the
40
+ # Hermes plugin catalog applies. Hermes must be installed for this to run, so it is a
41
+ # separate job that pins a Hermes version rather than pulling it into the test matrix.
42
+ #
43
+ # The plugin manifest lives at src/hermes_mai_image/plugin.yaml, so the validator must
44
+ # target that directory. Validating the repository root fails with
45
+ # "no plugin.yaml (or portable plugin.json) in the plugin directory".
46
+ #
47
+ # Reproduce locally with the Hermes CLI on PATH:
48
+ # hermes plugins validate src/hermes_mai_image
49
+ runs-on: ubuntu-latest
50
+ steps:
51
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
52
+ - name: Set up Python
53
+ uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
54
+ with:
55
+ python-version: "3.12"
56
+ - name: Install Hermes (pinned release tag)
57
+ # Pinned so catalog validation is deterministic; bump deliberately. Hermes is not
58
+ # installable from PyPI (the published hermes-agent is stale) and refuses wheel
59
+ # builds, so clone the release tag and install it editable, as Hermes itself does.
60
+ env:
61
+ HERMES_REF: v2026.9.24
62
+ run: |
63
+ git clone --depth 1 --branch "$HERMES_REF" https://github.com/NousResearch/hermes-agent.git "$RUNNER_TEMP/hermes-agent"
64
+ python -m pip install -e "$RUNNER_TEMP/hermes-agent"
65
+ - name: Validate plugin manifest
66
+ run: hermes plugins validate src/hermes_mai_image
@@ -0,0 +1,44 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ build:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
15
+ - name: Set up Python
16
+ uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
17
+ with:
18
+ python-version: "3.12"
19
+ - name: Build sdist and wheel
20
+ run: |
21
+ python -m pip install build twine
22
+ python -m build
23
+ - name: Check distribution metadata
24
+ run: python -m twine check --strict dist/*
25
+ - name: Verify plugin.yaml is in the wheel
26
+ run: python -m zipfile -l dist/*.whl | grep plugin.yaml
27
+ - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
28
+ with:
29
+ name: dist
30
+ path: dist/
31
+
32
+ publish:
33
+ needs: build
34
+ runs-on: ubuntu-latest
35
+ environment: pypi
36
+ permissions:
37
+ id-token: write # PyPI Trusted Publishing (OIDC); no stored token
38
+ steps:
39
+ - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
40
+ with:
41
+ name: dist
42
+ path: dist/
43
+ - name: Publish to PyPI
44
+ uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # v1.14.3
@@ -0,0 +1,16 @@
1
+ # Virtual environments
2
+ .venv/
3
+ venv/
4
+
5
+ # Build and packaging output
6
+ dist/
7
+ build/
8
+ *.egg-info/
9
+
10
+ # Python bytecode and caches
11
+ __pycache__/
12
+ *.py[cod]
13
+ .pytest_cache/
14
+
15
+ # Local environment files
16
+ .env
@@ -0,0 +1,128 @@
1
+ ---
2
+ name: hermes-mai-image
3
+ description: "Repository guidance for the standalone Hermes Agent Microsoft Foundry MAI image-generation plugin."
4
+ ---
5
+
6
+ # Hermes MAI Image Plugin
7
+
8
+ ## Purpose
9
+
10
+ This repository contains a standalone Hermes Agent image-generation provider for Microsoft Foundry MAI image models. It is distributed from GitHub and is designed to become a PyPI package later.
11
+
12
+ ## Repository layout
13
+
14
+ - `src/hermes_mai_image/provider.py` — provider implementation, model discovery, generation, editing, response handling, and cache integration.
15
+ - `src/hermes_mai_image/plugin.yaml` — Hermes plugin metadata.
16
+ - `src/hermes_mai_image/__init__.py` — public package exports and the module-level `register` hook.
17
+ - `tests/test_mai_image_plugin.py` — mocked HTTP contract tests; tests never use live Azure credentials.
18
+ - `README.md` — installation, configuration, Web UI, CLI, troubleshooting, and update instructions.
19
+ - `pyproject.toml` — package metadata, dependencies, test extra, and the `hermes_agent.plugins` entry point.
20
+
21
+ ## Hermes plugin contract
22
+
23
+ The package must keep this entry point format:
24
+
25
+ ```toml
26
+ [project.entry-points."hermes_agent.plugins"]
27
+ azure-mai-image = "hermes_mai_image"
28
+ ```
29
+
30
+ Hermes discovers this provider as a module import. Do not change it to `hermes_mai_image:register` without verifying Hermes' provider discovery contract; that target is treated differently by Hermes and previously caused the provider not to appear in `hermes tools`.
31
+
32
+ The provider identity is:
33
+
34
+ - Name: `azure-mai`
35
+ - Display name: `Microsoft Foundry MAI`
36
+ - Plugin key: `azure-mai-image`
37
+
38
+ The module-level `register(ctx)` function calls `ctx.register_image_gen_provider(MAIImageProvider())`.
39
+
40
+ ## API contract
41
+
42
+ Use Microsoft Foundry's native MAI API, not the standard OpenAI image API:
43
+
44
+ - Generation: `POST {endpoint}/mai/v1/images/generations`
45
+ - Editing: `POST {endpoint}/mai/v1/images/edits`
46
+ - Authentication: `api-key` request header
47
+ - Generation JSON fields: `model`, `prompt`, `width`, `height`
48
+ - Editing: multipart form fields `model`, `prompt`, and `image`
49
+
50
+ The configured endpoint is the resource root, such as `https://resource.services.ai.azure.com`. The provider appends `/mai/v1`; never require users to enter that suffix.
51
+
52
+ Required credentials/settings:
53
+
54
+ - `MAI_FOUNDRY_API_KEY` — secret; store in the active Hermes profile `.env` only.
55
+ - `MAI_FOUNDRY_ENDPOINT` — Foundry resource root.
56
+ - `MAI_IMAGE_MODEL` — optional deployment/model override; the exact Foundry deployment name is preferred.
57
+ - `MAI_FOUNDRY_MODELS_PATH` — optional discovery path override, default `/models`.
58
+
59
+ Never place credentials in source, tests, `config.yaml`, README examples, commits, logs, or chat messages.
60
+
61
+ ## Development workflow
62
+
63
+ Work on a feature branch, not directly on `main`:
64
+
65
+ ```bash
66
+ git switch main
67
+ git pull --ff-only origin main
68
+ git switch -c feature/<short-description>
69
+ ```
70
+
71
+ Create a local environment and install test dependencies:
72
+
73
+ ```bash
74
+ python3 -m venv .venv
75
+ . .venv/bin/activate
76
+ pip install -e '.[test]'
77
+ ```
78
+
79
+ Run the focused suite:
80
+
81
+ ```bash
82
+ python -m pytest -q
83
+ python -m compileall -q src tests
84
+ ```
85
+
86
+ Build a wheel before packaging changes:
87
+
88
+ ```bash
89
+ python -m pip wheel --no-deps --wheel-dir dist .
90
+ ```
91
+
92
+ ## Testing rules
93
+
94
+ - Keep HTTP tests mocked; no test should require or transmit a real Azure key.
95
+ - Test request URLs, headers, payload shapes, multipart edit fields, response parsing, missing credentials, and cache materialization.
96
+ - Follow red-green-refactor for behavior changes: write a failing test first, confirm the expected failure, implement the smallest fix, then run the full suite.
97
+ - Do not weaken tests to accommodate an implementation error.
98
+
99
+ For live validation, use a separately configured Hermes profile and explicit user-provided credentials. Never add live credentials to test fixtures or CI.
100
+
101
+ ## Release and distribution
102
+
103
+ The primary installation path is the GitHub URL:
104
+
105
+ ```bash
106
+ pip install --upgrade "git+https://github.com/joescars/hermes-mai-image.git"
107
+ ```
108
+
109
+ After changing package metadata or provider code:
110
+
111
+ 1. Run tests, compilation, and wheel build.
112
+ 2. Review `git diff --check` and the staged file list.
113
+ 3. Commit with a focused message.
114
+ 4. Push the feature branch and open a PR unless the user explicitly asks to work directly on `main`.
115
+ 5. After merge, verify the GitHub default branch contains the commit.
116
+
117
+ If the Hermes Python virtual environment is replaced by a Hermes update, users may need to reinstall the package into the new environment. Profile credentials and configuration are separate and should not be deleted by a plugin release.
118
+
119
+ ## Documentation requirements
120
+
121
+ Update `README.md` when installation, configuration, endpoints, provider discovery, or update behavior changes. Keep the CLI and Web UI steps accurate. Explicitly state that the endpoint is the resource root and that `/mai/v1` is appended by the provider.
122
+
123
+ ## Safety boundaries
124
+
125
+ - Do not modify Hermes core for provider-specific behavior.
126
+ - Do not add unauthenticated live API calls to tests or build steps.
127
+ - Do not print API keys, authorization headers, or full credential-bearing URLs.
128
+ - Do not use destructive Git commands (`reset --hard`, unapproved stash, broad clean) to resolve unrelated local changes.
@@ -0,0 +1,71 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ _Nothing yet._
11
+
12
+ ## [0.2.0] - 2026-11-15
13
+
14
+ ### Added
15
+
16
+ - Opt-in MAI-Image-2.6 `auto_aspect_ratio` and `web_grounding` options (config keys under
17
+ `image_gen.azure-mai` or `MAI_AUTO_ASPECT_RATIO` / `MAI_WEB_GROUNDING`), sent on generation and
18
+ edits only when enabled and skipped with a warning on non-2.6 deployments; `web_grounding` is
19
+ disclosed in the README and catalog. (#22)
20
+ - Image edits accept up to five reference images (repeated multipart `image` fields, in order), with
21
+ per-source checks, a total upload cap, and rejection of more than five before any request (#21).
22
+ - Model picker lists the configured deployment first (de-duplicated, bare id) and labels fallback
23
+ entries as model names that must match your deployment name; README documents best-effort discovery and
24
+ `az` deployment listing. (#27)
25
+ - Endpoint normalization and validation: pasted `/mai/v1`, `/models`, `/openai/v1` and project URL
26
+ suffixes resolve to the resource root, bare hosts get `https://`, non-https endpoints fail with
27
+ `invalid_config` without a request, and model discovery is skipped without credentials.
28
+ - Credential resolution through the Hermes secret scope, so provider settings are read from the
29
+ active profile's secret store instead of ambient environment lookup.
30
+ - An in-plugin, SSRF-safe image downloader that validates reference image URLs with the published
31
+ `tools.url_safety.is_safe_url` before fetching them. Local reference files are still checked with
32
+ the private `agent.file_safety.raise_if_read_blocked` import (documented in the README; tracked by #7).
33
+ - A draft catalog entry under `catalog/` for submission to the Hermes plugin catalog.
34
+
35
+ ### Changed
36
+
37
+ - Source images stream into memory (no temp file), local files are size-checked via `stat` before reading, `MAX_REMOTE_IMAGE_BYTES` is renamed `MAX_SOURCE_IMAGE_BYTES`, and the contradictory `agent.file_safety` comment is fixed.
38
+
39
+
40
+ - CI hardening: ruff lint, Python 3.13/3.14 matrix, SHA-pinned actions, Dependabot, wheel `plugin.yaml` check, a tag-triggered PyPI Trusted Publishing release workflow, and SPDX license metadata (#29).
41
+ - Image sizes are now model-aware: MAI-Image-2.5 models use presets within the 1,048,576-pixel limit
42
+ (landscape `1248x832`, portrait `832x1248`); 2.6 deployments (detected from the name, or via
43
+ `MAI_IMAGE_SIZE_PROFILE` / `image_gen.azure-mai.size_profile` = `2.5`/`2.6`) use larger presets. (#17)
44
+ - Safer retry policy: 429 is always retried honouring `Retry-After` (seconds or HTTP-date) with a
45
+ long jittered backoff otherwise; only 502/503 are retried among 5xx (never 500/504, to avoid
46
+ double billing); retries share an overall `MAI_REQUEST_DEADLINE` budget (default 600s).
47
+ - Honor Hermes secret scope for `MAI_FOUNDRY_API_KEY` (no env fallback inside Hermes); endpoint is a non-secret setting resolvable from `image_gen.azure-mai.*` config or env, and model/models-path are declared optional (#25).
48
+ - HTTP error messages are now accurate per status: 400 reports a rejected request (with the service
49
+ `error.code`/`message` and size/format limits) instead of a missing deployment, 404 covers deployment/endpoint,
50
+ 401/403 give authentication guidance, 429 mentions quota tiers, and detail is truncated to ~500 characters (#18).
51
+ - GIF and WebP source images are now rejected before upload with `invalid_input` (MAI accepts only
52
+ JPEG or PNG); README and catalog known issues list the supported formats.
53
+ - Security: remote source-image downloads no longer follow redirects (`allow_redirects=False`; 3xx and redirect history are rejected), closing an SSRF bypass (#19).
54
+ - Provider robustness: model discovery no longer raises when the resource `/models` endpoint is
55
+ unavailable, falling back to the documented MAI catalog.
56
+ - Provider robustness: remote image downloads enforce a byte cap to bound memory use.
57
+ - Runtime dependency is bounded: `requests>=2.28,<3` (catalog rule 10 asks for a floor and an upper bound).
58
+ - Manifest metadata completed; `pyproject.toml` and `plugin.yaml` declare the same version, enforced
59
+ by a test that parses both.
60
+ - README updated for catalog submission (installation, configuration, and discovery instructions).
61
+
62
+ ## [0.1.2]
63
+
64
+ ### Changed
65
+
66
+ - README installation, configuration, Web UI, CLI, troubleshooting, and update instructions
67
+ refreshed for the current Hermes plugin discovery flow.
68
+
69
+ [Unreleased]: https://github.com/joescars/hermes-mai-image/compare/v0.2.0...HEAD
70
+ [0.2.0]: https://github.com/joescars/hermes-mai-image/releases/tag/v0.2.0
71
+ [0.1.2]: https://github.com/joescars/hermes-mai-image/releases/tag/v0.1.2
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 joescars
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.