| import os, io, json, zipfile, hashlib, time |
| from typing import List, Dict, Any, Optional, Tuple |
| import gradio as gr |
| from pydantic import BaseModel |
| from tenacity import retry, stop_after_attempt, wait_exponential, RetryError |
|
|
| |
| try: |
| from dotenv import load_dotenv |
| load_dotenv() |
| except Exception: |
| pass |
|
|
| |
| try: |
| from openai import OpenAI |
| except Exception: |
| OpenAI = None |
|
|
| try: |
| import anthropic |
| from anthropic import NotFoundError as AnthropicNotFound |
| except Exception: |
| anthropic = None |
| AnthropicNotFound = Exception |
|
|
| from firecrawl import Firecrawl |
|
|
| |
| def _to_dict(obj: Any) -> Any: |
| if isinstance(obj, BaseModel): |
| return obj.model_dump() |
| if isinstance(obj, dict): |
| return {k: _to_dict(v) for k, v in obj.items()} |
| if isinstance(obj, (list, tuple)): |
| return [_to_dict(v) for v in obj] |
| if hasattr(obj, "__dict__") and not isinstance(obj, (str, bytes)): |
| try: |
| return {k: _to_dict(v) for k, v in vars(obj).items()} |
| except Exception: |
| pass |
| return obj |
|
|
| def _pretty_json(data: Any, limit: int = 300_000) -> str: |
| try: |
| s = json.dumps(_to_dict(data), indent=2) |
| return s[:limit] |
| except Exception as e: |
| return f"<!> Could not serialize to JSON: {e}" |
|
|
| def _listify(x) -> List[Any]: |
| if x is None: |
| return [] |
| if isinstance(x, list): |
| return x |
| return [x] |
|
|
| def _hash(s: str) -> str: |
| return hashlib.sha1(s.encode("utf-8")).hexdigest()[:10] |
|
|
| |
| class Keys(BaseModel): |
| openai: Optional[str] = None |
| anthropic: Optional[str] = None |
| firecrawl: Optional[str] = None |
|
|
| def resolve_keys(s: Keys) -> Keys: |
| return Keys( |
| openai=s.openai or os.getenv("OPENAI_API_KEY"), |
| anthropic=s.anthropic or os.getenv("ANTHROPIC_API_KEY"), |
| firecrawl=s.firecrawl or os.getenv("FIRECRAWL_API_KEY"), |
| ) |
|
|
| |
| def fc_client(s: Keys) -> Firecrawl: |
| k = resolve_keys(s) |
| if not k.firecrawl: |
| raise gr.Error("Missing FIRECRAWL_API_KEY. Enter it in Keys → Save.") |
| return Firecrawl(api_key=k.firecrawl) |
|
|
| @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=1, max=8)) |
| def fc_search(s: Keys, query: str, limit: int = 5, scrape_formats: Optional[List[str]] = None, location: Optional[str] = None) -> Dict[str, Any]: |
| fc = fc_client(s) |
| kwargs: Dict[str, Any] = {"query": query, "limit": limit} |
| if location: kwargs["location"] = location |
| if scrape_formats: kwargs["scrape_options"] = {"formats": scrape_formats} |
| res = fc.search(**kwargs) |
| return _to_dict(res) |
|
|
| @retry(stop=stop_after_attempt(2), wait=wait_exponential(multiplier=1, min=1, max=10)) |
| def fc_scrape(s: Keys, url: str, formats: Optional[List[str]] = None, timeout_ms: Optional[int] = None, mobile: bool = False) -> Dict[str, Any]: |
| fc = fc_client(s) |
| kwargs: Dict[str, Any] = {"url": url} |
| if formats: kwargs["formats"] = formats |
| if timeout_ms: kwargs["timeout"] = min(int(timeout_ms), 40000) |
| if mobile: kwargs["mobile"] = True |
| res = fc.scrape(**kwargs) |
| return _to_dict(res) |
|
|
| @retry(stop=stop_after_attempt(2), wait=wait_exponential(multiplier=1, min=1, max=10)) |
| def fc_crawl(s: Keys, url: str, max_pages: int = 25, formats: Optional[List[str]] = None) -> Dict[str, Any]: |
| fc = fc_client(s) |
| kwargs: Dict[str, Any] = {"url": url, "limit": max_pages} |
| if formats: kwargs["scrape_options"] = {"formats": formats} |
| res = fc.crawl(**kwargs) |
| return _to_dict(res) |
|
|
| |
| SYSTEM_STEER = ( |
| "You are ZEN's VibeCoder: extract web insights, generate clean scaffolds, " |
| "and produce production-ready artifacts. Prefer structured outlines, code blocks, and checklists. " |
| "When asked to clone or refactor, output file trees and exact text." |
| ) |
|
|
| def use_openai(s: Keys): |
| k = resolve_keys(s) |
| if not k.openai: raise gr.Error("Missing OPENAI_API_KEY.") |
| if OpenAI is None: raise gr.Error("OpenAI SDK not installed.") |
| return OpenAI(api_key=k.openai) |
|
|
| def use_anthropic(s: Keys): |
| k = resolve_keys(s) |
| if not k.anthropic: raise gr.Error("Missing ANTHROPIC_API_KEY.") |
| if anthropic is None: raise gr.Error("Anthropic SDK not installed.") |
| return anthropic.Anthropic(api_key=k.anthropic) |
|
|
| ANTHROPIC_FALLBACKS = [ |
| "claude-3-5-sonnet-20240620", |
| ] |
| OPENAI_FALLBACKS = ["gpt-4o", "gpt-4-turbo"] |
|
|
| def llm_once_openai(s: Keys, model: str, prompt: str, ctx: str, temp: float) -> str: |
| client = use_openai(s) |
| resp = client.chat.completions.create( |
| model=model, temperature=temp, |
| messages=[{"role":"system","content":SYSTEM_STEER}, |
| {"role":"user","content":f"{prompt}\n\n=== SOURCE (markdown) ===\n{ctx}"}] |
| ) |
| return (resp.choices[0].message.content or "").strip() |
|
|
| def llm_once_anthropic(s: Keys, model: str, prompt: str, ctx: str, temp: float) -> str: |
| client = use_anthropic(s) |
| resp = client.messages.create( |
| model=model, max_tokens=4000, temperature=temp, system=SYSTEM_STEER, |
| messages=[{"role":"user","content":f"{prompt}\n\n=== SOURCE (markdown) ===\n{ctx}"}], |
| ) |
| out=[] |
| for blk in resp.content: |
| t=getattr(blk,"text",None) |
| if t: out.append(t) |
| return "".join(out).strip() |
|
|
| def llm_summarize(s: Keys, provider: str, model_name: str, prompt: str, ctx_md: str, temp: float=0.4) -> str: |
| ctx = (ctx_md or "")[:150000] |
| if provider == "openai": |
| candidates = [model_name] + OPENAI_FALLBACKS if model_name else OPENAI_FALLBACKS |
| last=None |
| for m in candidates: |
| try: return llm_once_openai(s, m, prompt, ctx, temp) |
| except Exception as e: last=e; continue |
| raise gr.Error(f"OpenAI failed across fallbacks: {last}") |
| else: |
| candidates = [model_name] + ANTHROPIC_FALLBACKS if model_name else ANTHROPIC_FALLBACKS |
| last=None |
| for m in candidates: |
| try: return llm_once_anthropic(s, m, prompt, ctx, temp) |
| except AnthropicNotFound as e: last=e; continue |
| except Exception as e: last=e; continue |
| raise gr.Error(f"Anthropic failed across fallbacks: {last}") |
|
|
| |
| def pack_zip_pages(pages: List[Dict[str, Any]]) -> bytes: |
| mem = io.BytesIO() |
| with zipfile.ZipFile(mem, mode="w", compression=zipfile.ZIP_DEFLATED) as zf: |
| manifest = [] |
| for i, p in enumerate(pages, start=1): |
| url = p.get("url") or p.get("metadata", {}).get("sourceURL") or f"page_{i}" |
| slug = _hash(str(url)) |
| md = p.get("markdown") or p.get("data", {}).get("markdown") or p.get("content") or "" |
| html = p.get("html") or p.get("data", {}).get("html") or "" |
| links = p.get("links") or p.get("data", {}).get("links") or [] |
| title = p.get("title") or p.get("metadata", {}).get("title") |
| if md: zf.writestr(f"{i:03d}_{slug}.md", md) |
| if html: zf.writestr(f"{i:03d}_{slug}.html", html) |
| manifest.append({"url": url, "title": title, "links": links}) |
| zf.writestr("manifest.json", json.dumps(manifest, indent=2)) |
| mem.seek(0); return mem.read() |
|
|
| def pack_zip_corpus(corpus: List[Dict[str, Any]], merged_md: str, extras: Dict[str,str]) -> bytes: |
| mem = io.BytesIO() |
| with zipfile.ZipFile(mem, mode="w", compression=zipfile.ZIP_DEFLATED) as zf: |
| zf.writestr("corpus_merged.md", merged_md or "") |
| zf.writestr("corpus_manifest.json", json.dumps(corpus, indent=2)) |
| for name,content in extras.items(): |
| zf.writestr(name, content) |
| mem.seek(0); return mem.read() |
|
|
| |
| def save_keys(openai_key, anthropic_key, firecrawl_key): |
| return Keys( |
| openai=(openai_key or "").strip() or None, |
| anthropic=(anthropic_key or "").strip() or None, |
| firecrawl=(firecrawl_key or "").strip() or None, |
| ), gr.Info("Keys saved to this session. (Env vars still apply if set.)") |
|
|
| def action_search(sess: Keys, query: str, limit: int, scrape_content: bool, location: str): |
| if not query.strip(): raise gr.Error("Enter a search query.") |
| formats = ["markdown", "links"] if scrape_content else None |
| res = fc_search(sess, query=query.strip(), limit=limit, scrape_formats=formats, location=(location or None)) |
| data = res.get("data", res) |
| items: List[Any] = [] |
| if isinstance(data, dict): |
| for bucket in ("web", "news", "images", "videos", "discussion"): |
| b = data.get(bucket) |
| if b: |
| items.extend(_listify(_to_dict(b))) |
| elif isinstance(data, list): |
| items = _to_dict(data) |
| else: |
| items = _listify(_to_dict(data)) |
| if not items: |
| return _pretty_json(res), res |
| return json.dumps(items, indent=2), items |
|
|
| def action_scrape(sess: Keys, url: str, mobile: bool, formats_sel: List[str], timeout_ms: int): |
| if not url.strip(): raise gr.Error("Enter a URL.") |
| formats = formats_sel or ["markdown", "links"] |
| try: |
| out = fc_scrape(sess, url.strip(), formats=formats, timeout_ms=(timeout_ms or 15000), mobile=mobile) |
| pretty = _pretty_json(out) |
| md = out.get("markdown") or out.get("data", {}).get("markdown") or out.get("content") or "" |
| return pretty, md, out |
| except RetryError as e: |
| return f"<!> Scrape timed out after retries. Try increasing timeout, unchecking 'mobile', or limiting formats.\n\n{e}", "", {} |
| except Exception as e: |
| return f"<!> Scrape error: {e}", "", {} |
|
|
| def action_crawl(sess: Keys, base_url: str, max_pages: int, formats_sel: List[str]): |
| if not base_url.strip(): raise gr.Error("Enter a base URL to crawl.") |
| formats = formats_sel or ["markdown", "links"] |
| try: |
| out = fc_crawl(sess, base_url.strip(), max_pages=max_pages, formats=formats) |
| pages = out.get("data") |
| if not isinstance(pages, list) or not pages: raise gr.Error("Crawl returned no pages.") |
| zip_bytes = pack_zip_pages(pages) |
| return gr.File.update(value=io.BytesIO(zip_bytes), visible=True, filename="site_clone.zip"), f"Crawled {len(pages)} pages. ZIP is ready.", pages |
| except RetryError as e: |
| return gr.File.update(visible=False), f"<!> Crawl timed out after retries. Reduce Max Pages or try again.\n\n{e}", [] |
| except Exception as e: |
| return gr.File.update(visible=False), f"<!> Crawl error: {e}", [] |
|
|
| def action_generate(sess: Keys, provider: str, model_name: str, sys_prompt: str, user_prompt: str, context_md: str, temp: float): |
| if not user_prompt.strip(): raise gr.Error("Enter a prompt or click a starter tile.") |
| model = (model_name or "").strip() |
| steer = (sys_prompt or "").strip() |
| prompt = (("SYSTEM:\n" + steer + "\n\n") if steer else "") + user_prompt.strip() |
| out = llm_summarize(sess, provider, model, prompt, context_md or "", temp=temp) |
| return out |
|
|
| |
| def corpus_normalize_items(items: Any) -> List[Dict[str, Any]]: |
| """Accepts list/dict/raw and returns a list of page-like dicts with url/title/markdown/html/links.""" |
| out=[] |
| if isinstance(items, dict): items=[items] |
| for it in _listify(items): |
| d=_to_dict(it) |
| if not isinstance(d, dict): continue |
| url = d.get("url") or d.get("metadata",{}).get("sourceURL") or d.get("link") or "" |
| title = d.get("title") or d.get("metadata",{}).get("title") or d.get("name") or "" |
| md = d.get("markdown") or d.get("data",{}).get("markdown") or d.get("content") or "" |
| html = d.get("html") or d.get("data",{}).get("html") or "" |
| links = d.get("links") or d.get("data",{}).get("links") or [] |
| out.append({"url":url,"title":title,"markdown":md,"html":html,"links":links}) |
| return out |
|
|
| def corpus_add(corpus: List[Dict[str,Any]], items: Any, include_filter: str, exclude_filter: str, dedupe: bool) -> Tuple[List[Dict[str,Any]], str]: |
| added=0 |
| existing = set(_hash(x.get("url","")) for x in corpus if x.get("url")) |
| inc = (include_filter or "").strip().lower() |
| exc = (exclude_filter or "").strip().lower() |
| for rec in corpus_normalize_items(items): |
| url = (rec.get("url") or "").lower() |
| title = (rec.get("title") or "").lower() |
| if inc and (inc not in url and inc not in title): continue |
| if exc and (exc in url or exc in title): continue |
| if dedupe and rec.get("url") and _hash(rec["url"]) in existing: continue |
| corpus.append(rec); added+=1 |
| if rec.get("url"): existing.add(_hash(rec["url"])) |
| return corpus, f"Added {added} item(s). Corpus size: {len(corpus)}." |
|
|
| def corpus_list(corpus: List[Dict[str,Any]]) -> str: |
| lines=[] |
| for i,rec in enumerate(corpus,1): |
| url = rec.get("url") or "(no url)" |
| title = rec.get("title") or "(no title)" |
| mlen = len(rec.get("markdown") or "") |
| lines.append(f"{i:03d}. {title} — {url} [md:{mlen} chars]") |
| if not lines: return "_(empty)_" |
| return "\n".join(lines) |
|
|
| def corpus_clear() -> Tuple[List[Dict[str,Any]], str]: |
| return [], "Corpus cleared." |
|
|
| def corpus_merge_md(corpus: List[Dict[str,Any]]) -> str: |
| parts=[] |
| for rec in corpus: |
| hdr = f"### {rec.get('title') or rec.get('url') or 'Untitled'}" |
| md = rec.get("markdown") or "" |
| if md: parts.append(hdr+"\n\n"+md.strip()) |
| return "\n\n---\n\n".join(parts) |
|
|
| def corpus_export(corpus: List[Dict[str,Any]], merged: str, extras: Dict[str,str]): |
| data = pack_zip_corpus(corpus, merged, extras) |
| return gr.File.update(value=io.BytesIO(data), visible=True, filename=f"corpus_{int(time.time())}.zip") |
|
|
| def dual_generate(sess: Keys, model_openai: str, model_anthropic: str, sys_prompt: str, user_prompt: str, ctx_md: str, temp: float): |
| if not user_prompt.strip(): raise gr.Error("Enter a prompt or use a tile.") |
| steer = (sys_prompt or "").strip() |
| prompt = (("SYSTEM:\n" + steer + "\n\n") if steer else "") + user_prompt.strip() |
| ctx = ctx_md or "" |
| |
| oa_txt, an_txt = "", "" |
| try: |
| oa_txt = llm_summarize(sess, "openai", model_openai or "", prompt, ctx, temp) |
| except Exception as e: |
| oa_txt = f"<!> OpenAI error: {e}" |
| try: |
| an_txt = llm_summarize(sess, "anthropic", model_anthropic or "", prompt, ctx, temp) |
| except Exception as e: |
| an_txt = f"<!> Anthropic error: {e}" |
| |
| md = ( |
| "### OpenAI\n\n" + (oa_txt or "_(empty)_") + |
| "\n\n---\n\n" + |
| "### Anthropic\n\n" + (an_txt or "_(empty)_") |
| ) |
| return md |
|
|
| def scaffold_from_corpus(corpus_md: str, site_name: str = "zen-scan"): |
| """ |
| Produce a tiny site/docs scaffold as a ZIP: |
| /README.md |
| /docs/index.md (from corpus) |
| /docs/summary.md (brief) |
| """ |
| summary = (corpus_md[:1800] + ("..." if len(corpus_md) > 1800 else "")) if corpus_md else "No content." |
| mem = io.BytesIO() |
| with zipfile.ZipFile(mem, "w", zipfile.ZIP_DEFLATED) as zf: |
| zf.writestr("README.md", f"# {site_name}\n\nAuto-generated scaffold from ZEN VibeCoder corpus.\n") |
| zf.writestr("docs/index.md", corpus_md or "# Empty\n") |
| zf.writestr("docs/summary.md", f"# Summary\n\n{summary}\n") |
| mem.seek(0) |
| return gr.File.update(value=mem, visible=True, filename=f"{site_name}_scaffold.zip") |
|
|
| |
| with gr.Blocks(css="#keys .wrap.svelte-1ipelgc { filter: none !important; }") as demo: |
| gr.Markdown("## ZEN VibeCoder — Web Clone & Research Foundry") |
| session_state = gr.State(Keys()) |
|
|
| |
| last_search_obj = gr.State({}) |
| last_scrape_obj = gr.State({}) |
| last_crawl_pages = gr.State([]) |
| corpus_state = gr.State([]) |
| merged_md_state = gr.State("") |
|
|
| with gr.Accordion("🔐 Keys (session)", open=True): |
| with gr.Row(): |
| openai_key = gr.Textbox(label="OPENAI_API_KEY (GPT-4o / fallbacks)", type="password", placeholder="sk-...", value=os.getenv("OPENAI_API_KEY") or "") |
| anthropic_key = gr.Textbox(label="ANTHROPIC_API_KEY (Claude Sonnet)", type="password", placeholder="anthropic-key...", value=os.getenv("ANTHROPIC_API_KEY") or "") |
| firecrawl_key = gr.Textbox(label="FIRECRAWL_API_KEY", type="password", placeholder="fc-...", value=os.getenv("FIRECRAWL_API_KEY") or "") |
| save_btn = gr.Button("Save keys", variant="primary") |
| save_msg = gr.Markdown() |
| save_btn.click(save_keys, [openai_key, anthropic_key, firecrawl_key], [session_state, save_msg]) |
|
|
| with gr.Tabs(): |
| |
| with gr.Tab("🔎 Search"): |
| query = gr.Textbox(label="Query", placeholder='ex: site:docs "vector database" 2025') |
| with gr.Row(): |
| limit = gr.Slider(1, 20, value=6, step=1, label="Limit") |
| scrape_content = gr.Checkbox(label="Also scrape results (markdown + links)", value=True) |
| location = gr.Textbox(label="Location (optional)", placeholder="ex: Germany") |
| go_search = gr.Button("Run Search", variant="primary") |
| search_json = gr.Code(label="Results JSON", language="json") |
|
|
| def _search(sess, q, lmt, scp, loc): |
| txt, obj = action_search(sess, q, lmt, scp, loc) |
| return txt, obj |
| go_search.click(_search, [session_state, query, limit, scrape_content, location], [search_json, last_search_obj]) |
|
|
| |
| with gr.Tab("🕸️ Scrape • Crawl • Clone"): |
| with gr.Row(): |
| target_url = gr.Textbox(label="URL to Scrape", placeholder="https://example.com") |
| timeout_ms = gr.Number(label="Timeout (ms, max 40000)", value=15000) |
| with gr.Row(): |
| formats_sel = gr.CheckboxGroup(choices=["markdown","html","links","screenshot"], value=["markdown","links"], label="Formats") |
| mobile = gr.Checkbox(label="Emulate mobile", value=False) |
| run_scrape = gr.Button("Scrape URL", variant="primary") |
| scrape_json = gr.Code(label="Raw Response (JSON)", language="json") |
| scrape_md = gr.Markdown(label="Markdown Preview") |
| run_scrape.click(action_scrape, [session_state, target_url, mobile, formats_sel, timeout_ms], [scrape_json, scrape_md, last_scrape_obj]) |
|
|
| gr.Markdown("---") |
|
|
| with gr.Row(): |
| base_url = gr.Textbox(label="Base URL to Crawl", placeholder="https://docs.firecrawl.dev") |
| max_pages = gr.Slider(1, 200, value=25, step=1, label="Max Pages") |
| formats_crawl = gr.CheckboxGroup(choices=["markdown","html","links"], value=["markdown","links"], label="Crawl Formats") |
| run_crawl = gr.Button("Crawl & Build ZIP", variant="primary") |
| zip_file = gr.File(label="Clone ZIP", visible=False) |
| crawl_status = gr.Markdown() |
| run_crawl.click(action_crawl, [session_state, base_url, max_pages, formats_crawl], [zip_file, crawl_status, last_crawl_pages]) |
|
|
| |
| with gr.Tab("📦 Corpus & Build"): |
| with gr.Row(): |
| include_filter = gr.Textbox(label="Include filter (substring)", placeholder="docs, api, blog...") |
| exclude_filter = gr.Textbox(label="Exclude filter (substring)", placeholder="cdn, tracking, terms...") |
| dedupe = gr.Checkbox(label="Dedupe by URL", value=True) |
| with gr.Row(): |
| add_from_search = gr.Button("Add from Last Search") |
| add_from_scrape = gr.Button("Add from Last Scrape") |
| add_from_crawl = gr.Button("Add from Last Crawl") |
| status_corpus = gr.Markdown() |
| corpus_list_md = gr.Markdown(label="Corpus Items") |
|
|
| def do_add_from_search(corpus, items, inc, exc, dd): |
| corpus, msg = corpus_add(corpus or [], items, inc, exc, dd) |
| return corpus, msg, corpus_list(corpus) |
| def do_add_from_scrape(corpus, obj, inc, exc, dd): |
| corpus, msg = corpus_add(corpus or [], obj, inc, exc, dd) |
| return corpus, msg, corpus_list(corpus) |
| def do_add_from_crawl(corpus, pages, inc, exc, dd): |
| corpus, msg = corpus_add(corpus or [], pages, inc, exc, dd) |
| return corpus, msg, corpus_list(corpus) |
|
|
| add_from_search.click(do_add_from_search, [corpus_state, last_search_obj, include_filter, exclude_filter, dedupe], [corpus_state, status_corpus, corpus_list_md]) |
| add_from_scrape.click(do_add_from_scrape, [corpus_state, last_scrape_obj, include_filter, exclude_filter, dedupe], [corpus_state, status_corpus, corpus_list_md]) |
| add_from_crawl.click(do_add_from_crawl, [corpus_state, last_crawl_pages, include_filter, exclude_filter, dedupe], [corpus_state, status_corpus, corpus_list_md]) |
|
|
| with gr.Row(): |
| merge_btn = gr.Button("Merge ➜ Markdown", variant="primary") |
| clear_btn = gr.Button("Clear Corpus", variant="secondary") |
| merged_md = gr.Textbox(label="Merged Markdown (editable)", lines=12) |
|
|
| def do_merge(corpus): |
| md = corpus_merge_md(corpus or []) |
| return md, md |
| def do_clear(): |
| c,msg = corpus_clear() |
| return c, msg, corpus_list(c), "" |
| merge_btn.click(do_merge, [corpus_state], [merged_md, merged_md_state]) |
| clear_btn.click(do_clear, [], [corpus_state, status_corpus, corpus_list_md, merged_md]) |
|
|
| gr.Markdown("---") |
| with gr.Row(): |
| site_name = gr.Textbox(label="Scaffold Name", value="zen-scan") |
| scaffold_btn = gr.Button("Generate Minimal Site Scaffold (ZIP)") |
| scaffold_zip = gr.File(visible=False) |
| scaffold_btn.click(lambda md, name: scaffold_from_corpus(md, name or "zen-scan"), |
| [merged_md], [scaffold_zip]) |
|
|
| gr.Markdown("---") |
| with gr.Row(): |
| export_zip_btn = gr.Button("Export Corpus (ZIP)") |
| export_zip_file = gr.File(visible=False) |
|
|
| def do_export(corpus, merged): |
| extras = {"README.txt": "Exported by ZEN VibeCoder"} |
| return corpus_export(corpus or [], merged or "", extras) |
| export_zip_btn.click(do_export, [corpus_state, merged_md], [export_zip_file]) |
|
|
| |
| with gr.Tab("✨ Vibe Code (Synthesis)"): |
| with gr.Row(): |
| provider = gr.Radio(choices=["openai","anthropic"], value="openai", label="Provider") |
| model_name = gr.Textbox(label="Model (override)", placeholder="(blank = auto fallback)") |
| temp = gr.Slider(0.0, 1.2, value=0.4, step=0.05, label="Temperature") |
| sys_prompt = gr.Textbox(label="System Style (optional)", |
| value="Return structured outputs with file trees, code blocks and ordered steps. Be concise and concrete.") |
| user_prompt = gr.Textbox(label="User Prompt", lines=6) |
| ctx_md = gr.Textbox(label="Context (paste markdown or click Merge first)", lines=10) |
| gen_btn = gr.Button("Generate", variant="primary") |
| out_md = gr.Markdown() |
| gr.Markdown("**Starter Tiles**") |
| with gr.Row(): |
| t1 = gr.Button("🔧 Clone Docs ➜ Clean README") |
| t2 = gr.Button("🧭 Competitor Matrix") |
| t3 = gr.Button("🧪 Python API Client") |
| t4 = gr.Button("📐 ZEN Landing Rewrite") |
| t5 = gr.Button("📊 Dataset & ETL Plan") |
| def fill_tile(tile: str): |
| tiles = { |
| "t1": "Create a clean knowledge pack from the context, then output a README.md with: Overview, Key features, Quickstart, API endpoints, Notes & gotchas, License. Include a /docs/ outline.", |
| "t2": "Produce a feature matrix, pricing table, ICP notes, moats/risks, and a market POV. End with a ZEN playbook: 5 lever moves.", |
| "t3": "Design a Python client that wraps the target API with retry/backoff and typed responses. Provide package layout, requirements, client.py, examples/, and README.", |
| "t4": "Rewrite the landing content in ZEN brand voice: headline, 3 value props, social proof, CTA, concise FAQ. Provide HTML sections and copy.", |
| "t5": "Propose a dataset schema. Output a table of fields, types, constraints, plus an ETL plan (sources, transforms, validation, freshness, monitoring).", |
| } |
| return tiles[tile] |
| t1.click(lambda: fill_tile("t1"), outputs=[user_prompt]) |
| t2.click(lambda: fill_tile("t2"), outputs=[user_prompt]) |
| t3.click(lambda: fill_tile("t3"), outputs=[user_prompt]) |
| t4.click(lambda: fill_tile("t4"), outputs=[user_prompt]) |
| t5.click(lambda: fill_tile("t5"), outputs=[user_prompt]) |
| gen_btn.click(action_generate, [session_state, provider, model_name, sys_prompt, user_prompt, ctx_md, temp], [out_md]) |
|
|
| |
| with gr.Tab("🧪 Dual Synth (OpenAI vs Anthropic)"): |
| with gr.Row(): |
| model_openai = gr.Textbox(label="OpenAI Model", placeholder="(blank = auto fallback)") |
| model_anthropic = gr.Textbox(label="Anthropic Model", placeholder="(blank = auto fallback)") |
| temp2 = gr.Slider(0.0, 1.2, value=0.4, step=0.05, label="Temperature") |
| sys2 = gr.Textbox(label="System Style (optional)", value="Return structured outputs with file trees and clear steps.") |
| user2 = gr.Textbox(label="User Prompt", lines=6, value="Summarize the corpus and propose a 5-step execution plan.") |
| ctx2 = gr.Textbox(label="Context (tip: click Merge in Corpus tab)", lines=10) |
| dual_btn = gr.Button("Run Dual Synthesis", variant="primary") |
| dual_md = gr.Markdown() |
| dual_btn.click(dual_generate, [session_state, model_openai, model_anthropic, sys2, user2, ctx2, temp2], [dual_md]) |
|
|
| gr.Markdown("Built for **ZEN Arena** pipelines. Export ZIPs → ingest → credentialize via ZEN Cards.") |
|
|
| if __name__ == "__main__": |
| demo.launch(ssr_mode=False) |