Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,60 @@ The `type` and `subtype` values match the RLE control plane. The manifest
intentionally does not contain Foundry project endpoints, registry
locations, service IDs, or credentials.

## RLE SDK installation

All four RLE container samples install `azure-ai-projects[rle]` wheels from the
[public SDK manifest](https://raw.githubusercontent.com/Azure/azure-sdk-for-python/rle-releases/sdk/ai/azure-ai-projects/rle/registry.json),
hosted by [Azure/azure-sdk-for-python#49488](https://github.com/Azure/azure-sdk-for-python/pull/49488).
The SDK is installed inside the container during `azd ai rle publish`; no host
SDK installation, SDK source-fork access, or GitHub credentials are required.
Each sample includes `install_rle_sdk.py` in its own Docker build context.
The Dockerfile copies the installer locally; it is not downloaded during builds.
The installer selects the requested version and verifies its SHA-256 before
running pip. Docker uses the wheel already fetched beside the registry, without
downloading it again. Local development using the remote registry downloads the
selected wheel instead. Approved version paths must not be overwritten, and a
checksum mismatch always fails installation.
Keep the four installer copies identical when updating them. Each sample
retains its own build context and includes its installer when scaffolded by the CLI.

Each sample's Dockerfile sets `ENV AZURE_AI_PROJECTS_VERSION=latest` before
SDK installation. To pin a version, edit that line in your local Dockerfile to
an exact version listed in the manifest. A missing version fails; it never falls
back. No SDK setting is needed on the `azd` CLI side.
The Dockerfiles use Git-source `ADD` scoped to the public distribution
subdirectory. A builder supporting Git-source `ADD`
(such as current Docker with BuildKit)
resolves the branch commit on each build, so new releases refresh the SDK
installation layer without relying on a CDN-cached raw manifest.

The default Dockerfile setting is:

```dockerfile
ENV AZURE_AI_PROJECTS_VERSION=latest
```

To pin the SDK version, change that line locally before rebuilding:

```dockerfile
ENV AZURE_AI_PROJECTS_VERSION=2.7.0b1
```

For local server development outside Docker, run from the folder containing
the sample's Dockerfile:

```powershell
python install_rle_sdk.py --requirements requirements.txt
```

The local installer also accepts `--version <version>` for a version listed in
the manifest. Docker builds use the environment value defined in the Dockerfile.
Runtime container environment variables cannot change the SDK baked into an
image.
[`tools/test_rle_sdk_distribution.py`](./tools/test_rle_sdk_distribution.py)
checks that all sample installers match, along with version selection, checksum
verification, and pip installation.

## `.agents/skills/` and `.claude/skills/` — the authoring skills

The skills carry the RLE authoring contract: the server surface, the action
Expand Down
15 changes: 7 additions & 8 deletions examples/gym/openenv/code_rl/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,13 @@ ARG PIP_INDEX_URL

WORKDIR /app

ARG AZURE_AI_PROJECTS_RLE_REF=e56ac36c6b497f74cc7cb1caf1f2fa07bd0cb241
RUN apt-get update \
&& apt-get install -y --no-install-recommends git \
&& rm -rf /var/lib/apt/lists/* \
&& pip install --no-cache-dir \
"azure-ai-projects[rle] @ git+https://github.com/sujit-kamireddy/azure-sdk-for-python@${AZURE_AI_PROJECTS_RLE_REF}#subdirectory=sdk/ai/azure-ai-projects" \
"openenv==0.6.0" \
numpy
ENV AZURE_AI_PROJECTS_VERSION=latest
COPY requirements.txt /app/
COPY install_rle_sdk.py /app/
# Resolve the public branch commit; raw HTTP manifests can be CDN-cached.
ADD https://github.com/Azure/azure-sdk-for-python.git#rle-releases:sdk/ai/azure-ai-projects/rle /app/rle-sdk-dist/
RUN python /app/install_rle_sdk.py --manifest /app/rle-sdk-dist/registry.json \
--requirements /app/requirements.txt

COPY . /app/envs/code_rl

Expand Down
8 changes: 5 additions & 3 deletions examples/gym/openenv/code_rl/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,11 @@ not problem content:

## Build and run

The Dockerfile pins `openenv==0.6.0` and the immutable SDK source revision
`e56ac36c6b497f74cc7cb1caf1f2fa07bd0cb241`; the public SDK wheel at that
version does not yet contain `RLEnvironment`.
The Dockerfile pins `openenv==0.6.0` and installs an RLE SDK wheel from the
public manifest without accessing the SDK source fork. [`Dockerfile`](./Dockerfile)
sets `ENV AZURE_AI_PROJECTS_VERSION=latest`; edit that line locally to pin
an exact published SDK version before rebuilding. No SDK CLI setting is needed.
See [SDK installation](../../../../README.md#rle-sdk-installation).

```bash
docker build -t code-rl .
Expand Down
122 changes: 122 additions & 0 deletions examples/gym/openenv/code_rl/install_rle_sdk.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
"""Resolve, verify and install an approved RLE SDK wheel from the public registry."""

from __future__ import annotations

import argparse
from dataclasses import dataclass
import hashlib
import json
import os
from pathlib import Path
import subprocess
import sys
import tempfile
from urllib.request import urlopen

PUBLIC_REPOSITORY = "Azure/azure-sdk-for-python"
PUBLIC_BRANCH = "rle-releases"
PUBLIC_PATH = "sdk/ai/azure-ai-projects/rle"
MANIFEST_URL = (
f"https://raw.githubusercontent.com/{PUBLIC_REPOSITORY}/"
f"{PUBLIC_BRANCH}/{PUBLIC_PATH}/registry.json"
)


@dataclass(frozen=True)
class Release:
version: str
filename: str
url: str
sha256: str


def resolve_release(manifest: object, version: str = "latest") -> Release:
if not isinstance(manifest, dict):
raise ValueError("The SDK manifest must be a JSON object.")
if (
manifest.get("schema_version") != 1
or manifest.get("package") != "azure-ai-projects"
or manifest.get("channel") != "rle"
):
raise ValueError("Unsupported SDK manifest schema, package, or channel.")
selected = manifest.get("latest") if version == "latest" else version
versions = manifest.get("versions")
if not isinstance(selected, str) or not selected or not isinstance(versions, dict) or selected not in versions:
raise ValueError(f"RLE SDK version {selected!r} is not available.")
entry = versions[selected]
if not isinstance(entry, dict) or not all(
isinstance(entry.get(field), str) and entry[field]
for field in ("filename", "url", "sha256")
):
raise ValueError(f"Invalid release entry for {selected}.")
filename = entry["filename"]
if selected in (".", "..") or any(char in selected + filename for char in "/\\:") or not filename.endswith(".whl"):
raise ValueError("The SDK version and filename must be safe wheel path components.")
if not entry["url"].startswith("https://"):
raise ValueError("The SDK wheel URL must use HTTPS.")
return Release(selected, filename, entry["url"], entry["sha256"])


def verify_checksum(path: Path, release: Release) -> None:
if hashlib.sha256(path.read_bytes()).hexdigest() != release.sha256:
raise ValueError(f"SHA-256 mismatch for SDK {release.version}; refusing installation.")


def download_wheel(release: Release, directory: Path) -> Path:
target = directory / release.filename
with urlopen(release.url, timeout=120) as response:
target.write_bytes(response.read())
try:
verify_checksum(target, release)
except ValueError:
target.unlink()
raise
return target


def load_manifest(location: str) -> object:
if location.startswith("https://"):
with urlopen(location, timeout=60) as response:
return json.load(response)
return json.loads(Path(location).read_text(encoding="utf-8"))


def selected_version(explicit: str | None) -> str:
if explicit is not None:
return explicit
configured = os.environ.get("AZURE_AI_PROJECTS_VERSION")
if configured is not None:
return configured
return "latest"


def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--manifest", default=MANIFEST_URL)
parser.add_argument("--version")
parser.add_argument("--requirements", type=Path)
parser.add_argument("--index-url", default=os.environ.get("PIP_INDEX_URL"))
args = parser.parse_args()
release = resolve_release(
load_manifest(args.manifest), selected_version(args.version)
)
print(f"RLE SDK: {release.version} (SHA-256 {release.sha256})", flush=True)
with tempfile.TemporaryDirectory(prefix="rle-sdk-") as temporary:
if args.manifest.startswith("https://"):
wheel = download_wheel(release, Path(temporary))
else:
wheel = Path(args.manifest).resolve().parent / "wheels" / release.version / release.filename
verify_checksum(wheel, release)
command = [
sys.executable, "-m", "pip", "install", "--no-cache-dir",
"--progress-bar", "off", f"azure-ai-projects[rle] @ {wheel.as_uri()}",
]
if args.index_url:
command.extend(["--index-url", args.index_url])
if args.requirements is not None:
command.extend(["--requirement", str(args.requirements)])
subprocess.run(command, check=True)


if __name__ == "__main__":
main()
2 changes: 2 additions & 0 deletions examples/gym/openenv/code_rl/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
openenv==0.6.0
numpy
8 changes: 5 additions & 3 deletions examples/gym/openenv/code_rl/tests/test_sample_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,11 @@ def test_obsolete_schema_contract_is_removed():
def test_dockerfile_matches_sibling_sdk_and_openenv_pins():
dockerfile = (ROOT / "Dockerfile").read_text()

assert "e56ac36c6b497f74cc7cb1caf1f2fa07bd0cb241" in dockerfile
assert "azure-ai-projects[rle] @ git+" in dockerfile
assert "openenv==0.6.0" in dockerfile
assert "ENV AZURE_AI_PROJECTS_VERSION=latest" in dockerfile
assert "install_rle_sdk.py" in dockerfile
assert "rle-releases:sdk/ai/azure-ai-projects/rle" in dockerfile
assert "git+" not in dockerfile
assert "openenv==0.6.0" in (ROOT / "requirements.txt").read_text()


def test_app_uses_grade_action_and_explicit_state_model():
Expand Down
19 changes: 7 additions & 12 deletions examples/gym/openenv/math_rl/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,13 @@ ARG PIP_INDEX_URL

WORKDIR /app

# This immutable SDK source revision
# reports 2.7.0 and contains RLEnvironment, while the public 2.7.0 wheel does
# not yet contain azure.ai.projects.rle.
ARG AZURE_AI_PROJECTS_RLE_REF=e56ac36c6b497f74cc7cb1caf1f2fa07bd0cb241
RUN apt-get update \
&& apt-get install -y --no-install-recommends git \
&& rm -rf /var/lib/apt/lists/* \
&& pip install --no-cache-dir \
"azure-ai-projects[rle] @ git+https://github.com/sujit-kamireddy/azure-sdk-for-python@${AZURE_AI_PROJECTS_RLE_REF}#subdirectory=sdk/ai/azure-ai-projects" \
"openenv==0.6.0" \
sympy \
pylatexenc
ENV AZURE_AI_PROJECTS_VERSION=latest
COPY requirements.txt /app/
COPY install_rle_sdk.py /app/
# Resolve the public branch commit; raw HTTP manifests can be CDN-cached.
ADD https://github.com/Azure/azure-sdk-for-python.git#rle-releases:sdk/ai/azure-ai-projects/rle /app/rle-sdk-dist/
RUN python /app/install_rle_sdk.py --manifest /app/rle-sdk-dist/registry.json \
--requirements /app/requirements.txt

COPY . /app/envs/math_rl

Expand Down
11 changes: 6 additions & 5 deletions examples/gym/openenv/math_rl/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,17 +65,18 @@ to `safe_grade`; raw unboxed final text is never accepted as a lucky match.

## Build and run

Choose Docker or Podman and run from this directory:
Use a Git-source-ADD-capable builder (such as Docker with BuildKit) from this directory:

```bash
docker build -t math-rl .
docker run --rm -p 8000:8000 math-rl
```

The Dockerfile pins `openenv==0.6.0` and immutable SDK source revision
`e56ac36c6b497f74cc7cb1caf1f2fa07bd0cb241`. That revision contains
`azure.ai.projects.rle.environments.RLEnvironment`; the released public 2.7.0
wheel does not, so it is not currently an equivalent dependency.
The Dockerfile pins `openenv==0.6.0` and installs an RLE SDK wheel from the
public manifest without accessing the SDK source fork. [`Dockerfile`](./Dockerfile)
sets `ENV AZURE_AI_PROJECTS_VERSION=latest`; edit that line locally to pin
an exact published SDK version before rebuilding. No SDK CLI setting is needed.
See [SDK installation](../../../../README.md#rle-sdk-installation).

The server must use one worker. RLE owns the model loop and sends MCP
`tools/list`, optional `tools/call`, and terminal `GradeAction` exchanges over
Expand Down
Loading