Spaces:
Sleeping
Sleeping
| import os, pandas as pd, gradio as gr | |
| from huggingface_hub import hf_hub_download | |
| from llama_cpp import Llama # GGUF CPU backend | |
| # ---------- model loading (one‑time) ---------- | |
| MODEL_REPO = "MaziyarPanahi/gemma-2b-it-GGUF" | |
| MODEL_FILE = "gemma-2b-it.Q4_K_M.gguf" # 1.6 GB 4‑bit | |
| CTX_SIZE = 4096 | |
| model_path = hf_hub_download(repo_id=MODEL_REPO, filename=MODEL_FILE) | |
| llm = Llama( | |
| model_path=model_path, | |
| n_ctx=CTX_SIZE, | |
| n_threads=4 # чуть быстрее на 2 vCPU | |
| ) | |
| # ---------- analysis + generation ---------- | |
| def analyze_ads(file): | |
| print("DEBUG uploaded:", file.name, os.path.getsize(file.name), "bytes", flush=True) | |
| df = pd.read_csv(file.name) | |
| req = {"headline", "description", "impressions", "CTR", "form_opens", "spend"} | |
| if not req.issubset(df.columns): | |
| return f"Missing columns: {', '.join(req - set(df.columns))}" | |
| # convert numerics | |
| for col in ["impressions", "CTR", "form_opens", "spend"]: | |
| df[col] = pd.to_numeric(df[col], errors="coerce") | |
| df = df.dropna() | |
| df["engagement_rate"] = df["form_opens"] / df["impressions"] | |
| clicks = (df["CTR"] * df["impressions"]).replace(0, pd.NA) | |
| df["CPC"] = df["spend"] / clicks | |
| df["cost_per_form_open"] = df["spend"] / df["form_opens"].replace(0, pd.NA) | |
| # 1 best + 1 worst → короче промпт | |
| top = df.sort_values("CTR", ascending=False).head(1) | |
| worst = df.sort_values("CTR").head(1) | |
| def rows_to_text(sub): | |
| parts = [] | |
| for _, r in sub.iterrows(): | |
| parts.append( | |
| f"Headline: {r.headline}\n" | |
| f"Description: {r.description}\n" | |
| f"Impressions: {int(r.impressions)}, CTR: {r.CTR:.3f}, " | |
| f"Form Opens: {int(r.form_opens)}, ER: {r.engagement_rate:.3f}\n" | |
| f"Spend: ${r.spend:.2f}, CPC: ${r.CPC:.2f}, CPF: ${r.cost_per_form_open:.2f}\n" | |
| ) | |
| return "\n".join(parts) | |
| prompt = ( | |
| "You are a senior digital marketer.\n" | |
| "Analyse the high‑ and low‑performing ads below and deliver:\n" | |
| "1. Key patterns of winners.\n" | |
| "2. Weak points of losers.\n" | |
| "3. Three actionable creative improvements.\n\n" | |
| f"--- HIGH CTR ADS ---\n{rows_to_text(top)}\n" | |
| f"--- LOW CTR ADS ---\n{rows_to_text(worst)}" | |
| ) | |
| # stream=True → токены сразу в логах, ответ ~25‑30 с | |
| stream = llm( | |
| prompt, | |
| max_tokens=1500, | |
| temperature=0.2, | |
| top_p=0.8, | |
| stream=True | |
| ) | |
| out = [] | |
| for chunk in stream: | |
| tok = chunk["choices"][0]["text"] | |
| print(tok, end="", flush=True) # видно прогресс | |
| out.append(tok) | |
| return "".join(out).strip() | |
| # ---------- Gradio UI ---------- | |
| demo = gr.Interface( | |
| fn=analyze_ads, | |
| inputs=gr.File(label="CSV with: headline, description, impressions, CTR, form_opens, spend"), | |
| outputs=gr.Textbox(label="AI‑generated analysis & recommendations"), | |
| title="Ad Performance Analyzer (Gemma‑2b 4‑bit, CPU‑only)", | |
| description="Upload your ad data and get actionable insights without paid APIs." | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() |