evalstate HF Staff commited on
Commit
cc49c15
·
verified ·
1 Parent(s): 48717f2

Initial fast-agent deployment with Python 3.13 and uv

Browse files
Files changed (6) hide show
  1. .gitignore +5 -0
  2. DEPLOYMENT.md +187 -0
  3. Dockerfile +34 -0
  4. README.md +23 -6
  5. agent.md +33 -0
  6. hf_api_tool.py +141 -0
.gitignore ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ __pycache__/
2
+ *.pyc
3
+ .env
4
+ *.log
5
+ fastagent.jsonl
DEPLOYMENT.md ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Deploying Fast-Agent to Hugging Face Spaces
2
+
3
+ ## Quick Start
4
+
5
+ This directory contains everything you need to deploy fast-agent to a Hugging Face Space.
6
+
7
+ ## Files Overview
8
+
9
+ - **README.md**: Space metadata and description (with YAML header)
10
+ - **Dockerfile**: Docker image definition (Python 3.13 + uv)
11
+ - **agent.md**: Agent card defining the agent's capabilities and tools
12
+ - **hf_api_tool.py**: Python tool for HF API access (from fast-agent examples)
13
+ - **.gitignore**: Files to ignore in git
14
+
15
+ ## Deployment Steps
16
+
17
+ ### 1. Create a New Space
18
+
19
+ Go to https://huggingface.co/new-space and:
20
+ - Choose a name for your Space
21
+ - Select **Docker** as the SDK
22
+ - Choose visibility (Public or Private)
23
+ - Click "Create Space"
24
+
25
+ ### 2. Clone Your Space Repository
26
+
27
+ ```bash
28
+ git clone https://huggingface.co/spaces/YOUR_USERNAME/YOUR_SPACE_NAME
29
+ cd YOUR_SPACE_NAME
30
+ ```
31
+
32
+ ### 3. Copy Files
33
+
34
+ Copy all files from this directory to your Space repository:
35
+
36
+ ```bash
37
+ cp /path/to/this/directory/* YOUR_SPACE_NAME/
38
+ ```
39
+
40
+ Or manually copy:
41
+ - README.md
42
+ - Dockerfile
43
+ - agent.md
44
+ - hf_api_tool.py
45
+ - .gitignore
46
+
47
+ ### 4. Commit and Push
48
+
49
+ ```bash
50
+ git add .
51
+ git commit -m "Initial fast-agent deployment"
52
+ git push
53
+ ```
54
+
55
+ ### 5. Wait for Build
56
+
57
+ Hugging Face will automatically:
58
+ - Build the Docker image (Python 3.13 + uv)
59
+ - Deploy the container
60
+ - Make it available at your Space URL
61
+
62
+ This usually takes 2-5 minutes.
63
+
64
+ ### 6. (Optional) Add Secrets
65
+
66
+ If your agent needs authentication:
67
+
68
+ 1. Go to your Space settings
69
+ 2. Navigate to "Repository secrets"
70
+ 3. Add `HF_TOKEN` with your Hugging Face token
71
+ 4. Restart the Space
72
+
73
+ ## Testing Locally
74
+
75
+ Before deploying, you can test locally:
76
+
77
+ ```bash
78
+ # Build the Docker image
79
+ docker build -t fast-agent-space .
80
+
81
+ # Run it locally
82
+ docker run -p 7860:7860 -e HF_TOKEN=your_token fast-agent-space
83
+
84
+ # Test the endpoint
85
+ curl http://localhost:7860
86
+ ```
87
+
88
+ ## Technical Details
89
+
90
+ ### Python & Dependencies
91
+
92
+ - **Python 3.13**: Using the latest slim image
93
+ - **uv**: Astral's fast Python package installer
94
+ - **System packages**: bash, git, git-lfs, wget, curl, procps
95
+
96
+ The Dockerfile uses `uv pip install --system` for faster, more reliable package installation.
97
+
98
+ ## Customization
99
+
100
+ ### Change the Agent Card
101
+
102
+ Edit `agent.md` to modify:
103
+ - Agent name
104
+ - Model (e.g., `gpt-4`, `claude-3-sonnet`, etc.)
105
+ - Instructions
106
+ - Tool references
107
+
108
+ ### Add More Tools
109
+
110
+ 1. Create a new Python file (e.g., `my_tool.py`)
111
+ 2. Add your tool function
112
+ 3. Reference it in `agent.md`:
113
+ ```yaml
114
+ function_tools:
115
+ - hf_api_tool.py:hf_api_request
116
+ - my_tool.py:my_function
117
+ ```
118
+
119
+ ### Multiple Agent Cards
120
+
121
+ Modify the Dockerfile CMD to include multiple cards:
122
+
123
+ ```dockerfile
124
+ CMD ["fast-agent", "serve", "--card", "agent1.md", "--card", "agent2.md", "--port", "7860"]
125
+ ```
126
+
127
+ ### Change the Model
128
+
129
+ Add `--model` to the CMD in Dockerfile:
130
+
131
+ ```dockerfile
132
+ CMD ["fast-agent", "serve", "--card", "agent.md", "--model", "gpt-4", "--port", "7860"]
133
+ ```
134
+
135
+ ### Additional Dependencies
136
+
137
+ If you need extra Python packages, add them after the fast-agent-mcp install:
138
+
139
+ ```dockerfile
140
+ RUN uv pip install --system --no-cache fast-agent-mcp my-package another-package
141
+ ```
142
+
143
+ Or use a requirements.txt:
144
+
145
+ ```dockerfile
146
+ COPY requirements.txt .
147
+ RUN uv pip install --system --no-cache -r requirements.txt
148
+ ```
149
+
150
+ ## Accessing Your Agent
151
+
152
+ Once deployed, your agent will be accessible as an HTTP API at:
153
+ ```
154
+ https://YOUR_USERNAME-YOUR_SPACE_NAME.hf.space
155
+ ```
156
+
157
+ You can interact with it via HTTP requests or use it as an MCP server.
158
+
159
+ ## Troubleshooting
160
+
161
+ ### Space Won't Build
162
+
163
+ Check the build logs in your Space for errors. Common issues:
164
+ - Missing dependencies in Dockerfile
165
+ - Syntax errors in agent.md
166
+ - Tool file not found
167
+
168
+ ### Space Runs But Errors
169
+
170
+ Check the application logs. Common issues:
171
+ - Model API key not configured
172
+ - Port mismatch (must be 7860)
173
+ - Tool import errors
174
+
175
+ ### Need More Resources
176
+
177
+ If you need more CPU/RAM:
178
+ 1. Go to Space settings
179
+ 2. Upgrade to a paid tier
180
+ 3. Select appropriate hardware
181
+
182
+ ## Support
183
+
184
+ - Fast-Agent GitHub: https://github.com/evalstate/fast-agent
185
+ - Fast-Agent PyPI: https://pypi.org/project/fast-agent-mcp/
186
+ - Hugging Face Spaces docs: https://huggingface.co/docs/hub/spaces
187
+ - Astral uv docs: https://docs.astral.sh/uv/
Dockerfile ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.13-slim
2
+
3
+ # Install system dependencies required by fast-agent and HF Spaces
4
+ RUN apt-get update && \
5
+ apt-get install -y \
6
+ bash \
7
+ git git-lfs \
8
+ wget curl procps \
9
+ && rm -rf /var/lib/apt/lists/*
10
+
11
+ # Install uv
12
+ COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
13
+
14
+ # Set working directory
15
+ WORKDIR /app
16
+
17
+ # Install fast-agent-mcp using uv
18
+ RUN uv pip install --system --no-cache fast-agent-mcp
19
+
20
+ # Copy all files from the Space repository to /app
21
+ COPY --link ./ /app
22
+
23
+ # Ensure /app is owned by uid 1000 (required for HF Spaces)
24
+ RUN chown -R 1000:1000 /app
25
+
26
+ # Switch to non-root user
27
+ USER 1000
28
+
29
+ # Expose port 7860 (HF Spaces default)
30
+ EXPOSE 7860
31
+
32
+ # Run fast-agent serve
33
+ # The agent card (agent.md) and tool file (hf_api_tool.py) are in /app
34
+ CMD ["fast-agent", "serve", "--card", "agent.md", "--transport", "http", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -1,10 +1,27 @@
1
  ---
2
- title: Agent Test
3
- emoji: 📚
4
- colorFrom: gray
5
- colorTo: yellow
6
  sdk: docker
7
- pinned: false
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Fast Agent Demo
3
+ emoji: 🤖
4
+ colorFrom: blue
5
+ colorTo: green
6
  sdk: docker
7
+ app_port: 7860
8
  ---
9
 
10
+ # Fast Agent on Hugging Face Spaces
11
+
12
+ This Space runs [fast-agent](https://pypi.org/project/fast-agent-mcp/) with custom tools and agent cards.
13
+
14
+ The agent is accessible via HTTP API and can be interacted with programmatically.
15
+
16
+ ## Configuration
17
+
18
+ This Space includes:
19
+ - A custom agent card (`agent.md`) defining the agent's capabilities
20
+ - A Python tool file (`hf_api_tool.py`) for Hugging Face API access
21
+ - Environment-based configuration for tokens
22
+
23
+ ## Usage
24
+
25
+ Once the Space is running, you can interact with the agent via HTTP requests to the Space URL.
26
+
27
+ See the [fast-agent documentation](https://github.com/evalstate/fast-agent) for more details.
agent.md ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ type: agent
3
+ name: hf-api-agent
4
+ function_tools:
5
+ - hf_api_tool.py:hf_api_request
6
+ model: gpt-4
7
+ default: true
8
+ description: An agent with access to Hugging Face API
9
+ ---
10
+
11
+ # Hugging Face API Agent
12
+
13
+ You are an agent with access to the Hugging Face Hub API via the `hf_api_request` tool.
14
+
15
+ ## Available Tool
16
+
17
+ **hf_api_request(endpoint, method="GET", params=None, json_body=None, max_results=None)**
18
+
19
+ Call the Hugging Face Hub API. Examples:
20
+
21
+ - Get current user info: `hf_api_request(endpoint="/whoami-v2")`
22
+ - Get user overview: `hf_api_request(endpoint="/users/{username}/overview")`
23
+ - List user's liked repos: `hf_api_request(endpoint="/users/{username}/likes")`
24
+ - Get organization info: `hf_api_request(endpoint="/organizations/{org}/overview")`
25
+
26
+ ## Instructions
27
+
28
+ When users ask about Hugging Face data:
29
+ 1. Use the appropriate API endpoint
30
+ 2. Format the response in a clear, readable way
31
+ 3. If authentication is needed and fails, inform the user they may need to configure HF_TOKEN
32
+
33
+ Be helpful and concise in your responses.
hf_api_tool.py ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import os
5
+ from pathlib import Path
6
+ from typing import Any
7
+ from urllib.error import HTTPError, URLError
8
+ from urllib.parse import urlencode
9
+ from urllib.request import Request, urlopen
10
+
11
+ DEFAULT_MAX_RESULTS = 20
12
+ DEFAULT_TIMEOUT_SEC = 30
13
+
14
+
15
+ def _load_token() -> str | None:
16
+ token = os.getenv("HF_TOKEN")
17
+ if token:
18
+ return token
19
+
20
+ token_path = Path.home() / ".cache" / "huggingface" / "token"
21
+ if token_path.exists():
22
+ token_value = token_path.read_text(encoding="utf-8").strip()
23
+ return token_value or None
24
+
25
+ return None
26
+
27
+
28
+ def _max_results_from_env() -> int:
29
+ raw = os.getenv("HF_MAX_RESULTS")
30
+ if not raw:
31
+ return DEFAULT_MAX_RESULTS
32
+ try:
33
+ value = int(raw)
34
+ except ValueError:
35
+ return DEFAULT_MAX_RESULTS
36
+ return value if value > 0 else DEFAULT_MAX_RESULTS
37
+
38
+
39
+ def _normalize_endpoint(endpoint: str) -> str:
40
+ if endpoint.startswith("http://") or endpoint.startswith("https://"):
41
+ raise ValueError("Endpoint must be a path relative to /api, not a full URL.")
42
+ endpoint = endpoint.strip()
43
+ if not endpoint:
44
+ raise ValueError("Endpoint must be a non-empty string.")
45
+ if not endpoint.startswith("/"):
46
+ endpoint = f"/{endpoint}"
47
+ return endpoint
48
+
49
+
50
+ def _normalize_params(params: dict[str, Any] | None) -> dict[str, Any]:
51
+ if not params:
52
+ return {}
53
+ normalized: dict[str, Any] = {}
54
+ for key, value in params.items():
55
+ if value is None:
56
+ continue
57
+ if isinstance(value, (list, tuple)):
58
+ normalized[key] = [str(item) for item in value]
59
+ else:
60
+ normalized[key] = str(value)
61
+ return normalized
62
+
63
+
64
+ def _build_url(endpoint: str, params: dict[str, Any] | None) -> str:
65
+ base = os.getenv("HF_ENDPOINT", "https://huggingface.co").rstrip("/")
66
+ url = f"{base}/api{_normalize_endpoint(endpoint)}"
67
+ normalized_params = _normalize_params(params)
68
+ if normalized_params:
69
+ url = f"{url}?{urlencode(normalized_params, doseq=True)}"
70
+ return url
71
+
72
+
73
+ def hf_api_request(
74
+ endpoint: str,
75
+ method: str = "GET",
76
+ params: dict[str, Any] | None = None,
77
+ json_body: dict[str, Any] | None = None,
78
+ max_results: int | None = None,
79
+ ) -> dict[str, Any]:
80
+ """
81
+ Call the Hugging Face Hub API (GET/POST only).
82
+
83
+ Args:
84
+ endpoint: API endpoint relative to /api (e.g. "/whoami-v2").
85
+ method: HTTP method (GET or POST).
86
+ params: Optional query parameters.
87
+ json_body: Optional JSON payload for POST requests.
88
+ max_results: Max results when response is a list (defaults to HF_MAX_RESULTS).
89
+
90
+ Returns:
91
+ A dict with the response data and request metadata.
92
+ """
93
+ method_upper = method.upper()
94
+ if method_upper not in {"GET", "POST"}:
95
+ raise ValueError("Only GET and POST are allowed for hf_api_request.")
96
+
97
+ if method_upper == "GET" and json_body is not None:
98
+ raise ValueError("GET requests do not accept json_body.")
99
+
100
+ url = _build_url(endpoint, params)
101
+
102
+ headers = {
103
+ "Accept": "application/json",
104
+ }
105
+ token = _load_token()
106
+ if token:
107
+ headers["Authorization"] = f"Bearer {token}"
108
+
109
+ data = None
110
+ if method_upper == "POST":
111
+ headers["Content-Type"] = "application/json"
112
+ data = json.dumps(json_body or {}).encode("utf-8")
113
+
114
+ request = Request(url, headers=headers, data=data, method=method_upper)
115
+
116
+ try:
117
+ with urlopen(request, timeout=DEFAULT_TIMEOUT_SEC) as response:
118
+ raw = response.read()
119
+ status_code = response.status
120
+ except HTTPError as exc:
121
+ error_body = exc.read().decode("utf-8", errors="replace")
122
+ raise RuntimeError(
123
+ f"HF API error {exc.code} for {url}: {error_body}"
124
+ ) from exc
125
+ except URLError as exc:
126
+ raise RuntimeError(f"HF API request failed for {url}: {exc}") from exc
127
+
128
+ try:
129
+ payload = json.loads(raw)
130
+ except json.JSONDecodeError:
131
+ payload = raw.decode("utf-8", errors="replace")
132
+
133
+ if isinstance(payload, list):
134
+ limit = max_results if max_results is not None else _max_results_from_env()
135
+ payload = payload[: max(limit, 0)]
136
+
137
+ return {
138
+ "url": url,
139
+ "status": status_code,
140
+ "data": payload,
141
+ }