Skip to content

Anthropic MVP#126

Open
MichaelMcCulloch wants to merge 26 commits intomicrosoft:mainfrom
MichaelMcCulloch:main
Open

Anthropic MVP#126
MichaelMcCulloch wants to merge 26 commits intomicrosoft:mainfrom
MichaelMcCulloch:main

Conversation

@MichaelMcCulloch
Copy link
Copy Markdown

No description provided.

Comment thread pyproject.toml Outdated

dependencies = [
"llmx>=0.0.21a",
"llmx@git+https://github.com/MichaelMcCulloch/llmx.git#egg=main",
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

TODO: Revert this to upstream llmx when and if the above is merged into it

MichaelMcCulloch and others added 24 commits August 8, 2024 12:44
overhaul the backend and the UI

See merge request MichaelMcCulloch/lida!1
Replace the single "Analyzing data..." spinner with a deterministic
5-stage tracker (compress / upload / decompress / analyze / visualize),
real byte-level compress + upload progress bars, an LLM token counter
fed by streaming deltas, and per-table sub-progress for sqlite/tar
fan-out.

- LiteLLMTextGenerator gains a token_sink ContextVar; when set, the
  generator switches to OpenAI streaming and routes deltas to the sink
  without changing adapter call sites.
- New POST /api/v1/summarize/stream returns text/event-stream and emits
  stage/llm.token/table/chart events; final 'complete' payload mirrors
  the existing /summarize response shape so the App handler is unchanged.
- Frontend useUploadPipeline hook drives chunked pako compression, XHR
  upload progress, and incremental SSE frame parsing into a reducer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SQLite BLOBs (and similar object-dtype byte columns) were being sampled
and f-stringed into the summarizer prompt, where the LLM either echoed
the raw bytes back or hallucinated against them — observed as
little-endian float32 garbage in DeepSeek output for embedding tables.

- Detect bytes/bytearray/memoryview columns and classify dtype="binary".
- Replace samples with size summaries (<binary blob: N bytes>) and
  expose min/max/mean size so the model has shape context.
- Add a recursive _scrub_summary() pass in enrich() as defense-in-depth
  for stragglers (mixed-type columns, numpy bytes_, nested structures).
- Catch TypeError in pd.to_datetime so non-string object columns no
  longer crash detection.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three things, one commit because they ride together:

1. Parallel table fan-out. SQLite/tar uploads now run summarize_and_visualize
   concurrently across tables via ThreadPoolExecutor (LIDA_TABLE_CONCURRENCY,
   default 4). Token sinks moved into summarize_and_visualize so each table
   tags its llm.token events with file_name. Per-table chart pools are sized
   to share CPUs (cpu_count // n_parallel) so we don't oversubscribe when 4
   tables each spawn matplotlib worker pools. Original table order is
   preserved in the response.

2. SSE keepalive. Yield ': keepalive' comment every 5s when the queue is
   quiet so buffering proxies (Vite dev proxy, nginx, k8s ingress) don't
   hold bytes back during gaps between LLM streaming and the heuristic
   phases — the gap was masking summary.ready and later events on a
   deployed setup.

3. Pipeline logging at emit boundaries (summary.ready, goals.ready,
   viz.code.ready, chart count, complete) so the docker logs show real
   pipeline progress instead of going silent after the LLM call.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Goal generation now uses an SSE endpoint (/api/v1/goal/stream) so the user
sees progress while the LLM is producing the array. Goals appear one at a
time as their closing brace arrives — same cost as the existing single
call (no per-goal prompt overhead, the LLM still balances diversity across
the full set), with progressive UI.

- Extracted _sse_streaming_response so /summarize/stream and /goal/stream
  share the queue/thread/keepalive plumbing.
- _GoalStreamParser walks streamed tokens once, tracking brace depth and
  string state to emit each top-level object as it closes. State is
  preserved across feeds so we don't rescan from the start.
- Final 'goals' event still emits the adapter's authoritative parse so
  anything the streaming tracker missed is reconciled.
- Frontend: useSseFetch hook (XHR + incremental SSE parser, smaller
  cousin of useUploadPipeline). GoalGenerator now shows the LLM token
  counter while streaming and pops in goal cards as goal.ready events
  arrive.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The upload pipeline is now end-to-end LLM-driven and pipelined: as each
goal closes in the streamed JSON array, an LLM viz-code task is
submitted to a thread pool without waiting for subsequent goals. Chart
execution runs in a shared process pool because matplotlib state is
not thread-safe. With N=10 goals you get up to N+1 concurrent LLM
calls (1 goal stream + N plot streams) all running while goals are
still being generated.

User-visible changes:
- Goal-count slider (1-10, default 5) on the upload screen.
- Goal generation is automatic — no button click after upload.
- New GoalsBoard renders goals progressively with attached chart
  slots that show "generating code" -> "rendering chart" -> rendered,
  driven by plot.started / plot.code.ready / chart.rendered events.

Backend:
- /summarize/stream takes n_goals as a query param (clamped 1..10).
- summarize_and_visualize splits behavior on `emit`: streaming flow
  uses _llm_visualize_pipeline; JSON endpoint keeps heuristic flow.
- _GoalStreamParser feeds a goal token sink that submits plot tasks
  the moment each '{...}' closes.
- _run_plot_task per goal: LLM viz call with token sink (tagged with
  goal_index), then chart execution dispatched to ProcessPoolExecutor.
- New events: goal.ready, plot.started, plot.code.ready, plot.failed,
  chart.rendered (now goal_index-keyed). Existing llm.token events
  carry phase=summary|goals|plot for attribution.

Frontend:
- App.tsx now owns the pipeline hook and renders progressively from
  pipeline state (summary / goals / charts appear during the run, not
  just at the end). FileUploader becomes presentational.
- useUploadPipeline state grows summary, goals[], charts{}, plotStatuses{}
  on each TableProgress entry, populated by the new event types.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per the user's clarification: analyzing tables is a fence (wait for all),
but goals are NOT per-table — one LLM call ingests every analysis and
returns n_goals globally, each tagged with the data_source it queries.
Plots stay 1:1 with goals.

Pipeline phases (replacing the old per-table goals/plots inside each
summarize_and_visualize call):

1. analyze   per-table summaries in parallel; fence on completion.
2. goals     single LLM call with all summaries; goals carry data_source.
3. visualize 1:1 plot tasks per goal; each picks its DataFrame via
             data_source. LLM viz calls run in a thread pool, chart
             execution in a shared process pool.

Backend:
- summarize_and_visualize in streaming mode now returns summary only
  (and file_path so plots can re-open the dataframe). The orchestrator
  in _run_streaming_pipeline drives goals + plots at the global level.
- New _generate_cross_dataset_goals composes a multi-summary prompt,
  asks the model to emit a "data_source" field on every goal, and
  parses the stream incrementally into goal.ready events.
- New _run_parallel_plots dispatches plot tasks 1:1 with goals, each
  picking its (summary, data_df) by data_source.
- Stage list collapsed: dropped "decompress" (folded into analyze),
  added explicit "goals" stage. Final order: compress / upload /
  analyze / goals / visualize.
- JSON /summarize endpoint keeps its legacy heuristic per-table flow
  (unchanged behavior for non-streaming clients).

Frontend:
- PipelineState moves goals/charts/plotStatuses to the top level — they
  are no longer keyed by table because the backend produces them once
  globally. TableProgress now holds only {name, status, summary}.
- GoalsBoard takes GoalWithSource[] and renders a data_source pill on
  each card when the upload is multi-table.
- App.tsx selects the active table for summary display, but the goals
  board shows the global list with data_source attribution. The
  Visualizer pulls the right summary by matching selectedGoal.dataSource
  against the table list.
- TableProgressList simplified — per-table chart counts are gone since
  charts are global.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
LIDA_MO_INSTANT_ANALYSIS=false in the deployed env was suppressing
goals/visualize entirely — pipeline emitted "summary-only" right after
the analyze fence. The env var was a relic from the heuristic auto-flow;
in the new architecture an explicit n_goals slider implies the user
wants goals and plots. Default visualize=True unconditionally and have
the frontend pass it explicitly so server-side env changes can't bite
us again.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Plot tasks were calling lida.visualize(), which under the hood does both
code generation AND execution — so every chart ran twice and the second
pass got a ChartExecutorResponse where it expected a code string. The
'NoneType object is not subscriptable' errors in the docker logs were
the LLM-generated chart code crashing during the FIRST execution, and
'ChartExecutorResponse object has no attribute replace' in the UI was
preprocess_code() trying to .replace() the response object during the
SECOND execution attempt.

Switch to lida.vizgen.generate() which returns just the code strings,
then submit to chart_pool exactly once. Halves the work and gives clean
error attribution: failed user code now surfaces with the actual
seaborn / pandas exception message.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant