Spaces:
Sleeping
Sleeping
Zeetay commited on
Commit 路
08b77b5
1
Parent(s): 98ee05e
prep for hf deploy
Browse files- .gitignore +2 -3
- Procfile +0 -1
- README.md +2 -2
- api/main.py +1 -1
- web/.env.example +1 -1
- web/app/page.tsx +15 -9
- web/components/BriefForm.tsx +4 -2
.gitignore
CHANGED
|
@@ -1,6 +1,5 @@
|
|
| 1 |
-
# Local notes
|
| 2 |
-
|
| 3 |
-
prompt.txt
|
| 4 |
|
| 5 |
# Secrets
|
| 6 |
.env
|
|
|
|
| 1 |
+
# Local notes
|
| 2 |
+
local/
|
|
|
|
| 3 |
|
| 4 |
# Secrets
|
| 5 |
.env
|
Procfile
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
web: uvicorn api.main:app --host 0.0.0.0 --port $PORT
|
|
|
|
|
|
README.md
CHANGED
|
@@ -32,14 +32,14 @@ with a FitFuel example), click **Generate**, and watch the agent retrieve past
|
|
| 32 |
winners, generate three variants, and score each on four dimensions in real time.
|
| 33 |
|
| 34 |
- **Frontend:** Next.js (App Router) deployed on **Vercel**, in [`web/`](web/).
|
| 35 |
-
- **Backend:** the FastAPI app deployed on **
|
| 36 |
with CORS, a per-IP rate limit on `/run`, a `/stats` endpoint, and a startup
|
| 37 |
seed so a fresh deployment shows non-zero stats and working retrieval on the
|
| 38 |
very first visit.
|
| 39 |
|
| 40 |
> Deployment env vars: the backend needs `GROQ_API_KEY` (and optionally
|
| 41 |
> `CORS_ORIGINS` = your Vercel URL); the frontend needs `NEXT_PUBLIC_API_URL` =
|
| 42 |
-
> your
|
| 43 |
|
| 44 |
---
|
| 45 |
|
|
|
|
| 32 |
winners, generate three variants, and score each on four dimensions in real time.
|
| 33 |
|
| 34 |
- **Frontend:** Next.js (App Router) deployed on **Vercel**, in [`web/`](web/).
|
| 35 |
+
- **Backend:** the FastAPI app deployed on **Hugging Face Spaces** (Docker, see [`Dockerfile`](Dockerfile)),
|
| 36 |
with CORS, a per-IP rate limit on `/run`, a `/stats` endpoint, and a startup
|
| 37 |
seed so a fresh deployment shows non-zero stats and working retrieval on the
|
| 38 |
very first visit.
|
| 39 |
|
| 40 |
> Deployment env vars: the backend needs `GROQ_API_KEY` (and optionally
|
| 41 |
> `CORS_ORIGINS` = your Vercel URL); the frontend needs `NEXT_PUBLIC_API_URL` =
|
| 42 |
+
> your Hugging Face Space URL. See the per-app `.env.example` files.
|
| 43 |
|
| 44 |
---
|
| 45 |
|
api/main.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
"""FastAPI app for the hosted public demo.
|
| 2 |
|
| 3 |
Endpoints:
|
| 4 |
-
- GET /health -> liveness probe
|
| 5 |
- GET /stats -> live counts (runs, golden, flagged) for the frontend strip
|
| 6 |
- POST /run -> full agent loop (rate limited), returns outputs + scores
|
| 7 |
|
|
|
|
| 1 |
"""FastAPI app for the hosted public demo.
|
| 2 |
|
| 3 |
Endpoints:
|
| 4 |
+
- GET /health -> liveness probe
|
| 5 |
- GET /stats -> live counts (runs, golden, flagged) for the frontend strip
|
| 6 |
- POST /run -> full agent loop (rate limited), returns outputs + scores
|
| 7 |
|
web/.env.example
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
# URL of the FastAPI backend the frontend calls (/run and /stats).
|
| 2 |
# Locally, point this at the running uvicorn server (default port 8000).
|
| 3 |
-
# In production, set it to your
|
| 4 |
NEXT_PUBLIC_API_URL=http://localhost:3000
|
|
|
|
| 1 |
# URL of the FastAPI backend the frontend calls (/run and /stats).
|
| 2 |
# Locally, point this at the running uvicorn server (default port 8000).
|
| 3 |
+
# In production, set it to your Hugging Face Space URL in the Vercel dashboard.
|
| 4 |
NEXT_PUBLIC_API_URL=http://localhost:3000
|
web/app/page.tsx
CHANGED
|
@@ -8,12 +8,12 @@ import { StatsStrip } from "@/components/StatsStrip";
|
|
| 8 |
import { ApiError, getStats, runAgent } from "@/lib/api";
|
| 9 |
import type { Brief, RunResponse, Stats } from "@/lib/types";
|
| 10 |
|
| 11 |
-
const
|
| 12 |
-
brand: "
|
| 13 |
-
product: "
|
| 14 |
-
audience: "
|
| 15 |
-
tone: "
|
| 16 |
-
goal: "
|
| 17 |
};
|
| 18 |
|
| 19 |
// Ordered variant rendering regardless of backend ordering.
|
|
@@ -30,7 +30,7 @@ const LOADING_PHASES = [
|
|
| 30 |
];
|
| 31 |
|
| 32 |
export default function Page() {
|
| 33 |
-
const [brief, setBrief] = useState<Brief>(
|
| 34 |
const [loading, setLoading] = useState(false);
|
| 35 |
const [phase, setPhase] = useState(0);
|
| 36 |
const [result, setResult] = useState<RunResponse | null>(null);
|
|
@@ -69,7 +69,12 @@ export default function Page() {
|
|
| 69 |
const onChange = (key: keyof Brief, value: string) =>
|
| 70 |
setBrief((b) => ({ ...b, [key]: value }));
|
| 71 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
const onSubmit = async () => {
|
|
|
|
| 73 |
setLoading(true);
|
| 74 |
setError(null);
|
| 75 |
try {
|
|
@@ -115,6 +120,7 @@ export default function Page() {
|
|
| 115 |
onSubmit={onSubmit}
|
| 116 |
loading={loading}
|
| 117 |
statusText={LOADING_PHASES[phase]}
|
|
|
|
| 118 |
/>
|
| 119 |
|
| 120 |
{/* Error */}
|
|
@@ -153,8 +159,8 @@ export default function Page() {
|
|
| 153 |
</div>
|
| 154 |
|
| 155 |
<footer className="mt-10 border-t border-ink-700 pt-5 font-mono text-[11px] text-zinc-600">
|
| 156 |
-
Backend: FastAPI on
|
| 157 |
-
generator: llama-3.3-70b
|
| 158 |
</footer>
|
| 159 |
</main>
|
| 160 |
);
|
|
|
|
| 8 |
import { ApiError, getStats, runAgent } from "@/lib/api";
|
| 9 |
import type { Brief, RunResponse, Stats } from "@/lib/types";
|
| 10 |
|
| 11 |
+
const EMPTY_BRIEF: Brief = {
|
| 12 |
+
brand: "",
|
| 13 |
+
product: "",
|
| 14 |
+
audience: "",
|
| 15 |
+
tone: "",
|
| 16 |
+
goal: "",
|
| 17 |
};
|
| 18 |
|
| 19 |
// Ordered variant rendering regardless of backend ordering.
|
|
|
|
| 30 |
];
|
| 31 |
|
| 32 |
export default function Page() {
|
| 33 |
+
const [brief, setBrief] = useState<Brief>(EMPTY_BRIEF);
|
| 34 |
const [loading, setLoading] = useState(false);
|
| 35 |
const [phase, setPhase] = useState(0);
|
| 36 |
const [result, setResult] = useState<RunResponse | null>(null);
|
|
|
|
| 69 |
const onChange = (key: keyof Brief, value: string) =>
|
| 70 |
setBrief((b) => ({ ...b, [key]: value }));
|
| 71 |
|
| 72 |
+
const isComplete = (Object.keys(brief) as (keyof Brief)[]).every(
|
| 73 |
+
(k) => brief[k].trim() !== "",
|
| 74 |
+
);
|
| 75 |
+
|
| 76 |
const onSubmit = async () => {
|
| 77 |
+
if (!isComplete) return;
|
| 78 |
setLoading(true);
|
| 79 |
setError(null);
|
| 80 |
try {
|
|
|
|
| 120 |
onSubmit={onSubmit}
|
| 121 |
loading={loading}
|
| 122 |
statusText={LOADING_PHASES[phase]}
|
| 123 |
+
canSubmit={isComplete}
|
| 124 |
/>
|
| 125 |
|
| 126 |
{/* Error */}
|
|
|
|
| 159 |
</div>
|
| 160 |
|
| 161 |
<footer className="mt-10 border-t border-ink-700 pt-5 font-mono text-[11px] text-zinc-600">
|
| 162 |
+
Backend: FastAPI on Hugging Face Spaces 路 Frontend: Next.js on Vercel 路
|
| 163 |
+
Judge & generator: llama-3.3-70b
|
| 164 |
</footer>
|
| 165 |
</main>
|
| 166 |
);
|
web/components/BriefForm.tsx
CHANGED
|
@@ -14,12 +14,14 @@ export function BriefForm({
|
|
| 14 |
onSubmit,
|
| 15 |
loading,
|
| 16 |
statusText,
|
|
|
|
| 17 |
}: {
|
| 18 |
brief: Brief;
|
| 19 |
onChange: (key: keyof Brief, value: string) => void;
|
| 20 |
onSubmit: () => void;
|
| 21 |
loading: boolean;
|
| 22 |
statusText: string;
|
|
|
|
| 23 |
}) {
|
| 24 |
return (
|
| 25 |
<form
|
|
@@ -66,11 +68,11 @@ export function BriefForm({
|
|
| 66 |
loading ? "animate-soft-pulse" : ""
|
| 67 |
}`}
|
| 68 |
>
|
| 69 |
-
{loading ? statusText : "Ready"}
|
| 70 |
</p>
|
| 71 |
<button
|
| 72 |
type="submit"
|
| 73 |
-
disabled={loading}
|
| 74 |
className="border border-zinc-300 bg-zinc-100 px-4 py-2 text-sm font-medium text-ink-950 transition-colors hover:bg-white disabled:cursor-not-allowed disabled:border-ink-600 disabled:bg-ink-700 disabled:text-zinc-400"
|
| 75 |
>
|
| 76 |
{loading ? "Running..." : "Generate Copy"}
|
|
|
|
| 14 |
onSubmit,
|
| 15 |
loading,
|
| 16 |
statusText,
|
| 17 |
+
canSubmit,
|
| 18 |
}: {
|
| 19 |
brief: Brief;
|
| 20 |
onChange: (key: keyof Brief, value: string) => void;
|
| 21 |
onSubmit: () => void;
|
| 22 |
loading: boolean;
|
| 23 |
statusText: string;
|
| 24 |
+
canSubmit: boolean;
|
| 25 |
}) {
|
| 26 |
return (
|
| 27 |
<form
|
|
|
|
| 68 |
loading ? "animate-soft-pulse" : ""
|
| 69 |
}`}
|
| 70 |
>
|
| 71 |
+
{loading ? statusText : canSubmit ? "Ready" : "Fill in the brief to generate"}
|
| 72 |
</p>
|
| 73 |
<button
|
| 74 |
type="submit"
|
| 75 |
+
disabled={loading || !canSubmit}
|
| 76 |
className="border border-zinc-300 bg-zinc-100 px-4 py-2 text-sm font-medium text-ink-950 transition-colors hover:bg-white disabled:cursor-not-allowed disabled:border-ink-600 disabled:bg-ink-700 disabled:text-zinc-400"
|
| 77 |
>
|
| 78 |
{loading ? "Running..." : "Generate Copy"}
|