Skip to content

feat: add cog playground browser UI#3086

Open
anish-sahoo wants to merge 10 commits into
mainfrom
interactive-ui
Open

feat: add cog playground browser UI#3086
anish-sahoo wants to merge 10 commits into
mainfrom
interactive-ui

Conversation

@anish-sahoo

@anish-sahoo anish-sahoo commented Jun 30, 2026

Copy link
Copy Markdown
Member

Closes #649

Adds a cog playground command: a local web server that serves a schema-driven, Postman-like browser UI and reverse-proxies to a running model API (e.g. cog serve). It reflects the model's OpenAPI schema and supports sync, streaming (SSE), and async (webhook) predictions, with form or raw-JSON input.

The browser only talks to the playground's own origin, which proxies to the target chosen at runtime (via X-Cog-Target) — sidestepping CORS and keeping SSE intact — and hosts a webhook sink that relays async events back over SSE.

flowchart LR
    Browser["Browser UI"]

    subgraph Playground["cog playground (host)"]
        Static["Static UI assets<br/>(go:embed)"]
        Proxy["Reverse proxy<br/>/proxy/*"]
        Sink["Webhook sink + relay<br/>/webhook/{token} → /events"]
    end

    Model["Target model API<br/>(e.g. cog serve)"]

    Browser -->|"load UI"| Static
    Browser -->|"schema, predictions, SSE"| Proxy
    Proxy -->|"X-Cog-Target"| Model
    Model -->|"webhook (async)"| Sink
    Sink -->|"SSE events"| Browser
Loading

Usage: run cog serve in one terminal and cog playground in another.

The UI is plain HTML/JS embedded via go:embed (no build step; Ace vendored for JSON editing). Documented in docs/cli.md and architecture/06-cli.md. Also bumps the streaming-text example to Python 3.13.

image image image

- Render boolean inputs as a true/false select so optional booleans no
  longer show two checkboxes (the include toggle plus the value checkbox)
- Start optional fields without a real default unchecked: treat a
  null/None default as "no default" so they are omitted rather than
  sending an explicit null
- Add a tooltip clarifying the optional-field include checkbox
- Move run controls (mode, id, Run/Stop/Reset) into a sticky top action
  bar so they stay reachable without scrolling
- Vendor Ace (buildless) and use it for all JSON: editable inputs
  (Form/JSON view and dict/object fields), read-only outputs and the Raw
  view, with code folding, light/dark theme binding, and a Copy button
- Hide the metrics table in the Raw view (already part of the payload)
- Use fixed-height editors with internal scroll so drag-selection
  autoscrolls, and keep normal full-page scrolling
- Surface error responses in the Raw view
@anish-sahoo anish-sahoo requested a review from a team as a code owner June 30, 2026 17:42
@ask-bonk

ask-bonk Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

I'm Bonk, and I've done a quick review of your PR.

This PR adds a cog playground command with a browser-based UI for interacting with running Cog models. It includes a Go server with reverse proxy, webhook sink, and SSE relay, plus a vanilla JS UI.

I found two issues:

1. SSE newline injection in pkg/cli/playground.go

In handleEvents (line ~201), webhook payloads are written directly into SSE data lines without escaping newlines:

_, _ = fmt.Fprintf(w, "data: %s\n\n", msg)

The SSE spec requires each line of a data: field to be prefixed with data: . A payload containing \n\n would prematurely terminate the event, and lines starting with event: or data: could inject spoofed events.

Fix: split the payload by newlines and emit a data: prefix for each line:

for _, line := range strings.Split(string(msg), "\n") {
    _, _ = fmt.Fprintf(w, "data: %s\n", line)
}
_, _ = fmt.Fprint(w, "\n")

2. Missing playground.go in architecture docs

architecture/06-cli.md lists the CLI source files but omits playground.go from the tree.

@ask-bonk

ask-bonk Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

I'm Bonk, and I've done a quick review of your PR.

This PR adds a cog playground command with a browser-based UI for interacting with running Cog models. It includes a Go server with reverse proxy, webhook sink, and SSE relay, plus a vanilla JS UI.

I found two issues:

1. SSE newline injection in pkg/cli/playground.go

In handleEvents (line ~201), webhook payloads are written directly into SSE data lines without escaping newlines:

_, _ = fmt.Fprintf(w, "data: %s\n\n", msg)

The SSE spec requires each line of a data: field to be prefixed with data: . A payload containing \n\n would prematurely terminate the event, and lines starting with event: or data: could inject spoofed events.

Fix: split the payload by newlines and emit a data: prefix for each line:

for _, line := range strings.Split(string(msg), "\n") {
    _, _ = fmt.Fprintf(w, "data: %s\n", line)
}
_, _ = fmt.Fprint(w, "\n")

2. Missing playground.go in architecture docs

architecture/06-cli.md lists the CLI source files but omits playground.go from the tree.

github run

- handleEvents prefixes every line of a relayed webhook payload with
  'data: ' so embedded newlines can't end the SSE event early or inject
  spoofed event:/data: fields; add a regression test
- list playground.go and the playground/ asset dir in architecture/06-cli.md

@ask-bonk ask-bonk Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm Bonk, and I've done a quick review of your PR.

This PR adds a cog playground command with a browser-based UI, Go reverse proxy, webhook sink, and SSE relay for interacting with running Cog models.

I found three issues:

  1. SSE newline injection in pkg/cli/playground.go (HIGH)
    Webhook payloads are written directly into SSE data lines without escaping newlines. A payload containing \n\n would prematurely terminate the event, and lines starting with event: or data: could inject spoofed events.

  2. Missing test for SSE newline escaping (MEDIUM)
    TestPlaygroundWebhookRelay tests the happy path but doesn't verify that webhook payloads containing newlines are properly escaped in the SSE stream.

  3. Missing playground.go in architecture docs file tree (LOW)
    architecture/06-cli.md lists CLI source files but omits playground.go and playground_test.go.

Comment thread pkg/cli/playground.go
Comment thread architecture/06-cli.md
Comment thread pkg/cli/playground_test.go
@ask-bonk

ask-bonk Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Posted a batch review on PR #3086 with three comments:

  1. SSE newline injection (pkg/cli/playground.go:201) — webhook payloads are written into SSE data lines without escaping newlines, which can break framing or inject spoofed events. Provided a fix suggestion.
  2. Missing test for SSE escaping (pkg/cli/playground_test.go:217) — suggested adding a test that verifies payloads containing newlines are properly escaped.
  3. Missing files in architecture docs (architecture/06-cli.md:263) — the CLI file tree omits playground.go and playground_test.go. Provided a fix suggestion.

github run

@ask-bonk

ask-bonk Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

LGTM

github run

@ask-bonk

ask-bonk Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

LGTM

github run

Vendor the Kumo UI semantic color tokens (vendor/kumo.css) and move the
playground's inline CSS into styles.css built on those tokens. Light/dark
now flips via the data-mode attribute Kumo keys off of (was data-theme).

Also retheme the Ace JSON editors to match: register token-driven
kumo-light/kumo-dark themes and drop the vendored chrome/tomorrow_night
theme files. Extend the UI-serving test to assert both stylesheets ship.
@ask-bonk

ask-bonk Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

LGTM

github run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Local web interface

1 participant