Skip to content

Commit b0a3e04

Browse files
committed
add basic project matter
1 parent 178a30d commit b0a3e04

File tree

4 files changed

+311
-11
lines changed

4 files changed

+311
-11
lines changed

src/envs/websearch_env/README.md

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
---
2+
title: Web Search Environment Server
3+
emoji: 📡
4+
colorFrom: red
5+
colorTo: pink
6+
sdk: docker
7+
pinned: false
8+
app_port: 8000
9+
base_path: /web
10+
tags:
11+
- openenv
12+
---
13+
14+
# Web Search Environment
15+
16+
A simple test environment that echoes back messages. Perfect for testing the env APIs as well as demonstrating environment usage patterns.
17+
18+
## Quick Start
19+
20+
The simplest way to use the Web Search environment is through the `WebSearchEnv` class:
21+
22+
```python
23+
from web_search import WebSearchAction, WebSearchEnv
24+
25+
try:
26+
# Create environment from Docker image
27+
web_searchenv = WebSearchEnv.from_docker_image("web_search-env:latest")
28+
29+
# Reset
30+
result = web_searchenv.reset()
31+
print(f"Reset: {result.observation.echoed_message}")
32+
33+
# Send multiple messages
34+
messages = ["Hello, World!", "Testing echo", "Final message"]
35+
36+
for msg in messages:
37+
result = web_searchenv.step(WebSearchAction(message=msg))
38+
print(f"Sent: '{msg}'")
39+
print(f" → Echoed: '{result.observation.echoed_message}'")
40+
print(f" → Length: {result.observation.message_length}")
41+
print(f" → Reward: {result.reward}")
42+
43+
finally:
44+
# Always clean up
45+
web_searchenv.close()
46+
```
47+
48+
That's it! The `WebSearchEnv.from_docker_image()` method handles:
49+
- Starting the Docker container
50+
- Waiting for the server to be ready
51+
- Connecting to the environment
52+
- Container cleanup when you call `close()`
53+
54+
## Building the Docker Image
55+
56+
Before using the environment, you need to build the Docker image:
57+
58+
```bash
59+
# From project root
60+
docker build -t web_search-env:latest -f server/Dockerfile .
61+
```
62+
63+
## Deploying to Hugging Face Spaces
64+
65+
You can easily deploy your OpenEnv environment to Hugging Face Spaces using the `openenv push` command:
66+
67+
```bash
68+
# From the environment directory (where openenv.yaml is located)
69+
openenv push
70+
71+
# Or specify options
72+
openenv push --namespace my-org --private
73+
```
74+
75+
The `openenv push` command will:
76+
1. Validate that the directory is an OpenEnv environment (checks for `openenv.yaml`)
77+
2. Prepare a custom build for Hugging Face Docker space (enables web interface)
78+
3. Upload to Hugging Face (ensuring you're logged in)
79+
80+
### Prerequisites
81+
82+
- Authenticate with Hugging Face: The command will prompt for login if not already authenticated
83+
84+
### Options
85+
86+
- `--directory`, `-d`: Directory containing the OpenEnv environment (defaults to current directory)
87+
- `--repo-id`, `-r`: Repository ID in format 'username/repo-name' (defaults to 'username/env-name' from openenv.yaml)
88+
- `--base-image`, `-b`: Base Docker image to use (overrides Dockerfile FROM)
89+
- `--private`: Deploy the space as private (default: public)
90+
91+
### Examples
92+
93+
```bash
94+
# Push to your personal namespace (defaults to username/env-name from openenv.yaml)
95+
openenv push
96+
97+
# Push to a specific repository
98+
openenv push --repo-id my-org/my-env
99+
100+
# Push with a custom base image
101+
openenv push --base-image ghcr.io/meta-pytorch/openenv-base:latest
102+
103+
# Push as a private space
104+
openenv push --private
105+
106+
# Combine options
107+
openenv push --repo-id my-org/my-env --base-image custom-base:latest --private
108+
```
109+
110+
After deployment, your space will be available at:
111+
`https://huggingface.co/spaces/<repo-id>`
112+
113+
The deployed space includes:
114+
- **Web Interface** at `/web` - Interactive UI for exploring the environment
115+
- **API Documentation** at `/docs` - Full OpenAPI/Swagger interface
116+
- **Health Check** at `/health` - Container health monitoring
117+
118+
## Environment Details
119+
120+
### Action
121+
**WebSearchAction**: Contains a single field
122+
- `message` (str) - The message to echo back
123+
124+
### Observation
125+
**WebSearchObservation**: Contains the echo response and metadata
126+
- `echoed_message` (str) - The message echoed back
127+
- `message_length` (int) - Length of the message
128+
- `reward` (float) - Reward based on message length (length × 0.1)
129+
- `done` (bool) - Always False for echo environment
130+
- `metadata` (dict) - Additional info like step count
131+
132+
### Reward
133+
The reward is calculated as: `message_length × 0.1`
134+
- "Hi" → reward: 0.2
135+
- "Hello, World!" → reward: 1.3
136+
- Empty message → reward: 0.0
137+
138+
## Advanced Usage
139+
140+
### Connecting to an Existing Server
141+
142+
If you already have a Web Search environment server running, you can connect directly:
143+
144+
```python
145+
from web_search import WebSearchEnv
146+
147+
# Connect to existing server
148+
web_searchenv = WebSearchEnv(base_url="<ENV_HTTP_URL_HERE>")
149+
150+
# Use as normal
151+
result = web_searchenv.reset()
152+
result = web_searchenv.step(WebSearchAction(message="Hello!"))
153+
```
154+
155+
Note: When connecting to an existing server, `web_searchenv.close()` will NOT stop the server.
156+
157+
## Development & Testing
158+
159+
### Direct Environment Testing
160+
161+
Test the environment logic directly without starting the HTTP server:
162+
163+
```bash
164+
# From the server directory
165+
python3 server/web_search_environment.py
166+
```
167+
168+
This verifies that:
169+
- Environment resets correctly
170+
- Step executes actions properly
171+
- State tracking works
172+
- Rewards are calculated correctly
173+
174+
### Running Locally
175+
176+
Run the server locally for development:
177+
178+
```bash
179+
uvicorn server.app:app --reload
180+
```
181+
182+
## Project Structure
183+
184+
```
185+
web_search/
186+
├── __init__.py # Module exports
187+
├── README.md # This file
188+
├── openenv.yaml # OpenEnv manifest
189+
├── pyproject.toml # Project metadata and dependencies
190+
├── uv.lock # Locked dependencies (generated)
191+
├── client.py # WebSearchEnv client implementation
192+
├── models.py # Action and Observation models
193+
└── server/
194+
├── __init__.py # Server module exports
195+
├── web_search_environment.py # Core environment logic
196+
├── app.py # FastAPI application
197+
└── Dockerfile # Container image definition
198+
```
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
spec_version: 1
2+
name: web_search
3+
type: space
4+
runtime: fastapi
5+
app: server.app:app
6+
port: 8000
7+
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
# All rights reserved.
3+
#
4+
# This source code is licensed under the BSD-style license found in the
5+
# LICENSE file in the root directory of this source tree.
6+
7+
[build-system]
8+
requires = ["setuptools>=45", "wheel"]
9+
build-backend = "setuptools.build_meta"
10+
11+
[project]
12+
name = "openenv-web_search"
13+
version = "0.1.0"
14+
description = "Web Search environment for OpenEnv"
15+
requires-python = ">=3.10"
16+
dependencies = [
17+
# Core OpenEnv dependencies (required for server functionality)
18+
"openenv-core>=0.1.0",
19+
"fastapi>=0.115.0",
20+
"pydantic>=2.0.0",
21+
"uvicorn>=0.24.0",
22+
"requests>=2.31.0",
23+
# Environment-specific dependencies
24+
"chardet==5.2.0",
25+
"aiohttp"
26+
]
27+
28+
[project.optional-dependencies]
29+
dev = [
30+
"pytest>=8.0.0",
31+
"pytest-cov>=4.0.0",
32+
]
33+
34+
[project.scripts]
35+
# Server entry point - enables running via: uv run --project . server
36+
# or: python -m web_search.server.app
37+
server = "web_search.server.app:main"
38+
39+
[tool.setuptools]
40+
package-dir = {"" = "."}
41+
42+
[tool.setuptools.packages.find]
43+
where = ["."]
44+

src/envs/websearch_env/server/Dockerfile

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,72 @@
44
# This source code is licensed under the BSD-style license found in the
55
# LICENSE file in the root directory of this source tree.
66

7-
# Use the shared OpenEnv base image (Python 3.11)
8-
ARG BASE_IMAGE=openenv-base:latest
7+
# Multi-stage build using openenv-base
8+
# This Dockerfile is flexible and works for both:
9+
# - In-repo environments (with local src/core)
10+
# - Standalone environments (with openenv-core from pip)
11+
# The build script (openenv build) handles context detection and sets appropriate build args.
12+
13+
ARG BASE_IMAGE=ghcr.io/meta-pytorch/openenv-base:latest
14+
FROM ${BASE_IMAGE} AS builder
15+
16+
WORKDIR /app
17+
18+
# Build argument to control whether we're building standalone or in-repo
19+
ARG BUILD_MODE=in-repo
20+
ARG ENV_NAME=web_search
21+
22+
# Copy environment code (always at root of build context)
23+
COPY . /app/env
24+
25+
# For in-repo builds, openenv-core is already in the pyproject.toml dependencies
26+
# For standalone builds, openenv-core will be installed from pip via pyproject.toml
27+
WORKDIR /app/env
28+
29+
# Ensure uv is available (for local builds where base image lacks it)
30+
RUN if ! command -v uv >/dev/null 2>&1; then \
31+
curl -LsSf https://astral.sh/uv/install.sh | sh && \
32+
mv /root/.local/bin/uv /usr/local/bin/uv && \
33+
mv /root/.local/bin/uvx /usr/local/bin/uvx; \
34+
fi
35+
36+
# Install dependencies using uv sync
37+
# If uv.lock exists, use it; otherwise resolve on the fly
38+
RUN --mount=type=cache,target=/root/.cache/uv \
39+
if [ -f uv.lock ]; then \
40+
uv sync --frozen --no-install-project --no-editable; \
41+
else \
42+
uv sync --no-install-project --no-editable; \
43+
fi
44+
45+
RUN --mount=type=cache,target=/root/.cache/uv \
46+
if [ -f uv.lock ]; then \
47+
uv sync --frozen --no-editable; \
48+
else \
49+
uv sync --no-editable; \
50+
fi
51+
52+
# Final runtime stage
953
FROM ${BASE_IMAGE}
1054

11-
# Install Python dependencies
12-
RUN pip install --no-cache-dir \
13-
chardet==5.2.0
55+
WORKDIR /app
56+
57+
# Copy the virtual environment from builder
58+
COPY --from=builder /app/env/.venv /app/.venv
59+
60+
# Copy the environment code
61+
COPY --from=builder /app/env /app/env
62+
63+
# Set PATH to use the virtual environment
64+
ENV PATH="/app/.venv/bin:$PATH"
1465

15-
# Copy OpenEnv core and WebSearch environment sources
16-
COPY src/core/ /app/src/core/
17-
COPY src/envs/websearch_env/ /app/src/envs/websearch_env/
66+
# Set PYTHONPATH so imports work correctly
67+
ENV PYTHONPATH="/app/env:$PYTHONPATH"
1868

19-
# Optional: health check to ensure server responsiveness
69+
# Health check
2070
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
2171
CMD curl -f http://localhost:8000/health || exit 1
2272

23-
# Run the WebSearch FastAPI server
24-
CMD ["uvicorn", "envs.websearch_env.server.app:app", "--host", "0.0.0.0", "--port", "8000"]
73+
# Run the FastAPI server
74+
# The module path is constructed to work with the /app/env structure
75+
CMD ["sh", "-c", "cd /app/env && uvicorn server.app:app --host 0.0.0.0 --port 8000"]

0 commit comments

Comments
 (0)