cgeorgiaw HF Staff commited on
Commit
252669b
Β·
0 Parent(s):

Initial feedback API

Browse files
Files changed (4) hide show
  1. Dockerfile +12 -0
  2. README.md +34 -0
  3. main.py +134 -0
  4. requirements.txt +4 -0
Dockerfile ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY requirements.txt .
6
+ RUN pip install --no-cache-dir -r requirements.txt
7
+
8
+ COPY main.py .
9
+
10
+ EXPOSE 7860
11
+
12
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
README.md ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Hugging Science Feedback API
3
+ emoji: πŸ’¬
4
+ colorFrom: gray
5
+ colorTo: gray
6
+ sdk: docker
7
+ pinned: false
8
+ ---
9
+
10
+ # Hugging Science Feedback API
11
+
12
+ Minimal FastAPI service that accepts feedback submissions from huggingscience.co
13
+ and appends them to the [hugging-science/feedback](https://huggingface.co/datasets/hugging-science/feedback) dataset.
14
+
15
+ ## Setup
16
+
17
+ Add a Space secret named `HF_TOKEN` with a write-scoped token for the `hugging-science` org.
18
+
19
+ ## Endpoints
20
+
21
+ - `GET /` β€” health check
22
+ - `POST /submit` β€” submit a feedback entry
23
+
24
+ ## Payload
25
+
26
+ ```json
27
+ {
28
+ "type": "dataset | model | challenge | feedback",
29
+ "title": "Optional name or link",
30
+ "description": "Required β€” what should be added and why?",
31
+ "submitted_at": "2026-04-13T10:00:00Z",
32
+ "source": "huggingscience.co"
33
+ }
34
+ ```
main.py ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Hugging Science Feedback API
3
+ A minimal FastAPI app that accepts feedback submissions and appends them
4
+ to the hugging-science/feedback HF dataset.
5
+
6
+ Deploy as a HF Space (Docker SDK):
7
+ hugging-science/feedback-api
8
+
9
+ Required Space secret:
10
+ HF_TOKEN β€” a write-scoped token for the hugging-science org
11
+ """
12
+
13
+ import os
14
+ import json
15
+ import uuid
16
+ from datetime import datetime, timezone
17
+ from typing import Optional
18
+
19
+ from fastapi import FastAPI, HTTPException
20
+ from fastapi.middleware.cors import CORSMiddleware
21
+ from pydantic import BaseModel, field_validator
22
+ from huggingface_hub import HfApi, hf_hub_download
23
+ import tempfile
24
+
25
+ # ── Config ────────────────────────────────────────────────────────────────────
26
+
27
+ DATASET_REPO = "hugging-science/feedback"
28
+ FEEDBACK_FILE = "feedback.jsonl"
29
+
30
+ HF_TOKEN = os.environ.get("HF_TOKEN")
31
+ if not HF_TOKEN:
32
+ raise RuntimeError("HF_TOKEN secret is not set")
33
+
34
+ api = HfApi(token=HF_TOKEN)
35
+
36
+ # ── App ───────────────────────────────────────────────────────────────────────
37
+
38
+ app = FastAPI(title="Hugging Science Feedback API", version="1.0.0")
39
+
40
+ app.add_middleware(
41
+ CORSMiddleware,
42
+ allow_origins=["https://huggingscience.co", "http://localhost:5173"],
43
+ allow_methods=["POST", "GET"],
44
+ allow_headers=["Content-Type"],
45
+ )
46
+
47
+ # ── Schema ────────────────────────────────────────────────────────────────────
48
+
49
+ VALID_TYPES = {"dataset", "model", "challenge", "feedback"}
50
+
51
+ class FeedbackItem(BaseModel):
52
+ type: str
53
+ title: Optional[str] = None
54
+ description: str
55
+ submitted_at: Optional[str] = None
56
+ source: Optional[str] = "huggingscience.co"
57
+
58
+ @field_validator("type")
59
+ @classmethod
60
+ def validate_type(cls, v):
61
+ if v not in VALID_TYPES:
62
+ raise ValueError(f"type must be one of {VALID_TYPES}")
63
+ return v
64
+
65
+ @field_validator("description")
66
+ @classmethod
67
+ def validate_description(cls, v):
68
+ v = v.strip()
69
+ if len(v) < 5:
70
+ raise ValueError("description must be at least 5 characters")
71
+ if len(v) > 2000:
72
+ raise ValueError("description must be under 2000 characters")
73
+ return v
74
+
75
+ # ── Helpers ───────────────────────────────────────────────────────────────────
76
+
77
+ def load_existing() -> list[dict]:
78
+ """Download the current feedback.jsonl from the dataset, return as list."""
79
+ try:
80
+ path = hf_hub_download(
81
+ repo_id=DATASET_REPO,
82
+ filename=FEEDBACK_FILE,
83
+ repo_type="dataset",
84
+ token=HF_TOKEN,
85
+ )
86
+ with open(path) as f:
87
+ return [json.loads(line) for line in f if line.strip()]
88
+ except Exception:
89
+ # File doesn't exist yet β€” start fresh
90
+ return []
91
+
92
+
93
+ def save_feedback(rows: list[dict]) -> None:
94
+ """Upload the full feedback.jsonl back to the dataset."""
95
+ content = "\n".join(json.dumps(r, ensure_ascii=False) for r in rows) + "\n"
96
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".jsonl", delete=False) as f:
97
+ f.write(content)
98
+ tmp_path = f.name
99
+
100
+ api.upload_file(
101
+ path_or_fileobj=tmp_path,
102
+ path_in_repo=FEEDBACK_FILE,
103
+ repo_id=DATASET_REPO,
104
+ repo_type="dataset",
105
+ commit_message=f"Add feedback entry ({rows[-1]['id'][:8]})",
106
+ )
107
+
108
+ # ── Routes ────────────────────────────────────────────────────────────────────
109
+
110
+ @app.get("/")
111
+ def root():
112
+ return {"status": "ok", "service": "Hugging Science Feedback API"}
113
+
114
+
115
+ @app.post("/submit", status_code=201)
116
+ def submit_feedback(item: FeedbackItem):
117
+ entry = {
118
+ "id": str(uuid.uuid4()),
119
+ "type": item.type,
120
+ "title": item.title or "",
121
+ "description": item.description,
122
+ "submitted_at": item.submitted_at or datetime.now(timezone.utc).isoformat(),
123
+ "source": item.source or "huggingscience.co",
124
+ "status": "pending",
125
+ }
126
+
127
+ try:
128
+ rows = load_existing()
129
+ rows.append(entry)
130
+ save_feedback(rows)
131
+ except Exception as e:
132
+ raise HTTPException(status_code=500, detail=f"Failed to save feedback: {e}")
133
+
134
+ return {"ok": True, "id": entry["id"]}
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ fastapi>=0.111.0
2
+ uvicorn[standard]>=0.29.0
3
+ huggingface-hub>=0.23.0
4
+ pydantic>=2.0.0