""" VC Deal Flow Signal — live demo Space. Three Gradio tabs that exercise the public, no-auth GitDealFlow API: 1. Live signals — top startups by 14-day commit-velocity acceleration. 2. Glossary search — the 84-term controlled vocabulary. 3. Cite this — copy-paste citation snippets for the SSRN paper + dataset. All data is fetched live from signals.gitdealflow.com/api/v1/* on every interaction so we never serve stale snapshots. The API is rate-limited, public, and CC BY 4.0. License: CC BY 4.0 — attribution required. Canonical site: https://gitdealflow.com Methodology paper: https://ssrn.com/abstract=6606558 """ from __future__ import annotations import gradio as gr import requests BASE = "https://signals.gitdealflow.com" UA = "vc-deal-flow-signal-hf-space/1.0 (+https://huggingface.co/spaces/the-data-nerd/vc-deal-flow-signal)" TIMEOUT = 15 def _get_json(path: str) -> dict | list: """GET an API endpoint and return parsed JSON, raising on non-2xx.""" url = f"{BASE}{path}" r = requests.get(url, headers={"User-Agent": UA}, timeout=TIMEOUT) r.raise_for_status() return r.json() # ── Tab 1: Live signals ──────────────────────────────────────────────── def load_signals(limit: int) -> list[list]: """Fetch top startups ranked by engineering acceleration.""" try: data = _get_json("/api/v1/signals.json") except requests.RequestException as e: return [["—", "—", "—", "—", f"API unavailable: {e}"]] # API shape: {"meta": {...}, "trending": [...], "sectors": [...]}. # Each trending item has: name, description, stage, geography, # commitVelocity14d, commitVelocityChange, contributors, contributorGrowth, # newRepos, signalType, githubUrl, websiteUrl. rows = data.get("trending") if isinstance(data, dict) else [] if not isinstance(rows, list): return [["—", "—", "—", "—", "Unexpected API shape"]] out = [] for r in rows[: max(1, min(int(limit), 50))]: name = r.get("name") or "—" signal = r.get("signalType") or "—" delta = r.get("commitVelocityChange") or "—" stage = r.get("stage") or "—" # Prefer GitHub deep link (Code-Side Sourcing source of truth). link = r.get("githubUrl") or r.get("websiteUrl") or BASE out.append([name, signal, str(delta), stage, link]) if not out: out = [["—", "—", "—", "—", "No trending signals returned"]] return out # ── Tab 2: Glossary search ───────────────────────────────────────────── def _load_glossary() -> list[dict]: """Fetch the controlled vocabulary from the public JSON-LD surface.""" try: data = _get_json("/api/v1/glossary.json") except requests.RequestException: return [] if not isinstance(data, dict): return [] terms = data.get("hasDefinedTerm") or [] return [t for t in terms if isinstance(t, dict)] GLOSSARY_CACHE: list[dict] = _load_glossary() def search_glossary(query: str) -> list[list]: """Substring filter across term name + definition.""" q = (query or "").strip().lower() if not GLOSSARY_CACHE: return [["—", "Glossary API unavailable — try again in a minute.", "—"]] if not q: rows = GLOSSARY_CACHE[:20] else: rows = [ t for t in GLOSSARY_CACHE if q in (t.get("name") or "").lower() or q in (t.get("description") or "").lower() ][:50] if not rows: return [[f"No match for '{query}'", "—", "—"]] out = [] for t in rows: name = t.get("name") or "—" defn = t.get("description") or "—" if len(defn) > 280: defn = defn[:277] + "…" term_id = t.get("termCode") or "" link = f"{BASE}/define/{term_id}" if term_id else BASE out.append([name, defn, link]) return out # ── Tab 3: Cite this ─────────────────────────────────────────────────── CITATIONS = { "BibTeX (paper)": """@article{thedatanerd2026vcdealflow, title = {A Longitudinal Panel of GitHub Engineering Velocity for Venture-Backed Startups}, author = {{The Data Nerd}}, journal = {SSRN Electronic Journal}, year = {2026}, doi = {10.2139/ssrn.6606558}, url = {https://ssrn.com/abstract=6606558}, note = {Published by VC Deal Flow Signal (GitDealFlow). CC BY 4.0.}, orcid = {0009-0002-2222-4112} }""", "RIS (paper)": """TY - JOUR AU - The Data Nerd TI - A Longitudinal Panel of GitHub Engineering Velocity for Venture-Backed Startups JO - SSRN Electronic Journal PY - 2026 DO - 10.2139/ssrn.6606558 UR - https://ssrn.com/abstract=6606558 ER -""", "APA (paper)": ( "The Data Nerd. (2026). A Longitudinal Panel of GitHub Engineering " "Velocity for Venture-Backed Startups. SSRN Electronic Journal. " "https://doi.org/10.2139/ssrn.6606558" ), "Wikipedia (paper)": ( "{{cite journal |last=The Data Nerd " "|title=A Longitudinal Panel of GitHub Engineering Velocity for Venture-Backed Startups " "|journal=SSRN Electronic Journal |year=2026 |doi=10.2139/ssrn.6606558 " "|url=https://ssrn.com/abstract=6606558 " "|publisher=VC Deal Flow Signal (GitDealFlow) " "|orcid=0009-0002-2222-4112}}" ), "Wikipedia (dataset)": ( "{{cite web |last=The Data Nerd " "|title=VC Deal Flow Signal — Public Engineering-Velocity Panel " "|publisher=VC Deal Flow Signal (GitDealFlow) |year=2026 " "|url=https://signals.gitdealflow.com/api/dataset.jsonl " "|format=NDJSON |access-date={{subst:CURRENTDATE}}}}" ), "HF dataset (BibTeX)": """@dataset{thedatanerd2026vcdealflowglossary, title = {VC Deal Flow Signal — Controlled Vocabulary Glossary}, author = {{The Data Nerd}}, year = {2026}, publisher = {Hugging Face}, url = {https://huggingface.co/datasets/the-data-nerd/vc-deal-flow-signal-glossary}, license = {CC BY 4.0} }""", } def get_citation(fmt: str) -> str: return CITATIONS.get(fmt, "") # ── App layout ───────────────────────────────────────────────────────── HEADER = """ # 📊 VC Deal Flow Signal **Engineering acceleration as a leading indicator of fundraise events.** Three-to-six-week lead time, all from public GitHub data, fully reproducible. [Methodology paper (SSRN)](https://ssrn.com/abstract=6606558) · [Glossary dataset](https://huggingface.co/datasets/the-data-nerd/vc-deal-flow-signal-glossary) · [Live API](https://signals.gitdealflow.com/api/v1/openapi.json) · [MCP server](https://signals.gitdealflow.com/.well-known/mcp.json) · [Wikidata Q139376302](https://www.wikidata.org/wiki/Q139376302) """ FOOTER = """ --- Built by [VC Deal Flow Signal](https://gitdealflow.com). Data is **CC BY 4.0** — free to use commercially, attribution required. """ with gr.Blocks( title="VC Deal Flow Signal — Live Engineering Acceleration", theme=gr.themes.Soft(primary_hue="blue", neutral_hue="slate"), ) as demo: gr.Markdown(HEADER) with gr.Tabs(): with gr.Tab("Live signals"): gr.Markdown( "Top startups ranked by 14-day commit-velocity acceleration. " "Live from `signals.gitdealflow.com/api/v1/signals.json`. " "Bot filter applied (Dependabot, Renovate, GitHub Actions excluded)." ) sig_limit = gr.Slider( minimum=5, maximum=50, value=20, step=5, label="How many signals to show", ) sig_btn = gr.Button("Fetch live signals", variant="primary") sig_table = gr.Dataframe( headers=[ "Company", "Signal type", "Δ velocity (14d)", "Stage", "GitHub / website", ], interactive=False, wrap=True, ) sig_btn.click(load_signals, inputs=[sig_limit], outputs=[sig_table]) demo.load(load_signals, inputs=[sig_limit], outputs=[sig_table]) with gr.Tab("Glossary search"): gr.Markdown( "Search the 84-term controlled vocabulary. Same corpus as the " "[`the-data-nerd/vc-deal-flow-signal-glossary`]" "(https://huggingface.co/datasets/the-data-nerd/vc-deal-flow-signal-glossary) " "Hugging Face dataset." ) glossary_query = gr.Textbox( label="Search term or definition (substring match)", placeholder="e.g. commit velocity, hiring burst, dream 100…", ) glossary_table = gr.Dataframe( headers=["Term", "Definition", "Deep link"], interactive=False, wrap=True, ) glossary_query.change( search_glossary, inputs=[glossary_query], outputs=[glossary_table], ) demo.load(search_glossary, inputs=[glossary_query], outputs=[glossary_table]) with gr.Tab("Cite this"): gr.Markdown( "Copy-paste-ready citation snippets for the SSRN paper, the " "public dataset, and the Hugging Face glossary corpus." ) fmt = gr.Radio( choices=list(CITATIONS.keys()), value="BibTeX (paper)", label="Format", ) cite_box = gr.Code( language=None, interactive=False, lines=10, ) fmt.change(get_citation, inputs=[fmt], outputs=[cite_box]) demo.load(get_citation, inputs=[fmt], outputs=[cite_box]) gr.Markdown(FOOTER) if __name__ == "__main__": demo.queue().launch()