diff --git a/workshops/build_workshop/README.md b/workshops/build_workshop/README.md index ff88d9f..7235a29 100644 --- a/workshops/build_workshop/README.md +++ b/workshops/build_workshop/README.md @@ -15,8 +15,8 @@ feature branch, open a PR to protected `dev-build-workshop-v1`, verify ## Architecture The app edge runs on the participant's laptop. ClickHouse, Postgres, ClickPipes, and -ClickStack/HyperDX live in ClickHouse Cloud; OpenAI, Langfuse, and optional LibreChat are -separate hosted services. No database or product UI is deployed locally. +ClickStack/HyperDX live in ClickHouse Cloud; OpenAI and Langfuse are separate hosted +services. No database or product UI is deployed locally. ```mermaid flowchart LR @@ -40,7 +40,6 @@ flowchart LR subgraph THIRD["Third-party"] OAI["OpenAI API
chat completions"] LF["Langfuse Cloud
chat traces"] - LC["Hosted LibreChat
optional SRE chat"] TLC["NYC TLC public dataset
download source only"] end @@ -58,7 +57,6 @@ flowchart LR TLC -.->|url seed, module 01| CH HDX -->|reads otel db| CH AG -->|RBAC-governed SQL| CH - LC -.->|remote MCP| MCP ``` The published diagrams (the ClickHouse Cloud platform stack, the workshop architecture, the @@ -100,7 +98,7 @@ sequenceDiagram G->>P: INSERT trips (throttled, TLS) Note over G,P: first run also creates the table
and publication pub_taxi P->>CP: WAL changes via publication + slot - CP->>CH: rows land in realtime_trips (_peerdb_* cols), about 60s + CP->>CH: rows land in default.realtime_trips, about 60s CH->>CH: materialized view fans rows into nyc_tlc_data.taxi_trips UI->>CH: parameterized SQL / guarded NL-to-SQL / RBAC-governed BI CH-->>UI: live Ops dashboard + 3M-row Historical seed diff --git a/workshops/build_workshop/app/OBSERVABILITY.md b/workshops/build_workshop/app/OBSERVABILITY.md index dfcf758..c7d03e0 100644 --- a/workshops/build_workshop/app/OBSERVABILITY.md +++ b/workshops/build_workshop/app/OBSERVABILITY.md @@ -107,12 +107,12 @@ cp .env.workshop.example .env.workshop # 2) Bring up the stack with the overlay: docker compose --env-file .env.workshop \ - -f docker-compose.workshop.yml -f docker-compose.otel.yml up -d + -f docker-compose.workshop.yml -f docker-compose.otel.yml up -d --build # Optional: also scrape raw container stdout (see VERIFY-LIVE): docker compose --env-file .env.workshop \ -f docker-compose.workshop.yml -f docker-compose.otel.yml \ - --profile container-logs up -d + --profile container-logs up -d --build ``` ## Environment variables diff --git a/workshops/build_workshop/app/README.md b/workshops/build_workshop/app/README.md index 958d53a..d224276 100644 --- a/workshops/build_workshop/app/README.md +++ b/workshops/build_workshop/app/README.md @@ -48,7 +48,7 @@ docker compose --env-file .env.workshop -f docker-compose.workshop.yml up -d # with the ClickStack observability overlay (module 05 onward): docker compose --env-file .env.workshop \ - -f docker-compose.workshop.yml -f docker-compose.otel.yml up -d + -f docker-compose.workshop.yml -f docker-compose.otel.yml up -d --build ``` Frontend: http://localhost:8080 - Backend API docs (FastAPI Swagger): diff --git a/workshops/build_workshop/app/WORKSHOP_CHANGES.md b/workshops/build_workshop/app/WORKSHOP_CHANGES.md index 6b4e9a9..3029c24 100644 --- a/workshops/build_workshop/app/WORKSHOP_CHANGES.md +++ b/workshops/build_workshop/app/WORKSHOP_CHANGES.md @@ -19,10 +19,9 @@ The workshop intentionally separates application code from managed services. - remote ClickHouse and ClickStack MCP endpoints - ClickHouse Agents - Langfuse Cloud and OpenAI -- the optional instructor-provided HTTPS LibreChat instance -No PostgreSQL, ClickHouse, MongoDB, LibreChat, HyperDX, Langfuse, or MCP server is -started on a learner machine. +No PostgreSQL, ClickHouse, MongoDB, HyperDX, Langfuse, or MCP server is started on a +learner machine. ## Runtime sequence @@ -30,8 +29,8 @@ started on a learner machine. 2. Module 01 creates and seeds the Cloud schema. 3. Module 03 creates managed Postgres, validates it with `./preflight.sh --require-postgres`, then explicitly enables the `cdc` trip writer. -4. Module 05 enables Managed ClickStack and starts the stateless collector overlay. -5. Module 06b uses hosted LibreChat; Module 08 sends chat traces to Langfuse Cloud. +4. Module 05 starts the stateless collector overlay, then enables Managed ClickStack. +5. Module 08 sends chat traces to Langfuse Cloud. `.env.workshop.example` leaves managed Postgres credentials blank until Module 03 and requires TLS. `preflight.sh` rejects loopback/local database hosts. CI policy checks in diff --git a/workshops/build_workshop/app/docker-compose.otel.yml b/workshops/build_workshop/app/docker-compose.otel.yml index 6b524f0..922ed7f 100644 --- a/workshops/build_workshop/app/docker-compose.otel.yml +++ b/workshops/build_workshop/app/docker-compose.otel.yml @@ -14,7 +14,7 @@ # "ClickStack observability" section of .env.workshop.example): # # docker compose --env-file .env.workshop \ -# -f docker-compose.workshop.yml -f docker-compose.otel.yml up -d +# -f docker-compose.workshop.yml -f docker-compose.otel.yml up -d --build # # Add raw container-log scraping (optional, see VERIFY-LIVE notes in OBSERVABILITY.md): # ... -f docker-compose.otel.yml --profile container-logs up -d diff --git a/workshops/build_workshop/docs/diagrams/gen_diagrams.py b/workshops/build_workshop/docs/diagrams/gen_diagrams.py index 4947446..d4317e7 100644 --- a/workshops/build_workshop/docs/diagrams/gen_diagrams.py +++ b/workshops/build_workshop/docs/diagrams/gen_diagrams.py @@ -20,9 +20,9 @@ # zone tints (muted, harmonious) ZONES = { - "laptop": {"fill": "#1B2531", "stroke": "#3E5A78", "label": "PARTICIPANT LAPTOP · the NYC-taxi app runs here"}, - "cloud": {"fill": "#211F14", "stroke": YELLOW, "label": "CLICKHOUSE CLOUD · your trial org (you create all of this)"}, - "third": {"fill": "#1E1E22", "stroke": "#54545C", "label": "THIRD-PARTY"}, + "laptop": {"fill": "#1B2531", "stroke": "#3E5A78", "label": "PARTICIPANT LAPTOP · local app + tools"}, + "cloud": {"fill": "#211F14", "stroke": YELLOW, "label": "CLICKHOUSE CLOUD · your trial org"}, + "third": {"fill": "#1E1E22", "stroke": "#54545C", "label": "EXTERNAL SERVICES"}, } @@ -46,21 +46,22 @@ def box(x, y, w, h, title, lines=None, accent=False, rounded=12, fill=BOX_FILL, cx = x + w / 2 # title ty = y + (22 if lines else h/2 + 5) - s += (f'{escape(title)}\n') if lines: for i, ln in enumerate(lines): - s += (f'{escape(ln)}\n') return s def zone(x, y, w, h, key): z = ZONES[key] + label_color = YELLOW if key == "cloud" else SUBINK s = (f'\n') - s += (f'{escape(z["label"])}\n') + s += (f'{escape(z["label"])}\n') return s @@ -72,9 +73,9 @@ def edge(x1, y1, x2, y2, label=None, mid=None, dashed=False, label_dx=0, label_d lx = (x1 + x2) / 2 + label_dx if mid is None else mid[0] ly = (y1 + y2) / 2 + label_dy if mid is None else mid[1] w = 7.2 * len(label) + 10 - s += (f'\n') - s += (f'\n') + s += (f'{escape(label)}\n') return s @@ -87,9 +88,9 @@ def elbow(pts, label=None, mid=None, dashed=False): f'marker-end="url(#arrow)"/>\n') if label and mid: w = 7.2 * len(label) + 10 - s += (f'\n') - s += (f'\n') + s += (f'{escape(label)}\n') return s @@ -106,7 +107,6 @@ def module_flow(): ("04 ClickHouse Agents", "10 min", "conversational BI", "over your taxi data"), ("05 ClickStack", "15 min", "OTel overlay: traces", "+ logs in HyperDX"), ("06 AI SRE", "15 min", "agent + ClickStack MCP", "builds dashboard + alert"), - ("06b Hosted LibreChat", "optional · 15 min", "remote ClickStack + GitHub MCP", "chat-native SRE workflow"), ("07 Test, fail, and fix", "20 min", "inject a fault, diagnose", "with the AI SRE, fix it"), ("08 Chat + Langfuse", "15 min", "in-app AI chat,", "every turn traced"), ("09 Wrap-up", "10 min", "running prototype,", "take it home"), @@ -122,7 +122,7 @@ def module_flow(): s += (f'' f'ClickHouse BUILD Workshop · Module flow\n') s += (f'' - f'~2h30 core + optional 06b · only module 07 switches to a fault branch\n') + f'~2h30 hands-on · only module 07 switches to a fault branch\n') top = gy + 52 pos = {} @@ -167,66 +167,38 @@ def module_flow(): # =========================================================================== -# ARCHITECTURE (3 zones, aligned rows, top bus for third-party API calls) +# ARCHITECTURE (three simple groups; the data-flow diagram owns sequencing) # =========================================================================== def architecture(): - W, H = 1700, 1000 + W, H = 1200, 650 s = _svg_header(W, H) s += (f'' - f'ClickHouse BUILD Workshop · Architecture (target end state)\n') + f'ClickHouse BUILD Workshop · Where each component runs\n') s += (f'' - f'App-side components run locally; stateful data services run in ClickHouse Cloud. ' - f'Dashed lines are control/OAuth; solid lines are data.\n') + f'Grouped by location; no data direction is implied. Follow the next diagram for the trip path.\n') # zones - s += zone(30, 135, 515, 700, "laptop") - s += zone(590, 135, 650, 760, "cloud") - s += zone(1290, 135, 380, 560, "third") - - # --- laptop boxes --- - s += box(60, 190, 455, 62, "frontend", ["React SPA · nginx :8080", "Ops + Historical dashboards, chat"]) - s += box(60, 300, 455, 62, "backend", ["FastAPI :8000", "analytics API + /api/chat (NL-to-SQL)"]) - s += box(60, 410, 455, 56, "otel-collector", ["ClickStack overlay (module 05)"]) - s += box(60, 520, 455, 62, "pg-trip-writer", ["synthetic trips; creates the CDC", "table + publication on first run"]) - s += box(60, 660, 455, 62, "coding agent + clickhousectl", ["Claude Code / Cursor / Codex", "+ ClickHouse skills + docs llms.txt"]) - - # --- cloud boxes --- - s += box(620, 185, 590, 96, "ClickHouse service :8443 TLS", ["nyc_tlc_data (taxi_trips, taxi_zones,", "views, CDC MV) + otel db (logs, traces)"], accent=True) - s += box(620, 330, 280, 88, "ClickPipes", ["Postgres CDC pipe", "snapshot + stream (~60s)"]) - s += box(930, 330, 280, 88, "Managed ClickStack / HyperDX", ["traces + logs UI", "module 05"]) - s += box(620, 500, 590, 82, "Postgres managed by ClickHouse :5432 TLS", ["you create it with clickhousectl (module 03) · public.realtime_trips + pub_taxi"]) - s += box(620, 628, 280, 74, "Remote MCP", ["/mcp + /clickstack (OAuth)"]) - s += box(930, 628, 280, 74, "ClickHouse Agents", ["ai.clickhouse.cloud (module 04)"]) - s += box(620, 748, 590, 60, "Instructor cloud fallback only", ["managed Postgres pool when a trial org cannot create one"], dashed=True, fill="#1C1B14") - - # --- third-party boxes --- - s += box(1315, 195, 330, 74, "Langfuse Cloud", ["chat traces, sessions, cost"]) - s += box(1315, 300, 330, 74, "OpenAI API", ["chat completions (gpt-5.4-mini)"]) - s += box(1315, 410, 330, 88, "NYC TLC dataset", ["public parquet; read once by the", "module 01 seed. Data source only."]) - s += box(1315, 530, 330, 74, "Hosted LibreChat", ["optional SRE chat · remote MCP"]) - - # --- edges (laptop internal) --- - s += edge(287, 252, 287, 300, "/api proxy", label_dx=42) - s += edge(180, 362, 180, 410, "OTLP", label_dx=-34) - - # laptop -> cloud (near-horizontal, labels in the gap) - s += elbow([(515, 331), (560, 331), (560, 250), (620, 250)], "SQL · TLS :8443", mid=(567, 300)) - s += elbow([(515, 438), (585, 438), (585, 262), (620, 262)], "traces → otel db", mid=(585, 400), dashed=False) - s += edge(515, 545, 620, 541, "INSERT trips · TLS", label_dy=-8) - s += edge(515, 690, 620, 665, "MCP · OAuth", label_dy=-8) - - # cloud internal (CDC chain + reads) - s += edge(760, 500, 760, 418, "logical replication", label_dx=0, label_dy=-4) - s += edge(760, 330, 760, 281, "CDC rows ~60s", label_dy=-4) - s += edge(1070, 330, 1070, 281, "reads otel db", label_dy=-4) - # agents -> service (RBAC), up the cloud's right margin - s += elbow([(1070, 628), (1215, 628), (1215, 235), (1210, 235)], "RBAC SQL", mid=(1215, 470)) - - # third-party: backend -> OpenAI / Langfuse along the top bus (above zones) - s += elbow([(360, 300), (360, 118), (1480, 118), (1480, 300)], "chat completions", mid=(950, 112)) - s += elbow([(330, 300), (330, 96), (1430, 96), (1430, 195)], "chat traces (Langfuse SDK)", mid=(760, 90)) - # TLC seed -> service (in the cloud/third gap) - s += elbow([(1315, 452), (1262, 452), (1262, 215), (1210, 215)], "url() seed", mid=(1262, 305)) + s += zone(30, 110, 330, 480, "laptop") + s += zone(400, 110, 500, 480, "cloud") + s += zone(940, 110, 230, 480, "third") + + # Participant laptop: application edge and stateless tools only. + s += box(50, 165, 290, 78, "Frontend + FastAPI", ["dashboards, chat, and API", "runs with Docker Compose"]) + s += box(50, 270, 290, 68, "Load generator", ["creates synthetic trip rows"]) + s += box(50, 365, 290, 68, "OTel forwarder", ["sends telemetry to ClickStack"]) + s += box(50, 460, 290, 78, "Coding agent + CLI", ["workshop commands, MCP,", "and ClickHouse skills"]) + + # ClickHouse Cloud: state, ingestion, observability, and AI surfaces. + s += box(425, 165, 450, 88, "ClickHouse service", ["default.realtime_trips lands here", "nyc_tlc_data.taxi_trips powers the app"], accent=True) + s += box(425, 285, 215, 78, "Managed Postgres", ["public.realtime_trips"]) + s += box(660, 285, 215, 78, "ClickPipes", ["streams Postgres changes"]) + s += box(425, 395, 215, 88, "Managed ClickStack", ["HyperDX traces, logs,", "dashboards, and alerts"]) + s += box(660, 395, 215, 88, "Agents + MCP", ["data questions and", "coding-agent tools"]) + + # External services used by the app, kept separate from workshop data storage. + s += box(960, 175, 190, 82, "OpenAI API", ["chat completions"]) + s += box(960, 290, 190, 82, "Langfuse Cloud", ["chat traces and cost"]) + s += box(960, 405, 190, 92, "NYC TLC dataset", ["one-time historical", "seed source"]) s += "\n" return s @@ -320,99 +292,56 @@ def up_arrow(cx, y_from, y_to): # =========================================================================== -# DATA FLOW (four swimlanes: seed, live CDC, read, observe) +# DATA FLOW (one learner-facing left-to-right story) # =========================================================================== def data_flow(): - W, H = 1560, 900 + W, H = 1200, 620 s = _svg_header(W, H) s += (f'' - f'ClickHouse BUILD Workshop · Data flow\n') + f'ClickHouse BUILD Workshop · A trip\'s path\n') s += (f'' - f'How a row moves: seeded once, streamed continuously, then read and observed — ' - f'all through your ClickHouse service.\n') - - LX, LW = 30, W - 60 - TITLE_W = 176 # left title gutter per lane - - def lane(y, h, title, subtitle): - t = (f'\n') - t += (f'\n') - t += (f'{escape(title)}\n') - # wrap subtitle into the gutter - words, line, ln = subtitle.split(), "", 0 - for wd in words: - if len(line) + len(wd) > 20: - t += (f'{escape(line)}\n'); line = wd; ln += 1 - else: - line = (line + " " + wd).strip() - if line: - t += (f'{escape(line)}\n') - return t + f'Follow steps 1–6: across the top, then back across the bottom.\n') + + xcols, yrows, bw, bh = [40, 450, 860], [135, 345], 300, 110 + steps = [ + ("Load generator", ["creates synthetic trips", "on your laptop"], False), + ("Managed Postgres", ["source table:", "public.realtime_trips"], False), + ("ClickPipes", ["streams each change", "with Postgres CDC"], False), + ("ClickHouse", ["first landing table:", "default.realtime_trips"], True), + ("Materialized view", ["moves new rows into", "nyc_tlc_data.taxi_trips"], False), + ("Frontend + FastAPI", ["reads taxi_trips for", "the live dashboards"], False), + ] - def fbox(x, y, w, label, sub, accent=False): - return box(x, y, w, 62, label, [sub] if sub else None, accent=accent, rounded=10) - - def harrow(x1, x2, y, label): - return edge(x1, y, x2, y, label, label_dy=-9) - - x0 = LX + TITLE_W + 10 # where the flow chain starts - bw = 216 # box width - gap = 140 # arrow gap (wide enough for the longest label chip) - - def col(i): - return x0 + i*(bw+gap) - - # Lane 1 — SEED (module 01) - y = 96; s += lane(y, 96, "Seed", "one-time, module 01") - by = y + 17 - s += fbox(col(0), by, bw, "NYC TLC parquet", "~3.2M rows, public") - s += fbox(col(2), by, bw, "ClickHouse service", "nyc_tlc_data.taxi_trips", accent=True) - s += harrow(col(0)+bw, col(2)-6, by+31, "url() seed — one statement, module 01") - - # Lane 2 — LIVE CDC (module 03) - y = 212; s += lane(y, 110, "Live CDC", "continuous, module 03") - by = y + 24 - s += fbox(col(0), by, bw, "pg-trip-writer", "synthetic trips") - s += fbox(col(1), by, bw, "Managed Postgres", "public.realtime_trips") - s += fbox(col(2), by, bw, "ClickPipes", "Postgres CDC pipe") - s += fbox(col(3), by, bw, "ClickHouse service", "MV → taxi_trips", accent=True) - s += harrow(col(0)+bw, col(1)-6, by+31, "INSERT · TLS") - s += harrow(col(1)+bw, col(2)-6, by+31, "replication") - s += harrow(col(2)+bw, col(3)-6, by+31, "CDC rows ~60s") - - # Lane 3 — READ (modules 02, 04, 08) - y = 342; s += lane(y, 176, "Read", "modules 02, 04, 08") - by = y + 22 - s += fbox(col(0), by, bw, "ClickHouse service", "nyc_tlc_data", accent=True) - s += fbox(col(1), by, bw, "FastAPI", "safe parameterized SQL") - s += fbox(col(2), by, bw, "Ops + Historical", "React dashboards") - s += harrow(col(0)+bw, col(1)-6, by+31, "SELECT") - s += harrow(col(1)+bw, col(2)-6, by+31, "GET /api/*") - # two more consumers off the service (stacked below) - by2 = y + 104 - s += fbox(col(1), by2, bw, "in-app AI chat", "guarded SELECT (module 08)") - s += fbox(col(2), by2, bw, "ClickHouse Agents", "RBAC SQL (module 04)") - s += elbow([(col(0)+bw/2, by+62), (col(0)+bw/2, by2+31), (col(1)-6, by2+31)], "NL → SQL", mid=(col(0)+bw/2+70, by2+22)) - s += harrow(col(1)+bw, col(2)-6, by2+31, "RBAC SQL") - - # Lane 4 — OBSERVE (modules 05, 06, 07) - y = 538; s += lane(y, 176, "Observe", "modules 05, 06, 07") - by = y + 22 - s += fbox(col(0), by, bw, "backend spans", "clickhouse.query + logs") - s += fbox(col(1), by, bw, "stateless OTel forwarder", "local OTLP :4318") - s += fbox(col(2), by, bw, "otel db", "in your service") - s += fbox(col(3), by, bw, "HyperDX UI", "search, traces, dashboards") - s += harrow(col(0)+bw, col(1)-6, by+31, "OTLP") - s += harrow(col(1)+bw, col(2)-6, by+31, "write") - s += harrow(col(2)+bw, col(3)-6, by+31, "read") - by2 = y + 104 - s += fbox(col(1), by2, bw, "coding agent", "via ClickStack MCP") - s += elbow([(col(1)+bw/2, by2), (col(1)+bw/2, by+62)], None) - s += (f'' - f'clickstack_search / save_dashboard / save_alert\n') + # Numbered snake: left-to-right on row one, then right-to-left on row two. + positions = [ + (xcols[0], yrows[0]), + (xcols[1], yrows[0]), + (xcols[2], yrows[0]), + (xcols[2], yrows[1]), + (xcols[1], yrows[1]), + (xcols[0], yrows[1]), + ] + for i, (title, lines, accent) in enumerate(steps): + x, y = positions[i] + s += box(x, y, bw, bh, title, lines, accent=accent, rounded=12) + s += (f'\n' + f'{i+1}\n') + + # Every connector stays in the gutter between boxes. Label chips are painted + # after each line, so a line can never cut through its text. + s += edge(340, 190, 443, 190, "INSERT", mid=(395, 167)) + s += edge(750, 190, 853, 190, "CDC", mid=(805, 167)) + s += elbow([(1010, 245), (1010, 338)], "lands here", mid=(1010, 302)) + s += edge(860, 400, 757, 400, "feeds", mid=(805, 377)) + s += edge(450, 400, 347, 400, "reads", mid=(395, 377)) + + s += (f'\n') + s += (f'' + f'What the other workshop tools do\n') + s += (f'' + f'ClickStack observes the app · ClickHouse Agents answers data questions · Langfuse traces the chat\n') s += "\n" return s diff --git a/workshops/build_workshop/docs/diagrams/workshop-architecture.svg b/workshops/build_workshop/docs/diagrams/workshop-architecture.svg index cd365ec..f261096 100644 --- a/workshops/build_workshop/docs/diagrams/workshop-architecture.svg +++ b/workshops/build_workshop/docs/diagrams/workshop-architecture.svg @@ -1,107 +1,54 @@ - - + + -ClickHouse BUILD Workshop · Architecture (target end state) -App-side components run locally; stateful data services run in ClickHouse Cloud. Dashed lines are control/OAuth; solid lines are data. - -PARTICIPANT LAPTOP · the NYC-taxi app runs here - -CLICKHOUSE CLOUD · your trial org (you create all of this) - -THIRD-PARTY - -frontend -React SPA · nginx :8080 -Ops + Historical dashboards, chat - -backend -FastAPI :8000 -analytics API + /api/chat (NL-to-SQL) - -otel-collector -ClickStack overlay (module 05) - -pg-trip-writer -synthetic trips; creates the CDC -table + publication on first run - -coding agent + clickhousectl -Claude Code / Cursor / Codex -+ ClickHouse skills + docs llms.txt - -ClickHouse service :8443 TLS -nyc_tlc_data (taxi_trips, taxi_zones, -views, CDC MV) + otel db (logs, traces) - -ClickPipes -Postgres CDC pipe -snapshot + stream (~60s) - -Managed ClickStack / HyperDX -traces + logs UI -module 05 - -Postgres managed by ClickHouse :5432 TLS -you create it with clickhousectl (module 03) · public.realtime_trips + pub_taxi - -Remote MCP -/mcp + /clickstack (OAuth) - -ClickHouse Agents -ai.clickhouse.cloud (module 04) - -Instructor cloud fallback only -managed Postgres pool when a trial org cannot create one - -Langfuse Cloud -chat traces, sessions, cost - -OpenAI API -chat completions (gpt-5.4-mini) - -NYC TLC dataset -public parquet; read once by the -module 01 seed. Data source only. - -Hosted LibreChat -optional SRE chat · remote MCP - - -/api proxy - - -OTLP - - -SQL · TLS :8443 - - -traces → otel db - - -INSERT trips · TLS - - -MCP · OAuth - - -logical replication - - -CDC rows ~60s - - -reads otel db - - -RBAC SQL - - -chat completions - - -chat traces (Langfuse SDK) - - -url() seed +ClickHouse BUILD Workshop · Where each component runs +Grouped by location; no data direction is implied. Follow the next diagram for the trip path. + +PARTICIPANT LAPTOP · local app + tools + +CLICKHOUSE CLOUD · your trial org + +EXTERNAL SERVICES + +Frontend + FastAPI +dashboards, chat, and API +runs with Docker Compose + +Load generator +creates synthetic trip rows + +OTel forwarder +sends telemetry to ClickStack + +Coding agent + CLI +workshop commands, MCP, +and ClickHouse skills + +ClickHouse service +default.realtime_trips lands here +nyc_tlc_data.taxi_trips powers the app + +Managed Postgres +public.realtime_trips + +ClickPipes +streams Postgres changes + +Managed ClickStack +HyperDX traces, logs, +dashboards, and alerts + +Agents + MCP +data questions and +coding-agent tools + +OpenAI API +chat completions + +Langfuse Cloud +chat traces and cost + +NYC TLC dataset +one-time historical +seed source diff --git a/workshops/build_workshop/docs/diagrams/workshop-data-flow.svg b/workshops/build_workshop/docs/diagrams/workshop-data-flow.svg index e60ec1a..dd9f36b 100644 --- a/workshops/build_workshop/docs/diagrams/workshop-data-flow.svg +++ b/workshops/build_workshop/docs/diagrams/workshop-data-flow.svg @@ -1,105 +1,60 @@ - - + + -ClickHouse BUILD Workshop · Data flow -How a row moves: seeded once, streamed continuously, then read and observed — all through your ClickHouse service. - - -Seed -one-time, module 01 - -NYC TLC parquet -~3.2M rows, public - -ClickHouse service -nyc_tlc_data.taxi_trips - - -url() seed — one statement, module 01 - - -Live CDC -continuous, module 03 - -pg-trip-writer -synthetic trips - -Managed Postgres -public.realtime_trips - -ClickPipes -Postgres CDC pipe - -ClickHouse service -MV → taxi_trips - - -INSERT · TLS - - -replication - - -CDC rows ~60s - - -Read -modules 02, 04, 08 - -ClickHouse service -nyc_tlc_data - -FastAPI -safe parameterized SQL - -Ops + Historical -React dashboards - - -SELECT - - -GET /api/* - -in-app AI chat -guarded SELECT (module 08) - -ClickHouse Agents -RBAC SQL (module 04) - - -NL → SQL - - -RBAC SQL - - -Observe -modules 05, 06, 07 - -backend spans -clickhouse.query + logs - -stateless OTel forwarder -local OTLP :4318 - -otel db -in your service - -HyperDX UI -search, traces, dashboards - - -OTLP - - -write - - -read - -coding agent -via ClickStack MCP - -clickstack_search / save_dashboard / save_alert +ClickHouse BUILD Workshop · A trip's path +Follow steps 1–6: across the top, then back across the bottom. + +Load generator +creates synthetic trips +on your laptop + +1 + +Managed Postgres +source table: +public.realtime_trips + +2 + +ClickPipes +streams each change +with Postgres CDC + +3 + +ClickHouse +first landing table: +default.realtime_trips + +4 + +Materialized view +moves new rows into +nyc_tlc_data.taxi_trips + +5 + +Frontend + FastAPI +reads taxi_trips for +the live dashboards + +6 + + +INSERT + + +CDC + + +lands here + + +feeds + + +reads + +What the other workshop tools do +ClickStack observes the app · ClickHouse Agents answers data questions · Langfuse traces the chat diff --git a/workshops/build_workshop/docs/diagrams/workshop-module-flow.svg b/workshops/build_workshop/docs/diagrams/workshop-module-flow.svg index 322eaf7..7318a01 100644 --- a/workshops/build_workshop/docs/diagrams/workshop-module-flow.svg +++ b/workshops/build_workshop/docs/diagrams/workshop-module-flow.svg @@ -1,8 +1,8 @@ - - + + ClickHouse BUILD Workshop · Module flow -~2h30 core + optional 06b · only module 07 switches to a fault branch +~2h30 hands-on · only module 07 switches to a fault branch 00 Setup @@ -47,28 +47,22 @@ builds dashboard + alert -06b Hosted LibreChat -optional · 15 min -remote ClickStack + GitHub MCP -chat-native SRE workflow +07 Test, fail, and fix +20 min +inject a fault, diagnose +with the AI SRE, fix it -07 Test, fail, and fix -20 min -inject a fault, diagnose -with the AI SRE, fix it +08 Chat + Langfuse +15 min +in-app AI chat, +every turn traced -08 Chat + Langfuse -15 min -in-app AI chat, -every turn traced - - -09 Wrap-up -10 min -running prototype, -take it home +09 Wrap-up +10 min +running prototype, +take it home @@ -78,6 +72,5 @@ - After module 03: historical + live data both flowing into your Cloud service. diff --git a/workshops/build_workshop/playbook/content/docs/index.mdx b/workshops/build_workshop/playbook/content/docs/index.mdx index 083dd68..a7cbdbb 100644 --- a/workshops/build_workshop/playbook/content/docs/index.mdx +++ b/workshops/build_workshop/playbook/content/docs/index.mdx @@ -89,33 +89,33 @@ stateful data or product service is cloud-hosted from the start. ClickHouse Cloud is a single platform that spans the whole stack — from ingesting data at the bottom, to storing and analyzing it in ClickHouse, to observing it and layering AI on top. This workshop is a guided tour through exactly these pieces: you ingest with -ClickPipes, analyze in ClickHouse, and observe with Managed ClickStack/HyperDX. Langfuse, -OpenAI, and optional LibreChat are separate hosted services. +ClickPipes, analyze in ClickHouse, and observe with Managed ClickStack/HyperDX. Langfuse +and OpenAI are separate hosted services. ![The ClickHouse Cloud platform: layered stack from sources and ClickPipes ingestion, through ClickHouse and Postgres, up to HyperDX, Langfuse and agentic AI](/clickhouse-platform.svg) ### Architecture (target end state) -Here is how those pieces come together: the app edge on your laptop; ClickHouse-managed -state in your trial; and separate hosted OpenAI, Langfuse, and optional LibreChat services. +First, group the pieces by where they run: the stateless app and tools on your laptop, +stateful services in ClickHouse Cloud, and the separate hosted AI services. -![Workshop architecture: participant laptop, your ClickHouse Cloud trial (service, ClickPipes, managed Postgres), and third-party services](/workshop-architecture.svg) +![Workshop components grouped into participant laptop, ClickHouse Cloud, and external hosted services](/workshop-architecture.svg) ### How data flows -The same picture, followed as data: seeded once from object storage, streamed continuously -via CDC, then read by the dashboards, chat, and agents, and observed through ClickStack. +Now follow one live trip from the local load generator through managed Postgres and +ClickPipes, into `default.realtime_trips`, and finally into the application dashboards. -![Data flow: four lanes — one-time seed, live CDC, read paths, and observability — all through your ClickHouse service](/workshop-data-flow.svg) +![A live trip flows from the load generator to managed Postgres, through ClickPipes into default.realtime_trips, then through a materialized view to the app](/workshop-data-flow.svg) The diagrams above are generated from `workshops/build_workshop/docs/diagrams/gen_diagrams.py` (edit the script and re-run it to regenerate the SVGs) — see the module flow below. -![Module flow: ten core modules plus optional hosted LibreChat 06b](/workshop-module-flow.svg) +![Module flow: ten core workshop modules](/workshop-module-flow.svg) ## Modules -Ten core modules plus optional 06b, in order. The app is complete on `build-workshop-v1`, so every module except +Ten core modules, in order. The app is complete on `build-workshop-v1`, so every module except 07 needs no per-module checkout — you configure and connect services rather than change app code. The only branch switches are the fault branches in module 07. @@ -128,8 +128,7 @@ app code. The only branch switches are the fault branches in module 07. | 04 | 10 min | [ClickHouse Agents](/docs/learner/04-clickhouse-agents) | [notes](/docs/instructor/04-clickhouse-agents) | `build-workshop-v1` | Conversational BI: create an agent over your taxi data and explore it in natural language | | 05 | 15 min | [ClickStack](/docs/learner/05-clickstack) | [notes](/docs/instructor/05-clickstack) | `build-workshop-v1` | Enable ClickStack and send app traces and logs to HyperDX | | 06 | 15 min | [AI SRE](/docs/learner/06-ai-sre) | [notes](/docs/instructor/06-ai-sre) | `build-workshop-v1` | Use the ClickStack MCP connection to build an SRE dashboard and alert | -| 06b | Optional, 15 min | [Hosted LibreChat AI SRE](/docs/learner/06b-ai-sre-librechat) | [notes](/docs/instructor/06b-ai-sre-librechat) | `build-workshop-v1` | Use a hosted chat agent with remote ClickStack and GitHub MCP connections | -| 07 | 20 min | [Test, fail, and fix](/docs/learner/07-break-and-fix) | [notes](/docs/instructor/07-break-and-fix) | `fault/*` | Continue from AI SRE or optional 06b, inject a fault, diagnose it, fix it, and prove recovery | +| 07 | 20 min | [Test, fail, and fix](/docs/learner/07-break-and-fix) | [notes](/docs/instructor/07-break-and-fix) | `fault/*` | Continue from AI SRE, inject a fault, diagnose it, fix it, and prove recovery | | 08 | 15 min | [Chat and Langfuse](/docs/learner/08-chat-langfuse) | [notes](/docs/instructor/08-chat-langfuse) | `build-workshop-v1` | Use the in-app AI chat and follow its traces, generations, and costs in Langfuse | | 09 | 10 min | [Wrap-up](/docs/learner/09-wrap-up) | [notes](/docs/instructor/09-wrap-up) | `build-workshop-v1` | Review what you built, take it home, and extend it to your own data | diff --git a/workshops/build_workshop/playbook/content/docs/instructor/05-clickstack.mdx b/workshops/build_workshop/playbook/content/docs/instructor/05-clickstack.mdx index 862cdff..404d9de 100644 --- a/workshops/build_workshop/playbook/content/docs/instructor/05-clickstack.mdx +++ b/workshops/build_workshop/playbook/content/docs/instructor/05-clickstack.mdx @@ -15,6 +15,8 @@ collector as early as this module allows and keep it running. - Managed ClickStack stores telemetry in ClickHouse Cloud; HyperDX is the hosted UI. The local collector only forwards data. Show both an end-to-end request trace and a fresh `DEBUG ... ClickHouse query ok` record in the Log source. +- Have learners start the collector in Step 1 before they launch Managed ClickStack in + Step 2. This gives telemetry time to arrive and makes the first hosted-UI view useful. - Show one end-to-end request trace on the projector. ## Common failures @@ -42,6 +44,6 @@ collector as early as this module allows and keep it running. ## Reset steps - Restart with both compose files: - `docker compose --env-file .env.workshop -f docker-compose.workshop.yml -f docker-compose.otel.yml up -d`. + `docker compose --env-file .env.workshop -f docker-compose.workshop.yml -f docker-compose.otel.yml up -d --build`. - In the Cloud console, open the service, choose **ClickStack**, and relaunch the hosted UI if the SSO session expired. diff --git a/workshops/build_workshop/playbook/content/docs/instructor/06-ai-sre.mdx b/workshops/build_workshop/playbook/content/docs/instructor/06-ai-sre.mdx index ac47ac6..4accbdc 100644 --- a/workshops/build_workshop/playbook/content/docs/instructor/06-ai-sre.mdx +++ b/workshops/build_workshop/playbook/content/docs/instructor/06-ai-sre.mdx @@ -1,5 +1,5 @@ --- -title: 06a AI SRE +title: 06 AI SRE description: Instructor notes for module 06 — timing, talk track, common failures, and reset steps. --- diff --git a/workshops/build_workshop/playbook/content/docs/instructor/06b-ai-sre-librechat.mdx b/workshops/build_workshop/playbook/content/docs/instructor/06b-ai-sre-librechat.mdx index a88fc20..612e1cc 100644 --- a/workshops/build_workshop/playbook/content/docs/instructor/06b-ai-sre-librechat.mdx +++ b/workshops/build_workshop/playbook/content/docs/instructor/06b-ai-sre-librechat.mdx @@ -1,17 +1,24 @@ --- -title: 06b AI SRE with hosted LibreChat -description: Instructor prework and facilitation notes for the optional cloud-hosted LibreChat module. +title: "Archived: 06b AI SRE with hosted LibreChat" +description: Archived instructor reference for the former optional hosted LibreChat module. --- + + Module 06b is no longer part of the run of show. Do not provision LibreChat, learner + accounts, or its MCP connections for the current workshop. Continue directly from + Module 06 to [Module 07](/docs/instructor/07-break-and-fix). The notes below remain + only as a historical reference. + + Facilitator companion to [06b AI SRE with hosted LibreChat](/docs/learner/06b-ai-sre-librechat). -## Timing +## Historical timing -Optional, about 15 minutes. Run it immediately before Module 07. Skip it if the room is -tight; Module 06a already covers the remote ClickStack MCP workflow. +This module previously took about 15 minutes immediately before Module 07. Module 06 +already covers the remote ClickStack MCP workflow. -## Required cloud prework +## Historical cloud prework Learners must not deploy LibreChat or MongoDB locally. @@ -41,14 +48,14 @@ LibreChat documents remote MCP OAuth, per-user credentials, and agent sharing in ## Talk track -- Module 06a is an agent in the editor; 06b is the same evidence chain in a hosted chat UI. +- Module 06 is an agent in the editor; 06b was the same evidence chain in a hosted chat UI. - LibreChat is a client surface. ClickStack/HyperDX and the telemetry remain managed in ClickHouse Cloud; GitHub remains the source-of-truth service. - Each learner owns their ClickHouse OAuth session and GitHub PAT. ## Common failures -- **Hosted URL is unavailable**: skip 06b and continue with Module 06a. Do not improvise a +- **Hosted URL is unavailable**: skip this archived module and continue with Module 06. Do not improvise a local Docker deployment during the workshop. - **OAuth returns to the wrong host**: correct LibreChat's public `DOMAIN_SERVER` and `DOMAIN_CLIENT` to the HTTPS deployment URL, then reconnect. diff --git a/workshops/build_workshop/playbook/content/docs/instructor/07-break-and-fix.mdx b/workshops/build_workshop/playbook/content/docs/instructor/07-break-and-fix.mdx index 8bddf83..070e51a 100644 --- a/workshops/build_workshop/playbook/content/docs/instructor/07-break-and-fix.mdx +++ b/workshops/build_workshop/playbook/content/docs/instructor/07-break-and-fix.mdx @@ -17,8 +17,6 @@ so the wrap-up is not squeezed. ## Talk track - This is the payoff: use everything built so far to run a real incident. -- If the room completed 06b, fault 01 is already diagnosed — this module becomes - confirm-fix-verify, so keep the momentum and move quickly to the fix. - Describe the symptom, not the cause, and let the agents converge from telemetry. - Consider showing several attendees' diagnoses side by side on the projector. - The lab runs fault 01. If time permits an extra round, add fault 02 (and fault 03 only diff --git a/workshops/build_workshop/playbook/content/docs/instructor/09-wrap-up.mdx b/workshops/build_workshop/playbook/content/docs/instructor/09-wrap-up.mdx index d8c8d58..129c257 100644 --- a/workshops/build_workshop/playbook/content/docs/instructor/09-wrap-up.mdx +++ b/workshops/build_workshop/playbook/content/docs/instructor/09-wrap-up.mdx @@ -24,6 +24,6 @@ About 10 minutes. Reserve time for two or three volunteer demos on the projector ## Reset steps -- Reset any shared state used for demos (managed Postgres fallback pool, hosted LibreChat, - projector channel). +- Reset any shared state used for demos (managed Postgres fallback pool and projector + channel). - TODO: confirm the shared-state teardown checklist. diff --git a/workshops/build_workshop/playbook/content/docs/instructor/index.mdx b/workshops/build_workshop/playbook/content/docs/instructor/index.mdx index 31f195a..cd5cc30 100644 --- a/workshops/build_workshop/playbook/content/docs/instructor/index.mdx +++ b/workshops/build_workshop/playbook/content/docs/instructor/index.mdx @@ -31,8 +31,7 @@ the dry run and record the final numbers here. | 04 | ClickHouse Agents | 10 min | Beta tool; have a fallback question ready | | 05 | ClickStack | 15 min | Telemetry needs time to accumulate before module 07 | | 06 | AI SRE | 15 min | MCP OAuth is the fragile moment; pre-stage config | -| 06b | Hosted LibreChat AI SRE | Optional, 15 min | Pre-provision HTTPS instance, model, accounts, and remote MCPs | -| 07 | Break and fix | 20 min | Direct payoff from 06/06b; hard-stop management matters | +| 07 | Break and fix | 20 min | Direct payoff from 06; hard-stop management matters | | 08 | Chat and Langfuse | 15 min | Keys already set in module 00; restart plus trace tour | | 09 | Wrap-up | 10 min | Volunteers demo; reset any shared state | @@ -52,8 +51,6 @@ before doors open: - **Managed Postgres fallback pool** — cloud-hosted instances only for attendees whose org cannot create one. The primary path is one ClickHouse-managed Postgres per learner. -- **Hosted LibreChat** — an HTTPS cloud deployment and learner accounts for optional - Module 06b. Do not run LibreChat or MongoDB on learner machines. - **Loaner ClickHouse Cloud services** — a small pool for attendees whose trial fails. TODO: pool size and distribution method. - **Runtime LLM key pool** — spend-capped fallback keys for the in-app chat (module 08). @@ -77,7 +74,6 @@ should arrive then. TODO: confirm the clinic start time and staffing. - diff --git a/workshops/build_workshop/playbook/content/docs/instructor/meta.json b/workshops/build_workshop/playbook/content/docs/instructor/meta.json index f26058f..8c9386c 100644 --- a/workshops/build_workshop/playbook/content/docs/instructor/meta.json +++ b/workshops/build_workshop/playbook/content/docs/instructor/meta.json @@ -12,7 +12,6 @@ "04-clickhouse-agents", "05-clickstack", "06-ai-sre", - "06b-ai-sre-librechat", "07-break-and-fix", "08-chat-langfuse", "09-wrap-up" diff --git a/workshops/build_workshop/playbook/content/docs/learner/00-setup.mdx b/workshops/build_workshop/playbook/content/docs/learner/00-setup.mdx index 8cc0326..bc80909 100644 --- a/workshops/build_workshop/playbook/content/docs/learner/00-setup.mdx +++ b/workshops/build_workshop/playbook/content/docs/learner/00-setup.mdx @@ -17,7 +17,7 @@ In about **25 minutes** you will have: - Langfuse and OpenAI keys; and - the app healthy at [localhost:8080](http://localhost:8080). -ClickHouse, Postgres, ClickPipes, ClickStack/HyperDX, Langfuse, LibreChat, and MCP +ClickHouse, Postgres, ClickPipes, ClickStack/HyperDX, Langfuse, and MCP endpoints are cloud-hosted. Only the workshop app, CLI/client tools, coding agent, load generator, and stateless telemetry collector run on your machine. diff --git a/workshops/build_workshop/playbook/content/docs/learner/05-clickstack.mdx b/workshops/build_workshop/playbook/content/docs/learner/05-clickstack.mdx index 9c28303..2d8a8b2 100644 --- a/workshops/build_workshop/playbook/content/docs/learner/05-clickstack.mdx +++ b/workshops/build_workshop/playbook/content/docs/learner/05-clickstack.mdx @@ -1,6 +1,6 @@ --- title: 05 ClickStack -description: Enable Managed ClickStack, forward telemetry with a local stateless collector, and inspect it in cloud-hosted HyperDX. +description: Forward telemetry with a local stateless collector, enable Managed ClickStack, and inspect it in cloud-hosted HyperDX. --- ## Starting point @@ -23,35 +23,7 @@ produces telemetry you can query. App traces and backend query logs flowing into ClickStack, with at least one end-to-end request trace and a visible stream of successful query records. -## Step 1 — Enable Managed ClickStack on your service - -The workshop uses **Managed ClickStack (HyperDX)** inside your own ClickHouse Cloud -service: the collector writes `otel_*` tables to your service and the HyperDX UI renders -them there. Enable it in the console: - -Console - your service -> **ClickStack** -> **Start Ingestion** -> skip the collector -step (the app ships its own collector in Step 2) -> **Launch ClickStack** - -That single-signs you on into HyperDX. (The UI is empty at this point — telemetry starts -flowing once the collector overlay is running in Step 2.) - - - - The back end uses vanilla OpenTelemetry, not the convenience - `hyperdx-opentelemetry` package the ClickStack docs suggest. That package hard-pins - `opentelemetry-api==1.30.0`, which conflicts with the Langfuse v4 SDK the chat - feature uses (needs `opentelemetry-api>=1.33.1`) — the two cannot share one - environment. The ClickStack collector ingests standard OTLP, so the vanilla distro - behaves identically; the workshop just sets the exporter env vars itself. - - ClickStack's telemetry lives in a separate database on your service - (`CLICKSTACK_DATABASE=otel`), distinct from the app's data database - (`CLICKHOUSE_DATABASE=nyc_tlc_data`). - - Traces and backend Python logs flow over OTLP from the auto-instrumented back end. - The optional `--profile container-logs` collector is only for non-instrumented - services such as the trip writer; its Linux Docker log path may not be available on - Docker Desktop. - - -## Step 2 — Run the OpenTelemetry collector overlay +## Step 1 — Run the OpenTelemetry collector overlay HyperDX, storage, and query compute stay managed in ClickHouse Cloud. The only local component here is a stateless OpenTelemetry collector beside the local app; it forwards @@ -71,15 +43,6 @@ telemetry and is not a local ClickStack or HyperDX deployment. ### `.env.workshop` and `docker-compose.otel.yml` -Append the observability variables to your single `.env.workshop`, then bring the stack -up with the overlay. The overlay sets `OTEL_ENABLED=true` on the back end for you and -adds the `otel-collector` service (`clickhouse/clickstack-otel-collector`): - -```bash -docker compose --env-file .env.workshop \ - -f docker-compose.workshop.yml -f docker-compose.otel.yml up -d -``` - These live in the ClickStack observability section of `.env.workshop.example`; fill them in your `.env.workshop`: @@ -90,11 +53,56 @@ OTEL_SERVICE_NAME=nyc-taxi-backend # the service name shown in HyperDX LOG_LEVEL=DEBUG # show successful queries in Log source ``` +Source the updated file so later commands can use the same values: + +```bash +set -a +source .env.workshop +set +a +``` + +Now bring the stack up with the overlay. The overlay sets `OTEL_ENABLED=true` on the +back end, adds the `otel-collector` service (`clickhouse/clickstack-otel-collector`), +and rebuilds the front end with the browser telemetry settings: + +```bash +docker compose --env-file .env.workshop \ + -f docker-compose.workshop.yml -f docker-compose.otel.yml up -d --build +``` + The collector reuses `CLICKHOUSE_HOST` / `CLICKHOUSE_PORT` / `CLICKHOUSE_USER` / `CLICKHOUSE_PASSWORD` from `.env.workshop`; the back end exports OTLP to `http://otel-collector:4318` (HTTP/protobuf). To also scrape raw container stdout on a Linux host, add `--profile container-logs`. +## Step 2 — Enable Managed ClickStack on your service + +The workshop uses **Managed ClickStack (HyperDX)** inside your own ClickHouse Cloud +service: the collector writes `otel_*` tables to your service and the HyperDX UI renders +them there. Enable it in the console: + +Console - your service -> **ClickStack** -> **Start Ingestion** -> skip the collector +step (the app collector is already running from Step 1) -> **Launch ClickStack** + +That single-signs you on into HyperDX. Telemetry has already started flowing, so the +hosted UI can populate as soon as it opens. + + + - The back end uses vanilla OpenTelemetry, not the convenience + `hyperdx-opentelemetry` package the ClickStack docs suggest. That package hard-pins + `opentelemetry-api==1.30.0`, which conflicts with the Langfuse v4 SDK the chat + feature uses (needs `opentelemetry-api>=1.33.1`) — the two cannot share one + environment. The ClickStack collector ingests standard OTLP, so the vanilla distro + behaves identically; the workshop just sets the exporter env vars itself. + - ClickStack's telemetry lives in a separate database on your service + (`CLICKSTACK_DATABASE=otel`), distinct from the app's data database + (`CLICKHOUSE_DATABASE=nyc_tlc_data`). + - Traces and backend Python logs flow over OTLP from the auto-instrumented back end. + The optional `--profile container-logs` collector is only for non-instrumented + services such as the trip writer; its Linux Docker log path may not be available on + Docker Desktop. + + ## Step 3 — Generate and find traffic in ClickStack Open the Ops dashboard and leave its default `1m` interval and `5s` auto-refresh running diff --git a/workshops/build_workshop/playbook/content/docs/learner/06-ai-sre.mdx b/workshops/build_workshop/playbook/content/docs/learner/06-ai-sre.mdx index 3ff5017..f3aacd1 100644 --- a/workshops/build_workshop/playbook/content/docs/learner/06-ai-sre.mdx +++ b/workshops/build_workshop/playbook/content/docs/learner/06-ai-sre.mdx @@ -1,5 +1,5 @@ --- -title: 06a AI SRE +title: 06 AI SRE description: Connect your coding agent to the ClickStack MCP and have it build an SRE dashboard and an alert over your telemetry. --- @@ -125,8 +125,6 @@ artifacts from it. That is exactly what you will lean on when something breaks. ## End state -An AI-built SRE dashboard and alert are in place. Continue to optional -[06b AI SRE with LibreChat](/docs/learner/06b-ai-sre-librechat) if you want a chat-native -agent that reads both telemetry and source, then use that agent in -[07 Test, fail, and fix](/docs/learner/07-break-and-fix). If you are skipping 06b, go straight -to module 07 with the editor agent you configured here. +An AI-built SRE dashboard and alert are in place. Continue to +[07 Test, fail, and fix](/docs/learner/07-break-and-fix) with the editor agent you +configured here. diff --git a/workshops/build_workshop/playbook/content/docs/learner/06b-ai-sre-librechat.mdx b/workshops/build_workshop/playbook/content/docs/learner/06b-ai-sre-librechat.mdx index f7a1a39..f314a8f 100644 --- a/workshops/build_workshop/playbook/content/docs/learner/06b-ai-sre-librechat.mdx +++ b/workshops/build_workshop/playbook/content/docs/learner/06b-ai-sre-librechat.mdx @@ -1,11 +1,19 @@ --- -title: 06b AI SRE with hosted LibreChat -description: Optional — use a cloud-hosted LibreChat SRE agent with remote ClickStack and GitHub MCP connections. +title: "Archived: 06b AI SRE with hosted LibreChat" +description: Archived reference for the former optional hosted LibreChat module. --- -## Starting point + + Module 06b is no longer part of the workshop. Do not provision LibreChat for the + current learner path. Continue directly from [06 AI SRE](/docs/learner/06-ai-sre) to + [07 Test, fail, and fix](/docs/learner/07-break-and-fix). The material below remains + available only as a historical reference. + + +## Historical starting point -Optional module. You are on `build-workshop-v1`; budget about **15 minutes**. +When this module was active, learners stayed on `build-workshop-v1` and budgeted about +**15 minutes**. Prerequisites: Module 05 is complete, your instructor has provided an HTTPS LibreChat URL and account, and you have a fine-grained GitHub token with read-only access to @@ -21,7 +29,7 @@ starting this module. ## Why -Module 06a put the agent in your editor. Here a chat-native SRE agent investigates two +Module 06 put the agent in your editor. Here a chat-native SRE agent investigates two remote planes at once: ClickStack telemetry and the public workshop source on GitHub. ## Step 1 — Sign in to hosted LibreChat @@ -93,4 +101,4 @@ The agent should connect the `nyc-taxi-frontend` error to - The agent has connected remote `clickstack` and `github` MCP tools. - Its diagnosis cites both telemetry and a source file on the selected fault branch. -Continue to [07 Test, fail, and fix](/docs/learner/07-break-and-fix). +The active workshop continues at [07 Test, fail, and fix](/docs/learner/07-break-and-fix). diff --git a/workshops/build_workshop/playbook/content/docs/learner/07-break-and-fix.mdx b/workshops/build_workshop/playbook/content/docs/learner/07-break-and-fix.mdx index b562f7e..1fa59dc 100644 --- a/workshops/build_workshop/playbook/content/docs/learner/07-break-and-fix.mdx +++ b/workshops/build_workshop/playbook/content/docs/learner/07-break-and-fix.mdx @@ -6,8 +6,7 @@ description: Inject a known fault, diagnose it through ClickStack, apply a fix, ## Outcome In about **20 minutes**, you will run one incident from failure to verified recovery. This -continues directly from Module 06a or optional 06b and uses the ClickStack MCP configured -in Module 00. +continues directly from Module 06 and uses the ClickStack MCP configured in Module 00. ## Step 1 — Choose one fault @@ -40,9 +39,6 @@ docker compose --env-file .env.workshop \ Open a fresh browser session at [localhost:8080](http://localhost:8080). **The empty map is expected now**; a failure elsewhere is not part of this scenario. -If Module 06b already injected Fault 01, skip the branch switch and rebuild only the -frontend. - ## Step 3 — Diagnose from evidence Give the agent the symptom, not the answer: diff --git a/workshops/build_workshop/playbook/content/docs/learner/index.mdx b/workshops/build_workshop/playbook/content/docs/learner/index.mdx index 87ae992..1379450 100644 --- a/workshops/build_workshop/playbook/content/docs/learner/index.mdx +++ b/workshops/build_workshop/playbook/content/docs/learner/index.mdx @@ -21,8 +21,7 @@ on WSL 2, so all shared command blocks are identical after setup. 4. [ClickHouse Agents](/docs/learner/04-clickhouse-agents) - ask your data questions in plain language. 5. [ClickStack](/docs/learner/05-clickstack) - see the app's own traces and logs in HyperDX. 6. [AI SRE](/docs/learner/06-ai-sre) - have your agent build an SRE dashboard and alert over that telemetry. -6b. [Hosted LibreChat AI SRE](/docs/learner/06b-ai-sre-librechat) - optional hosted chat workflow over ClickStack and GitHub MCP. -7. [Break and fix](/docs/learner/07-break-and-fix) - continue from AI SRE or optional 06b, inject a fault, diagnose it, and fix it. +7. [Break and fix](/docs/learner/07-break-and-fix) - continue from AI SRE, inject a fault, diagnose it, and fix it. 8. [Chat and Langfuse](/docs/learner/08-chat-langfuse) - wire an in-app AI chat and trace every turn. 9. [Wrap-up](/docs/learner/09-wrap-up) - recap, take it home, extend it to your own data. @@ -44,8 +43,7 @@ About **2h30 hands-on** - and your dashboard is live on real data within the fir ## Cost ClickHouse Cloud trial credits cover the ClickHouse-managed services. Budget about -**$5 of OpenAI credit** for Module 08. Optional self-paced Module 06b may also incur a -small Railway hosting charge; instructor-led sessions use the provided hosted instance. +**$5 of OpenAI credit** for Module 08. ## Everything is copy-paste diff --git a/workshops/build_workshop/playbook/content/docs/learner/meta.json b/workshops/build_workshop/playbook/content/docs/learner/meta.json index 367c48a..858d7a9 100644 --- a/workshops/build_workshop/playbook/content/docs/learner/meta.json +++ b/workshops/build_workshop/playbook/content/docs/learner/meta.json @@ -13,7 +13,6 @@ "04-clickhouse-agents", "05-clickstack", "06-ai-sre", - "06b-ai-sre-librechat", "07-break-and-fix", "08-chat-langfuse", "09-wrap-up", diff --git a/workshops/build_workshop/playbook/public/workshop-architecture.svg b/workshops/build_workshop/playbook/public/workshop-architecture.svg index cd365ec..f261096 100644 --- a/workshops/build_workshop/playbook/public/workshop-architecture.svg +++ b/workshops/build_workshop/playbook/public/workshop-architecture.svg @@ -1,107 +1,54 @@ - - + + -ClickHouse BUILD Workshop · Architecture (target end state) -App-side components run locally; stateful data services run in ClickHouse Cloud. Dashed lines are control/OAuth; solid lines are data. - -PARTICIPANT LAPTOP · the NYC-taxi app runs here - -CLICKHOUSE CLOUD · your trial org (you create all of this) - -THIRD-PARTY - -frontend -React SPA · nginx :8080 -Ops + Historical dashboards, chat - -backend -FastAPI :8000 -analytics API + /api/chat (NL-to-SQL) - -otel-collector -ClickStack overlay (module 05) - -pg-trip-writer -synthetic trips; creates the CDC -table + publication on first run - -coding agent + clickhousectl -Claude Code / Cursor / Codex -+ ClickHouse skills + docs llms.txt - -ClickHouse service :8443 TLS -nyc_tlc_data (taxi_trips, taxi_zones, -views, CDC MV) + otel db (logs, traces) - -ClickPipes -Postgres CDC pipe -snapshot + stream (~60s) - -Managed ClickStack / HyperDX -traces + logs UI -module 05 - -Postgres managed by ClickHouse :5432 TLS -you create it with clickhousectl (module 03) · public.realtime_trips + pub_taxi - -Remote MCP -/mcp + /clickstack (OAuth) - -ClickHouse Agents -ai.clickhouse.cloud (module 04) - -Instructor cloud fallback only -managed Postgres pool when a trial org cannot create one - -Langfuse Cloud -chat traces, sessions, cost - -OpenAI API -chat completions (gpt-5.4-mini) - -NYC TLC dataset -public parquet; read once by the -module 01 seed. Data source only. - -Hosted LibreChat -optional SRE chat · remote MCP - - -/api proxy - - -OTLP - - -SQL · TLS :8443 - - -traces → otel db - - -INSERT trips · TLS - - -MCP · OAuth - - -logical replication - - -CDC rows ~60s - - -reads otel db - - -RBAC SQL - - -chat completions - - -chat traces (Langfuse SDK) - - -url() seed +ClickHouse BUILD Workshop · Where each component runs +Grouped by location; no data direction is implied. Follow the next diagram for the trip path. + +PARTICIPANT LAPTOP · local app + tools + +CLICKHOUSE CLOUD · your trial org + +EXTERNAL SERVICES + +Frontend + FastAPI +dashboards, chat, and API +runs with Docker Compose + +Load generator +creates synthetic trip rows + +OTel forwarder +sends telemetry to ClickStack + +Coding agent + CLI +workshop commands, MCP, +and ClickHouse skills + +ClickHouse service +default.realtime_trips lands here +nyc_tlc_data.taxi_trips powers the app + +Managed Postgres +public.realtime_trips + +ClickPipes +streams Postgres changes + +Managed ClickStack +HyperDX traces, logs, +dashboards, and alerts + +Agents + MCP +data questions and +coding-agent tools + +OpenAI API +chat completions + +Langfuse Cloud +chat traces and cost + +NYC TLC dataset +one-time historical +seed source diff --git a/workshops/build_workshop/playbook/public/workshop-data-flow.svg b/workshops/build_workshop/playbook/public/workshop-data-flow.svg index e60ec1a..dd9f36b 100644 --- a/workshops/build_workshop/playbook/public/workshop-data-flow.svg +++ b/workshops/build_workshop/playbook/public/workshop-data-flow.svg @@ -1,105 +1,60 @@ - - + + -ClickHouse BUILD Workshop · Data flow -How a row moves: seeded once, streamed continuously, then read and observed — all through your ClickHouse service. - - -Seed -one-time, module 01 - -NYC TLC parquet -~3.2M rows, public - -ClickHouse service -nyc_tlc_data.taxi_trips - - -url() seed — one statement, module 01 - - -Live CDC -continuous, module 03 - -pg-trip-writer -synthetic trips - -Managed Postgres -public.realtime_trips - -ClickPipes -Postgres CDC pipe - -ClickHouse service -MV → taxi_trips - - -INSERT · TLS - - -replication - - -CDC rows ~60s - - -Read -modules 02, 04, 08 - -ClickHouse service -nyc_tlc_data - -FastAPI -safe parameterized SQL - -Ops + Historical -React dashboards - - -SELECT - - -GET /api/* - -in-app AI chat -guarded SELECT (module 08) - -ClickHouse Agents -RBAC SQL (module 04) - - -NL → SQL - - -RBAC SQL - - -Observe -modules 05, 06, 07 - -backend spans -clickhouse.query + logs - -stateless OTel forwarder -local OTLP :4318 - -otel db -in your service - -HyperDX UI -search, traces, dashboards - - -OTLP - - -write - - -read - -coding agent -via ClickStack MCP - -clickstack_search / save_dashboard / save_alert +ClickHouse BUILD Workshop · A trip's path +Follow steps 1–6: across the top, then back across the bottom. + +Load generator +creates synthetic trips +on your laptop + +1 + +Managed Postgres +source table: +public.realtime_trips + +2 + +ClickPipes +streams each change +with Postgres CDC + +3 + +ClickHouse +first landing table: +default.realtime_trips + +4 + +Materialized view +moves new rows into +nyc_tlc_data.taxi_trips + +5 + +Frontend + FastAPI +reads taxi_trips for +the live dashboards + +6 + + +INSERT + + +CDC + + +lands here + + +feeds + + +reads + +What the other workshop tools do +ClickStack observes the app · ClickHouse Agents answers data questions · Langfuse traces the chat diff --git a/workshops/build_workshop/playbook/public/workshop-module-flow.svg b/workshops/build_workshop/playbook/public/workshop-module-flow.svg index 322eaf7..7318a01 100644 --- a/workshops/build_workshop/playbook/public/workshop-module-flow.svg +++ b/workshops/build_workshop/playbook/public/workshop-module-flow.svg @@ -1,8 +1,8 @@ - - + + ClickHouse BUILD Workshop · Module flow -~2h30 core + optional 06b · only module 07 switches to a fault branch +~2h30 hands-on · only module 07 switches to a fault branch 00 Setup @@ -47,28 +47,22 @@ builds dashboard + alert -06b Hosted LibreChat -optional · 15 min -remote ClickStack + GitHub MCP -chat-native SRE workflow +07 Test, fail, and fix +20 min +inject a fault, diagnose +with the AI SRE, fix it -07 Test, fail, and fix -20 min -inject a fault, diagnose -with the AI SRE, fix it +08 Chat + Langfuse +15 min +in-app AI chat, +every turn traced -08 Chat + Langfuse -15 min -in-app AI chat, -every turn traced - - -09 Wrap-up -10 min -running prototype, -take it home +09 Wrap-up +10 min +running prototype, +take it home @@ -78,6 +72,5 @@ - After module 03: historical + live data both flowing into your Cloud service. diff --git a/workshops/build_workshop/scripts/check-docs.sh b/workshops/build_workshop/scripts/check-docs.sh index e3f41f6..6aac6a2 100755 --- a/workshops/build_workshop/scripts/check-docs.sh +++ b/workshops/build_workshop/scripts/check-docs.sh @@ -29,6 +29,24 @@ fail_if_found \ '(comment|uncomment).*(sql|variant)|(run|execute|open).*(db/cloud|\.sql file)' \ "${CONTENT}/learner" +if grep -RInE --exclude='06b-ai-sre-librechat.mdx' \ + '06a|06b|LibreChat' "${CONTENT}"; then + echo "ERROR: active workshop must use Module 06 and exclude archived Module 06b" >&2 + exit 1 +fi + +fail_if_found \ + "active workshop summaries and diagrams must not include archived Module 06b" \ + '06b|LibreChat' \ + "${ROOT}/README.md" \ + "${ROOT}/app/README.md" \ + "${ROOT}/app/WORKSHOP_CHANGES.md" \ + "${ROOT}/docs/diagrams/gen_diagrams.py" \ + "${ROOT}/docs/diagrams/workshop-module-flow.svg" \ + "${ROOT}/docs/diagrams/workshop-architecture.svg" \ + "${ROOT}/playbook/public/workshop-module-flow.svg" \ + "${ROOT}/playbook/public/workshop-architecture.svg" + fail_if_found \ "workshop material must not direct users to local managed-service substitutes" \ 'PGHOST=postgres|localhost:3090|docker-compose\.librechat|local `mcp-clickhouse`|start (a |the )?local (Postgres|ClickHouse|LibreChat|HyperDX)|using (a |the )?local (Postgres|ClickHouse)' \ @@ -69,6 +87,37 @@ require_fixed \ 'git switch build-workshop-v1' \ "${CONTENT}/learner/00-setup.mdx" +require_fixed \ + "the former learner Module 06b page must remain clearly archived" \ + 'Module 06b is no longer part of the workshop.' \ + "${CONTENT}/learner/06b-ai-sre-librechat.mdx" + +require_fixed \ + "the former instructor Module 06b page must remain clearly archived" \ + 'Module 06b is no longer part of the run of show.' \ + "${CONTENT}/instructor/06b-ai-sre-librechat.mdx" + +require_fixed \ + "the archived instructor page must link to active Module 07" \ + '[Module 07](/docs/instructor/07-break-and-fix)' \ + "${CONTENT}/instructor/06b-ai-sre-librechat.mdx" + +for file in \ + "${ROOT}/app/README.md" \ + "${ROOT}/app/OBSERVABILITY.md" \ + "${CONTENT}/learner/05-clickstack.mdx" \ + "${CONTENT}/instructor/05-clickstack.mdx"; do + require_fixed \ + "ClickStack overlay startup must rebuild the frontend telemetry bundle" \ + 'docker-compose.otel.yml up -d --build' \ + "${file}" +done + +require_fixed \ + "the optional container-log overlay must also rebuild the frontend" \ + '--profile container-logs up -d --build' \ + "${ROOT}/app/OBSERVABILITY.md" + require_count() { local description=$1 local expected=$2