Spaces:
Running
Running
Upload 6 files
Browse files- .gitignore +2 -0
- DEPLOY_HF_SPACE.md +16 -13
- Dockerfile +24 -9
- README.md +34 -38
- app.py +64 -0
- requirements.txt +3 -13
.gitignore
CHANGED
|
@@ -3,3 +3,5 @@
|
|
| 3 |
*.db-shm
|
| 4 |
*.db-wal
|
| 5 |
.encryption-key
|
|
|
|
|
|
|
|
|
| 3 |
*.db-shm
|
| 4 |
*.db-wal
|
| 5 |
.encryption-key
|
| 6 |
+
__pycache__/
|
| 7 |
+
*.py[cod]
|
DEPLOY_HF_SPACE.md
CHANGED
|
@@ -1,21 +1,24 @@
|
|
| 1 |
-
# Deployment checklist
|
| 2 |
|
| 3 |
-
1. Create a **Docker**
|
| 4 |
-
2.
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
4.
|
| 8 |
-
5.
|
| 9 |
-
6.
|
| 10 |
-
7.
|
| 11 |
`npx freellmapi setup-claude --url "https://<username>-<space-name>.hf.space" --api-key "<unified-api-key>"`
|
| 12 |
|
| 13 |
-
##
|
| 14 |
|
| 15 |
- UI: `https://<username>-<space-name>.hf.space/`
|
| 16 |
-
-
|
| 17 |
-
-
|
| 18 |
|
| 19 |
## Important
|
| 20 |
|
| 21 |
-
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Deployment checklist
|
| 2 |
|
| 3 |
+
1. Create/select a Hugging Face Space with **Docker** SDK.
|
| 4 |
+
2. Upload `README.md`, `Dockerfile`, `app.py`, `requirements.txt`, and `.gitignore` to the repository root.
|
| 5 |
+
3. Add the `ENCRYPTION_KEY` secret in Space Settings → Variables and secrets. Generate it locally with:
|
| 6 |
+
`python -c "import secrets; print(secrets.token_hex(32))"`
|
| 7 |
+
4. For persistence, attach a writable Storage Bucket volume to `/data`. Without a mounted persistent volume, the database and fallback key may be lost on restart.
|
| 8 |
+
5. Wait for the build. The runtime should start with `node server/dist/index.js`; the Python launcher ensures this command is passed to the upstream entrypoint.
|
| 9 |
+
6. Open the Space, complete the dashboard's initial account setup, then configure upstream provider keys.
|
| 10 |
+
7. Copy the unified API key from the dashboard and configure Claude Code:
|
| 11 |
`npx freellmapi setup-claude --url "https://<username>-<space-name>.hf.space" --api-key "<unified-api-key>"`
|
| 12 |
|
| 13 |
+
## Endpoints
|
| 14 |
|
| 15 |
- UI: `https://<username>-<space-name>.hf.space/`
|
| 16 |
+
- OpenAI-compatible API: `https://<username>-<space-name>.hf.space/v1`
|
| 17 |
+
- Claude Code / Anthropic API base: `https://<username>-<space-name>.hf.space`
|
| 18 |
|
| 19 |
## Important
|
| 20 |
|
| 21 |
+
- FreeLLMAPI is a Node.js/React application. `app.py` is the Python startup wrapper, not a Python rewrite of the entire product.
|
| 22 |
+
- Never commit provider keys, unified API keys, or encryption secrets to Git.
|
| 23 |
+
- A private Space may require Hugging Face authentication in addition to FreeLLMAPI authentication. Test external CLI access for your visibility settings.
|
| 24 |
+
- Keep the same encryption secret when retaining an existing database.
|
Dockerfile
CHANGED
|
@@ -1,15 +1,30 @@
|
|
| 1 |
-
# FreeLLMAPI
|
| 2 |
-
#
|
| 3 |
FROM ghcr.io/tashfeenahmed/freellmapi:v0.16.7
|
| 4 |
|
| 5 |
USER root
|
| 6 |
-
COPY hf-entrypoint.sh /usr/local/bin/hf-entrypoint.sh
|
| 7 |
-
RUN chmod 755 /usr/local/bin/hf-entrypoint.sh
|
| 8 |
|
| 9 |
-
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
EXPOSE 7860
|
| 12 |
|
| 13 |
-
#
|
| 14 |
-
#
|
| 15 |
-
ENTRYPOINT ["/
|
|
|
|
|
|
| 1 |
+
# FreeLLMAPI's actual API and dashboard are Node.js/React; use the upstream
|
| 2 |
+
# release image, with a small Python launcher to configure Hugging Face Spaces.
|
| 3 |
FROM ghcr.io/tashfeenahmed/freellmapi:v0.16.7
|
| 4 |
|
| 5 |
USER root
|
|
|
|
|
|
|
| 6 |
|
| 7 |
+
# Python launcher + pip keep the Space package Python-friendly and install the
|
| 8 |
+
# declared requirements.txt. No third-party Python dependencies are currently
|
| 9 |
+
# needed by app.py.
|
| 10 |
+
RUN apt-get update \
|
| 11 |
+
&& apt-get install -y --no-install-recommends python3 python3-pip \
|
| 12 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 13 |
+
|
| 14 |
+
WORKDIR /app
|
| 15 |
+
COPY requirements.txt /app/requirements.txt
|
| 16 |
+
RUN if grep -qEv '^[[:space:]]*(#.*)?$' /app/requirements.txt; then \
|
| 17 |
+
python3 -m pip install --break-system-packages --no-cache-dir -r /app/requirements.txt; \
|
| 18 |
+
fi
|
| 19 |
+
COPY app.py /app/app.py
|
| 20 |
+
|
| 21 |
+
ENV NODE_ENV=production \
|
| 22 |
+
PORT=7860 \
|
| 23 |
+
FREELLMAPI_DATA_DIR=/data
|
| 24 |
+
|
| 25 |
EXPOSE 7860
|
| 26 |
|
| 27 |
+
# Explicit CMD is important: without a command, the inherited setpriv-based
|
| 28 |
+
# upstream entrypoint can emit "setpriv: No program specified".
|
| 29 |
+
ENTRYPOINT ["python3", "/app/app.py"]
|
| 30 |
+
CMD ["node", "server/dist/index.js"]
|
README.md
CHANGED
|
@@ -10,70 +10,66 @@ pinned: false
|
|
| 10 |
|
| 11 |
# FreeLLMAPI on Hugging Face Spaces
|
| 12 |
|
| 13 |
-
A Hugging Face Spaces deployment
|
| 14 |
|
| 15 |
-
|
| 16 |
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
| 20 |
|
| 21 |
```bash
|
| 22 |
-
|
| 23 |
```
|
| 24 |
|
| 25 |
-
|
| 26 |
-
4.
|
| 27 |
-
5. Wait for the Docker build
|
| 28 |
|
| 29 |
-
The
|
| 30 |
|
| 31 |
-
##
|
| 32 |
|
| 33 |
-
Replace the
|
| 34 |
|
| 35 |
-
- Dashboard/UI: `https://<
|
| 36 |
-
- OpenAI-compatible API
|
| 37 |
-
- Anthropic Messages API
|
| 38 |
-
-
|
| 39 |
|
| 40 |
-
|
| 41 |
|
| 42 |
## Claude Code
|
| 43 |
|
| 44 |
On the computer where Claude Code is installed, run:
|
| 45 |
|
| 46 |
```bash
|
| 47 |
-
npx freellmapi setup-claude
|
|
|
|
|
|
|
| 48 |
```
|
| 49 |
|
| 50 |
-
|
| 51 |
|
| 52 |
```bash
|
| 53 |
-
export ANTHROPIC_BASE_URL="https://<
|
| 54 |
export ANTHROPIC_AUTH_TOKEN="<your-freellmapi-unified-api-key>"
|
| 55 |
claude
|
| 56 |
```
|
| 57 |
|
| 58 |
-
Keep the key
|
| 59 |
-
|
| 60 |
-
## Other clients
|
| 61 |
-
|
| 62 |
-
For an OpenAI-compatible client, use:
|
| 63 |
-
|
| 64 |
-
```text
|
| 65 |
-
Base URL: https://<your-username>-<space-name>.hf.space/v1
|
| 66 |
-
API key: <your-freellmapi-unified-api-key>
|
| 67 |
-
```
|
| 68 |
|
| 69 |
-
##
|
| 70 |
|
| 71 |
-
- A
|
| 72 |
-
-
|
| 73 |
-
-
|
| 74 |
-
-
|
| 75 |
-
- FreeLLMAPI and upstream free-provider quotas/terms remain applicable. See the upstream README and documentation for the current limits and terms.
|
| 76 |
|
| 77 |
-
## Upstream
|
| 78 |
|
| 79 |
-
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
# FreeLLMAPI on Hugging Face Spaces
|
| 12 |
|
| 13 |
+
A Hugging Face Spaces deployment for [FreeLLMAPI](https://github.com/tashfeenahmed/freellmapi). It preserves the upstream web dashboard/UI, OpenAI-compatible API, and Anthropic Messages API used by Claude Code.
|
| 14 |
|
| 15 |
+
**Why Docker plus Python?** Hugging Face supports Python Spaces and Docker Spaces. The original FreeLLMAPI project itself is Node.js/React, so `requirements.txt` alone cannot run the whole product. This package includes a small Python launcher (`app.py`) and `requirements.txt`, while Docker runs the official upstream application image so its real UI and API remain intact.
|
| 16 |
|
| 17 |
+
## Deploy
|
| 18 |
+
|
| 19 |
+
1. Create a new Space with **Docker** as its SDK, or replace the files in the root of your existing Docker Space with this package.
|
| 20 |
+
2. Upload these files to the Space repository root: `README.md`, `Dockerfile`, `app.py`, `requirements.txt`, and `.gitignore`.
|
| 21 |
+
3. In **Settings → Variables and secrets**, add a secret named `ENCRYPTION_KEY`. Generate a 64-character hex key on your own computer:
|
| 22 |
|
| 23 |
```bash
|
| 24 |
+
python -c "import secrets; print(secrets.token_hex(32))"
|
| 25 |
```
|
| 26 |
|
| 27 |
+
Paste the output as the secret value. The launcher can generate a fallback key at `/data/.encryption-key`, but a stable secret is preferable for production use.
|
| 28 |
+
4. To retain the database and fallback key after restarts, create/attach a writable Hugging Face Storage Bucket volume mounted at `/data`. Without a persistent mounted volume, Space files may be lost on restarts.
|
| 29 |
+
5. Wait for the Docker build, then open the Space URL. Finish the initial account setup and add provider keys on the dashboard's **Keys** page.
|
| 30 |
|
| 31 |
+
The `setpriv: No program specified` error is addressed by launching the upstream entrypoint with an explicit default command (`node server/dist/index.js`) and a Python fallback when no command is provided.
|
| 32 |
|
| 33 |
+
## URLs
|
| 34 |
|
| 35 |
+
Replace the examples with your actual Space URL, usually `https://<username>-<space-name>.hf.space`.
|
| 36 |
|
| 37 |
+
- Dashboard/UI: `https://<username>-<space-name>.hf.space/`
|
| 38 |
+
- OpenAI-compatible API: `https://<username>-<space-name>.hf.space/v1`
|
| 39 |
+
- Anthropic Messages API / Claude Code: `https://<username>-<space-name>.hf.space` (the client calls `/v1/messages`)
|
| 40 |
+
- Model list check: `https://<username>-<space-name>.hf.space/v1/models`
|
| 41 |
|
| 42 |
+
Use the **unified FreeLLMAPI API key** shown in the dashboard. It is not your provider's key or a Hugging Face token.
|
| 43 |
|
| 44 |
## Claude Code
|
| 45 |
|
| 46 |
On the computer where Claude Code is installed, run:
|
| 47 |
|
| 48 |
```bash
|
| 49 |
+
npx freellmapi setup-claude \
|
| 50 |
+
--url "https://<username>-<space-name>.hf.space" \
|
| 51 |
+
--api-key "<your-freellmapi-unified-api-key>"
|
| 52 |
```
|
| 53 |
|
| 54 |
+
Or configure environment variables manually:
|
| 55 |
|
| 56 |
```bash
|
| 57 |
+
export ANTHROPIC_BASE_URL="https://<username>-<space-name>.hf.space"
|
| 58 |
export ANTHROPIC_AUTH_TOKEN="<your-freellmapi-unified-api-key>"
|
| 59 |
claude
|
| 60 |
```
|
| 61 |
|
| 62 |
+
Keep the key private: do not commit it to the Space repository or put it in browser/frontend code.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
|
| 64 |
+
## Notes
|
| 65 |
|
| 66 |
+
- A Space configured as **Private** can require Hugging Face authentication as well; external CLI compatibility depends on whether the client can authenticate to the private Space. Make the Space public only if appropriate and protect the app/API credentials.
|
| 67 |
+
- FreeLLMAPI is intended as a single-user router, not a public shared API service. Never share your unified API key.
|
| 68 |
+
- Keep `ENCRYPTION_KEY` stable if keeping an existing database. Changing it can make stored provider keys unreadable.
|
| 69 |
+
- The Python launcher uses only the standard library; `requirements.txt` is present for packaging consistency and currently has no third-party packages.
|
|
|
|
| 70 |
|
| 71 |
+
## Upstream
|
| 72 |
|
| 73 |
+
- [FreeLLMAPI source and docs](https://github.com/tashfeenahmed/freellmapi)
|
| 74 |
+
- [Hugging Face Docker Spaces documentation](https://huggingface.co/docs/hub/en/spaces-sdks-docker)
|
| 75 |
+
- [Hugging Face Spaces storage documentation](https://huggingface.co/docs/hub/main/spaces-storage)
|
app.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Hugging Face Spaces launcher for the upstream FreeLLMAPI container.
|
| 3 |
+
|
| 4 |
+
FreeLLMAPI itself is a Node.js/React application. This small Python launcher
|
| 5 |
+
prepares the Space environment and explicitly forwards the server command to
|
| 6 |
+
the upstream privilege-dropping entrypoint.
|
| 7 |
+
"""
|
| 8 |
+
from __future__ import annotations
|
| 9 |
+
|
| 10 |
+
import os
|
| 11 |
+
import secrets
|
| 12 |
+
import sys
|
| 13 |
+
from pathlib import Path
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def prepare_environment() -> None:
|
| 17 |
+
data_dir = Path(os.environ.get("FREELLMAPI_DATA_DIR", "/data"))
|
| 18 |
+
data_dir.mkdir(parents=True, exist_ok=True)
|
| 19 |
+
|
| 20 |
+
os.environ.setdefault("NODE_ENV", "production")
|
| 21 |
+
os.environ.setdefault("PORT", os.environ.get("SPACE_PORT", "7860"))
|
| 22 |
+
os.environ.setdefault("FREEAPI_DB_PATH", str(data_dir / "freeapi.db"))
|
| 23 |
+
|
| 24 |
+
# Keep a stable generated key beside the DB if a Space Secret was not set.
|
| 25 |
+
# For a persistent deployment, mount a writable bucket at /data.
|
| 26 |
+
if not os.environ.get("ENCRYPTION_KEY"):
|
| 27 |
+
key_file = Path(
|
| 28 |
+
os.environ.get("ENCRYPTION_KEY_FILE", str(data_dir / ".encryption-key"))
|
| 29 |
+
)
|
| 30 |
+
if not key_file.is_file() or key_file.stat().st_size == 0:
|
| 31 |
+
key_file.parent.mkdir(parents=True, exist_ok=True)
|
| 32 |
+
try:
|
| 33 |
+
descriptor = os.open(
|
| 34 |
+
key_file,
|
| 35 |
+
os.O_WRONLY | os.O_CREAT | os.O_EXCL,
|
| 36 |
+
0o600,
|
| 37 |
+
)
|
| 38 |
+
except FileExistsError:
|
| 39 |
+
# Another startup process created it first.
|
| 40 |
+
pass
|
| 41 |
+
else:
|
| 42 |
+
with os.fdopen(descriptor, "w", encoding="utf-8") as handle:
|
| 43 |
+
handle.write(secrets.token_hex(32))
|
| 44 |
+
handle.write("\n")
|
| 45 |
+
os.environ["ENCRYPTION_KEY"] = key_file.read_text(encoding="utf-8").strip()
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def main() -> None:
|
| 49 |
+
prepare_environment()
|
| 50 |
+
|
| 51 |
+
# Dockerfile supplies this CMD explicitly. Keep the fallback to avoid ever
|
| 52 |
+
# calling setpriv with an empty command (the error seen in the Space logs).
|
| 53 |
+
command = sys.argv[1:] or ["node", "server/dist/index.js"]
|
| 54 |
+
upstream_entrypoint = "/usr/local/bin/docker-entrypoint.sh"
|
| 55 |
+
if not os.path.isfile(upstream_entrypoint):
|
| 56 |
+
raise FileNotFoundError(
|
| 57 |
+
f"Expected upstream FreeLLMAPI entrypoint at {upstream_entrypoint}"
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
os.execv(upstream_entrypoint, [upstream_entrypoint, *command])
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
if __name__ == "__main__":
|
| 64 |
+
main()
|
requirements.txt
CHANGED
|
@@ -1,13 +1,3 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
rank_bm25>=0.2.2
|
| 5 |
-
torch>=2.0.0
|
| 6 |
-
tiktoken
|
| 7 |
-
aiohttp>=3.8.5
|
| 8 |
-
beautifulsoup4>=4.12.2
|
| 9 |
-
transformers>=4.36.0
|
| 10 |
-
accelerate>=0.25.0
|
| 11 |
-
bitsandbytes>=0.41.0
|
| 12 |
-
openai
|
| 13 |
-
faiss-cpu
|
|
|
|
| 1 |
+
# No third-party Python packages are required.
|
| 2 |
+
# app.py uses only the Python standard library; the actual FreeLLMAPI server/UI
|
| 3 |
+
# and its dependencies are shipped in the upstream container image.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|