Zeetay commited on
Commit
08b77b5
1 Parent(s): 98ee05e

prep for hf deploy

Browse files
Files changed (7) hide show
  1. .gitignore +2 -3
  2. Procfile +0 -1
  3. README.md +2 -2
  4. api/main.py +1 -1
  5. web/.env.example +1 -1
  6. web/app/page.tsx +15 -9
  7. web/components/BriefForm.tsx +4 -2
.gitignore CHANGED
@@ -1,6 +1,5 @@
1
- # Local notes / source briefs
2
- jd.txt
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 **Railway** (see [`Procfile`](Procfile)),
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 Railway backend URL. See the per-app `.env.example` files.
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 for Railway
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 Railway backend URL in the Vercel dashboard.
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 EXAMPLE_BRIEF: Brief = {
12
- brand: "FitFuel",
13
- product: "High-protein meal replacement shake",
14
- audience: "Busy professionals aged 25-40",
15
- tone: "Energetic and no-nonsense",
16
- goal: "Drive trial purchases",
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>(EXAMPLE_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 Railway 路 Frontend: Next.js on Vercel 路 Judge &
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 &amp; 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"}