mikeboone Cursor commited on
Commit
9885709
·
1 Parent(s): 5712f77

feat: replace Spotter Viz method with Spotter Viz Story tab output

Browse files

Remove SPOTTER_VIZ as a liveboard creation method (HYBRID is now the
only method). Add post-liveboard Spotter Viz Story generation — a
conversational sequence of NL prompts for ThoughtSpot's Spotter Viz
agent, displayed in a new Gradio tab. Update .gitignore to allow
legitdata_project source files to be tracked.

Co-authored-by: Cursor <cursoragent@cursor.com>

Files changed (7) hide show
  1. .gitignore +4 -2
  2. CLAUDE.md +12 -23
  3. chat_interface.py +167 -5
  4. liveboard_creator.py +1 -1
  5. prompts.py +27 -0
  6. sprint_2026_02.md +26 -9
  7. thoughtspot_deployer.py +7 -97
.gitignore CHANGED
@@ -221,5 +221,7 @@ scratch/
221
 
222
  # Sprint documents (local working docs)
223
 
224
- # LegitData project (third-party/separate project - needs proper integration strategy)
225
- legitdata_project/
 
 
 
221
 
222
  # Sprint documents (local working docs)
223
 
224
+ # LegitData project - track source, ignore build artifacts
225
+ legitdata_project/venv/
226
+ legitdata_project/*.egg-info/
227
+ legitdata_project/.DS_Store
CLAUDE.md CHANGED
@@ -195,37 +195,22 @@ When user says "create a test for X":
195
  ```
196
  Liveboard Creation:
197
  HYBRID: chat_interface.py → create_liveboard_from_model_mcp() → enhance_mcp_liveboard()
198
- SPOTTER_VIZ: chat_interface.py → create_liveboard_from_model() → enhance_mcp_liveboard()
199
 
200
  DO NOT use create_visualization_tml() directly - that's internal low-level code
201
  ```
202
 
203
  ---
204
 
205
- ## Liveboard Creation - Two-Method System
206
 
207
- **Settings UI:** Admin tab "Liveboard Creation Method" dropdown
208
- **Two options:** HYBRID (default) and SPOTTER_VIZ
209
 
210
- | Method | Speed | Dependency | Best For |
211
- |--------|-------|------------|----------|
212
- | **HYBRID** | ~60-90s | MCP server | AI-driven question selection |
213
- | **SPOTTER_VIZ** | ~20-30s | Direct REST API only | Production demos, reliability |
214
-
215
- ### HYBRID Method (MCP + TML Post-Processing)
216
  1. MCP creates liveboard via `agent.thoughtspot.app` (bearer auth)
217
- 2. TML post-processing enhances with groups, KPIs, colors, layout
218
  - **Entry:** `create_liveboard_from_model_mcp()` → `enhance_mcp_liveboard()`
219
 
220
- ### SPOTTER_VIZ Method (Direct API + TML)
221
- 1. LiveboardCreator builds complete TML from outlier patterns + AI
222
- 2. Deploys via REST API `/metadata/tml/import`
223
- 3. Same TML post-processing as HYBRID (groups, KPIs, colors, layout)
224
- - **Entry:** `create_liveboard_from_model()` → `enhance_mcp_liveboard()`
225
- - No MCP dependency — uses same auth as table/model deployment
226
-
227
- ### Shared Post-Processing: enhance_mcp_liveboard()
228
- Both methods share the same post-processing function:
229
  1. Exports the liveboard TML
230
  2. Classifies visualizations by type (KPI, trend, categorical)
231
  3. Adds Groups with proper `group_layouts` (Golden Demo style)
@@ -234,9 +219,13 @@ Both methods share the same post-processing function:
234
  6. Applies brand colors (liveboard-level + group-level)
235
  7. Re-imports the enhanced TML
236
 
237
- ### Backward Compatibility
238
- - Old settings values "TML" or "MCP" are mapped to "HYBRID" in the router
239
- - `USE_MCP_LIVEBOARD=true` env var still works (maps to HYBRID)
 
 
 
 
240
 
241
  ### KPI Requirements
242
  - **For sparklines and percent change comparisons:**
 
195
  ```
196
  Liveboard Creation:
197
  HYBRID: chat_interface.py → create_liveboard_from_model_mcp() → enhance_mcp_liveboard()
 
198
 
199
  DO NOT use create_visualization_tml() directly - that's internal low-level code
200
  ```
201
 
202
  ---
203
 
204
+ ## Liveboard Creation HYBRID Method
205
 
206
+ **Single method:** HYBRID (MCP + TML post-processing)
 
207
 
208
+ ### Pipeline
 
 
 
 
 
209
  1. MCP creates liveboard via `agent.thoughtspot.app` (bearer auth)
210
+ 2. `enhance_mcp_liveboard()` post-processes with groups, KPIs, colors, layout
211
  - **Entry:** `create_liveboard_from_model_mcp()` → `enhance_mcp_liveboard()`
212
 
213
+ ### Post-Processing: enhance_mcp_liveboard()
 
 
 
 
 
 
 
 
214
  1. Exports the liveboard TML
215
  2. Classifies visualizations by type (KPI, trend, categorical)
216
  3. Adds Groups with proper `group_layouts` (Golden Demo style)
 
219
  6. Applies brand colors (liveboard-level + group-level)
220
  7. Re-imports the enhanced TML
221
 
222
+ ### Spotter Viz Story (Post-Liveboard Output)
223
+ After liveboard creation, a **Spotter Viz Story** is generated:
224
+ - A conversational sequence of natural language prompts for ThoughtSpot's Spotter Viz agent
225
+ - Uses company context, use case, outlier patterns, and liveboard visualizations
226
+ - Displayed in the "Spotter Viz Story" tab in the app
227
+ - Can be manually entered into Spotter Viz to recreate/refine the liveboard
228
+ - Future: will be automated when the Spotter Viz API is published
229
 
230
  ### KPI Requirements
231
  - **For sparklines and percent change comparisons:**
chat_interface.py CHANGED
@@ -149,6 +149,7 @@ class ChatDemoInterface:
149
  # New tab content
150
  self.live_progress_log = [] # Real-time deployment progress
151
  self.demo_pack_content = "" # Generated demo pack markdown
 
152
 
153
  def load_default_settings(self):
154
  """Load settings from Supabase or defaults"""
@@ -1937,6 +1938,135 @@ To change settings, use:
1937
  - **Ask questions**: Let the AI demonstrate natural language
1938
  - **End with action**: Show how insights lead to decisions""")
1939
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1940
  def run_research(self, company, use_case):
1941
  """Run the research phase"""
1942
  import time
@@ -3383,6 +3513,22 @@ Ask these questions to showcase ThoughtSpot's AI capabilities:
3383
  except Exception as e:
3384
  safe_print(f"Could not generate demo pack: {e}", flush=True)
3385
  self.demo_pack_content = f"*Demo pack generation failed: {e}*"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3386
 
3387
  # Build final response
3388
  if results.get('success'):
@@ -3769,6 +3915,14 @@ def create_chat_interface():
3769
  elem_classes=["demo-pack-content"]
3770
  )
3771
 
 
 
 
 
 
 
 
 
3772
  with gr.Tab("⚙️ Settings"):
3773
  settings_components = create_settings_tab()
3774
 
@@ -3792,13 +3946,16 @@ def create_chat_interface():
3792
  )
3793
 
3794
  # Create update function for tabs
 
 
3795
  def update_all_tabs(controller):
3796
  if controller is None:
3797
  return (
3798
  "",
3799
  "-- DDL will appear here after generation",
3800
  "Progress will appear here during deployment...",
3801
- "Demo pack will be generated after deployment completes.\n\nThis will include:\n- Key insights/outliers\n- Spotter questions to ask\n- Talking points for the demo"
 
3802
  )
3803
 
3804
  # Get live progress from controller (captures deployment output)
@@ -3809,11 +3966,16 @@ def create_chat_interface():
3809
  demo_pack = getattr(controller, 'demo_pack_content', '')
3810
  demo_pack_text = demo_pack if demo_pack else "Demo pack will be generated after deployment completes.\n\nThis will include:\n- Key insights/outliers\n- Spotter questions to ask\n- Talking points for the demo"
3811
 
 
 
 
 
3812
  return (
3813
  "\n".join(controller.ai_feedback_log),
3814
  controller.ddl_code if controller.ddl_code else "-- DDL will appear here after generation",
3815
  live_progress_text,
3816
- demo_pack_text
 
3817
  )
3818
 
3819
  # Wire up tab updates on chat interactions
@@ -3821,7 +3983,7 @@ def create_chat_interface():
3821
  chat_components['chatbot'].change(
3822
  fn=update_all_tabs,
3823
  inputs=[chat_controller_state],
3824
- outputs=[ai_feedback_display, ddl_display, live_progress_display, demo_pack_display]
3825
  )
3826
 
3827
  # Load settings from Supabase on startup (uses SETTINGS_SCHEMA)
@@ -4133,9 +4295,9 @@ def create_settings_tab():
4133
 
4134
  liveboard_method = gr.Dropdown(
4135
  label="Liveboard Creation Method",
4136
- choices=["HYBRID", "SPOTTER_VIZ"],
4137
  value="HYBRID",
4138
- info="HYBRID: MCP + TML polish (AI-driven). SPOTTER_VIZ: Direct API + TML (faster, no MCP dependency)."
4139
  )
4140
 
4141
  # Existing Model Section
 
149
  # New tab content
150
  self.live_progress_log = [] # Real-time deployment progress
151
  self.demo_pack_content = "" # Generated demo pack markdown
152
+ self.spotter_viz_story = "" # Spotter Viz story (NL prompts for Spotter Viz agent)
153
 
154
  def load_default_settings(self):
155
  """Load settings from Supabase or defaults"""
 
1938
  - **Ask questions**: Let the AI demonstrate natural language
1939
  - **End with action**: Show how insights lead to decisions""")
1940
 
1941
+ def _generate_spotter_viz_story(self, company_name: str, use_case: str,
1942
+ model_name: str = None, liveboard_name: str = None) -> str:
1943
+ """Generate a Spotter Viz story — a conversational sequence of NL prompts
1944
+ that can be entered into ThoughtSpot's Spotter Viz agent to build a liveboard.
1945
+
1946
+ Uses the build_prompt() system with stage="spotter_viz_story" + LLM call.
1947
+ Falls back to a template-based story if LLM fails.
1948
+ """
1949
+ try:
1950
+ from prompts import build_prompt
1951
+ from demo_personas import parse_use_case
1952
+
1953
+ v, f = parse_use_case(use_case or '')
1954
+ vertical = v or "Generic"
1955
+ function = f or "Generic"
1956
+
1957
+ # Build company context for the prompt
1958
+ company_context = f"Company: {company_name}\nUse Case: {use_case}"
1959
+ if model_name:
1960
+ company_context += f"\nData Source/Model: {model_name}"
1961
+ if liveboard_name:
1962
+ company_context += f"\nLiveboard Name: {liveboard_name}"
1963
+
1964
+ # Add research context if available
1965
+ if hasattr(self, 'demo_builder') and self.demo_builder:
1966
+ research = getattr(self.demo_builder, 'company_summary', '') or ''
1967
+ if research:
1968
+ company_context += f"\n\nCompany Research:\n{research[:1500]}"
1969
+
1970
+ prompt = build_prompt(
1971
+ stage="spotter_viz_story",
1972
+ vertical=vertical,
1973
+ function=function,
1974
+ company_context=company_context,
1975
+ )
1976
+
1977
+ # Make LLM call
1978
+ from litellm import completion
1979
+ llm_model = self.settings.get('model', 'claude-sonnet-4')
1980
+ self.log_feedback(f"🎬 Generating Spotter Viz story ({llm_model})...")
1981
+
1982
+ response = completion(
1983
+ model=llm_model,
1984
+ messages=[{"role": "user", "content": prompt}],
1985
+ max_tokens=2000,
1986
+ temperature=0.7,
1987
+ )
1988
+
1989
+ story = response.choices[0].message.content.strip()
1990
+
1991
+ # Add header
1992
+ header = f"""# Spotter Viz Story: {company_name}
1993
+ ## {use_case}
1994
+
1995
+ *Copy these prompts into ThoughtSpot Spotter Viz to build this liveboard interactively.*
1996
+
1997
+ ---
1998
+
1999
+ """
2000
+ return header + story
2001
+
2002
+ except Exception as e:
2003
+ self.log_feedback(f"⚠️ Spotter Viz story generation failed: {e}")
2004
+ # Fallback: build a basic template from what we know
2005
+ return self._build_fallback_spotter_story(company_name, use_case, model_name)
2006
+
2007
+ def _build_fallback_spotter_story(self, company_name: str, use_case: str,
2008
+ model_name: str = None) -> str:
2009
+ """Build a basic Spotter Viz story without LLM, using available context."""
2010
+ data_source = model_name or f"{company_name} model"
2011
+
2012
+ # Get spotter questions from outlier system
2013
+ spotter_qs = []
2014
+ try:
2015
+ from demo_personas import parse_use_case
2016
+ from outlier_system import get_outliers_for_use_case
2017
+ v, f = parse_use_case(use_case or '')
2018
+ if v or f:
2019
+ outlier_config = get_outliers_for_use_case(v or "Generic", f or "Generic")
2020
+ for op in outlier_config.required:
2021
+ for sq in op.spotter_questions[:1]:
2022
+ spotter_qs.append(sq)
2023
+ except:
2024
+ pass
2025
+
2026
+ story = f"""# Spotter Viz Story: {company_name}
2027
+ ## {use_case}
2028
+
2029
+ *Copy these prompts into ThoughtSpot Spotter Viz to build this liveboard interactively.*
2030
+
2031
+ ---
2032
+
2033
+ ### Step 1: Set Context
2034
+ > "Create a new liveboard for {company_name} {use_case} using the {data_source} data source."
2035
+
2036
+ **Expected result:** Empty liveboard created with the correct data source connected.
2037
+
2038
+ ### Step 2: Add Key KPIs
2039
+ > "Add KPI cards showing the main metrics with weekly sparklines."
2040
+
2041
+ **Expected result:** KPI tiles with sparkline trends at the top of the liveboard.
2042
+
2043
+ ### Step 3: Add Trend Analysis
2044
+ > "Add a line chart showing how the primary metric has trended over the last 12 months."
2045
+
2046
+ **Expected result:** Time-series visualization showing monthly trends.
2047
+
2048
+ ### Step 4: Add Category Breakdown
2049
+ > "Show a bar chart breaking down performance by the main dimension."
2050
+
2051
+ **Expected result:** Categorical breakdown chart.
2052
+
2053
+ ### Step 5: Add Comparison
2054
+ > "Add a comparison showing this period vs. last period."
2055
+
2056
+ **Expected result:** Period-over-period comparison visualization.
2057
+ """
2058
+ if spotter_qs:
2059
+ story += "\n### Step 6: Explore with Spotter Questions\n"
2060
+ for i, q in enumerate(spotter_qs[:3]):
2061
+ story += f'> "{q}"\n\n'
2062
+
2063
+ story += """
2064
+ ---
2065
+
2066
+ *Refine the liveboard further by asking Spotter Viz to adjust colors, reorganize tiles, or add filters.*
2067
+ """
2068
+ return story
2069
+
2070
  def run_research(self, company, use_case):
2071
  """Run the research phase"""
2072
  import time
 
3513
  except Exception as e:
3514
  safe_print(f"Could not generate demo pack: {e}", flush=True)
3515
  self.demo_pack_content = f"*Demo pack generation failed: {e}*"
3516
+
3517
+ # Generate Spotter Viz Story
3518
+ try:
3519
+ model_name_for_story = results.get('model', None)
3520
+ liveboard_name_for_story = results.get('liveboard', None)
3521
+ self.spotter_viz_story = self._generate_spotter_viz_story(
3522
+ company_name=company_name,
3523
+ use_case=use_case,
3524
+ model_name=model_name_for_story,
3525
+ liveboard_name=liveboard_name_for_story
3526
+ )
3527
+ safe_print("Spotter Viz Story generated - check the Spotter Viz Story tab.", flush=True)
3528
+ self.live_progress_log.append("Spotter Viz Story generated")
3529
+ except Exception as e:
3530
+ safe_print(f"Could not generate Spotter Viz story: {e}", flush=True)
3531
+ self.spotter_viz_story = f"*Spotter Viz story generation failed: {e}*"
3532
 
3533
  # Build final response
3534
  if results.get('success'):
 
3915
  elem_classes=["demo-pack-content"]
3916
  )
3917
 
3918
+ with gr.Tab("🎬 Spotter Viz Story"):
3919
+ gr.Markdown("### Spotter Viz Story — Natural Language Liveboard Builder")
3920
+ gr.Markdown("*Conversational prompts you can enter into ThoughtSpot Spotter Viz to recreate this liveboard.*")
3921
+ spotter_viz_story_display = gr.Markdown(
3922
+ value="Spotter Viz story will be generated after liveboard creation.\n\n**What is Spotter Viz?**\nSpotter Viz is an AI agent in ThoughtSpot that creates, structures, and styles Liveboards through natural language prompts. The agent reviews the data, proposes layouts, generates KPIs and visualizations, and allows conversational refinement.",
3923
+ elem_classes=["spotter-viz-story-content"]
3924
+ )
3925
+
3926
  with gr.Tab("⚙️ Settings"):
3927
  settings_components = create_settings_tab()
3928
 
 
3946
  )
3947
 
3948
  # Create update function for tabs
3949
+ spotter_viz_default = "Spotter Viz story will be generated after liveboard creation.\n\n**What is Spotter Viz?**\nSpotter Viz is an AI agent in ThoughtSpot that creates, structures, and styles Liveboards through natural language prompts. The agent reviews the data, proposes layouts, generates KPIs and visualizations, and allows conversational refinement."
3950
+
3951
  def update_all_tabs(controller):
3952
  if controller is None:
3953
  return (
3954
  "",
3955
  "-- DDL will appear here after generation",
3956
  "Progress will appear here during deployment...",
3957
+ "Demo pack will be generated after deployment completes.\n\nThis will include:\n- Key insights/outliers\n- Spotter questions to ask\n- Talking points for the demo",
3958
+ spotter_viz_default
3959
  )
3960
 
3961
  # Get live progress from controller (captures deployment output)
 
3966
  demo_pack = getattr(controller, 'demo_pack_content', '')
3967
  demo_pack_text = demo_pack if demo_pack else "Demo pack will be generated after deployment completes.\n\nThis will include:\n- Key insights/outliers\n- Spotter questions to ask\n- Talking points for the demo"
3968
 
3969
+ # Get Spotter Viz story from controller
3970
+ spotter_story = getattr(controller, 'spotter_viz_story', '')
3971
+ spotter_story_text = spotter_story if spotter_story else spotter_viz_default
3972
+
3973
  return (
3974
  "\n".join(controller.ai_feedback_log),
3975
  controller.ddl_code if controller.ddl_code else "-- DDL will appear here after generation",
3976
  live_progress_text,
3977
+ demo_pack_text,
3978
+ spotter_story_text
3979
  )
3980
 
3981
  # Wire up tab updates on chat interactions
 
3983
  chat_components['chatbot'].change(
3984
  fn=update_all_tabs,
3985
  inputs=[chat_controller_state],
3986
+ outputs=[ai_feedback_display, ddl_display, live_progress_display, demo_pack_display, spotter_viz_story_display]
3987
  )
3988
 
3989
  # Load settings from Supabase on startup (uses SETTINGS_SCHEMA)
 
4295
 
4296
  liveboard_method = gr.Dropdown(
4297
  label="Liveboard Creation Method",
4298
+ choices=["HYBRID"],
4299
  value="HYBRID",
4300
+ info="HYBRID: MCP creates liveboard + TML post-processing for styling, groups, and KPI fixes."
4301
  )
4302
 
4303
  # Existing Model Section
liveboard_creator.py CHANGED
@@ -2513,7 +2513,7 @@ def create_liveboard_from_model(
2513
  Create and deploy a Liveboard via TML (Spotter Viz path).
2514
 
2515
  This is the direct API approach — builds complete TML and imports it.
2516
- Used by both the legacy TML path and the new SPOTTER_VIZ method.
2517
 
2518
  Args:
2519
  ts_client: Authenticated ThoughtSpotDeployer instance
 
2513
  Create and deploy a Liveboard via TML (Spotter Viz path).
2514
 
2515
  This is the direct API approach — builds complete TML and imports it.
2516
+ Used by the direct TML liveboard creation path.
2517
 
2518
  Args:
2519
  ts_client: Authenticated ThoughtSpotDeployer instance
prompts.py CHANGED
@@ -555,6 +555,33 @@ Create a bullet outline demo script with:
555
  - The "aha moment" reveal
556
  - Spotter questions to ask live
557
  - Closing value proposition""",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
558
  }
559
 
560
  DEFAULT_TEMPLATE = """You are helping create a {vertical} {function} demo.
 
555
  - The "aha moment" reveal
556
  - Spotter questions to ask live
557
  - Closing value proposition""",
558
+
559
+ "spotter_viz_story": """You are creating a Spotter Viz story for ThoughtSpot's AI-powered liveboard builder.
560
+
561
+ Spotter Viz is an AI agent in ThoughtSpot that creates, structures, and styles Liveboards through natural language prompts. Users type conversational requests and the agent builds the dashboard step by step, allowing iterative refinement.
562
+
563
+ Your job is to write a sequence of natural language prompts that a user would type into Spotter Viz to build a liveboard for this demo scenario. The prompts should be conversational, specific, and progressively build the liveboard from scratch.
564
+
565
+ {context}
566
+
567
+ ---
568
+
569
+ Write a Spotter Viz story as a numbered sequence of prompts. Format each step as:
570
+
571
+ ### Step N: [Brief label]
572
+ > "[The exact prompt to type into Spotter Viz]"
573
+
574
+ **Expected result:** [1 sentence describing what Spotter Viz should create]
575
+
576
+ Rules:
577
+ - Step 1 should set context: company name, data source, and overall goal
578
+ - Steps 2-3 should add KPIs with sparklines (mention time granularity)
579
+ - Steps 4-6 should add key visualizations (charts, breakdowns, comparisons)
580
+ - Steps 7-8 should refine: add styling, rename the liveboard, organize into groups/tabs
581
+ - Final step should call out the "aha moment" — the key data story or outlier to highlight
582
+ - Use the company name and real column/metric names from the context above
583
+ - Keep prompts natural — write how a business user would actually talk
584
+ - Include 6-10 steps total""",
585
  }
586
 
587
  DEFAULT_TEMPLATE = """You are helping create a {vertical} {function} demo.
sprint_2026_02.md CHANGED
@@ -88,15 +88,32 @@
88
 
89
  ### Done
90
 
91
-
92
- #### Feb 6, 2026 - Spotter Viz Method Added (Design Doc Implementation)
93
- - [x] **Settings dropdown updated**: `["HYBRID", "SPOTTER_VIZ"]` replaces old 3-option dropdown
94
- - [x] **SPOTTER_VIZ routing in deployer**: Direct TML path with outliers + model_columns
95
- - [x] **Backward compatibility**: old "TML"/"MCP" values mapped to "HYBRID" ✅
96
- - [x] **Shared post-processing**: Both methods use `enhance_mcp_liveboard()`
97
- - [x] **create_liveboard_from_model() extended**: accepts `outliers` and `model_columns` params
98
- - [x] **CLAUDE.md updated**: Two-method system documented ✅
99
- - [ ] **To test**: Run SPOTTER_VIZ end-to-end with Retail Sales
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
 
101
 
102
  #### Feb 6, 2026 - Gradio Compat + Hybrid Liveboard Layout Fix
 
88
 
89
  ### Done
90
 
91
+ #### Feb 6, 2026 - Spotter Viz Story Tab (Post-Liveboard Output)
92
+ - [x] **SPOTTER_VIZ method removed** HYBRID is the only liveboard creation method ✅
93
+ - [x] **Spotter Viz Story generator** `_generate_spotter_viz_story()` in chat_interface.py
94
+ - Uses `build_prompt(stage="spotter_viz_story")` + LLM call
95
+ - Fallback: `_build_fallback_spotter_story()` builds template without LLM
96
+ - Takes company name, use case, model name, liveboard name
97
+ - [x] **New "🎬 Spotter Viz Story" tab** in Gradio app
98
+ - Displays after liveboard creation
99
+ - Conversational sequence of NL prompts for Spotter Viz agent
100
+ - Can be manually entered into ThoughtSpot Spotter Viz
101
+ - [x] **Wired into pipeline** — called after demo pack generation in deploy flow ✅
102
+ - [x] **CLAUDE.md updated** — documents Spotter Viz Story as post-liveboard output ✅
103
+ - [ ] **To test**: Run end-to-end and verify story generation
104
+
105
+ #### Feb 6, 2026 - Spotter Viz API Investigation
106
+ - [x] **Tested ThoughtSpot AI endpoints on sebe staging cluster** ✅
107
+ - `ai/conversation/create` + `ai/conversation/{id}/converse` — WORKS (Spotter NL → search tokens)
108
+ - `ai/answer/create` — WORKS (single-shot NL → TS search tokens)
109
+ - `ai/agent/conversation/create` — schema unknown (ContextPayloadV2Input enum not discoverable)
110
+ - Documented in `dev_notes/SPOTTER_VIZ_API_TEST_RESULTS.md`
111
+ - [x] **Decision**: Spotter Viz API not ready for liveboard creation — use story output instead ✅
112
+
113
+ #### Feb 6, 2026 - Spotter Viz Method Added (REVERTED)
114
+ - ~~SPOTTER_VIZ routing in deployer~~ — Removed (was just TML pipeline renamed)
115
+ - ~~Settings dropdown `["HYBRID", "SPOTTER_VIZ"]`~~ — Simplified to `["HYBRID"]`
116
+ - [x] **Backward compatibility preserved**: old "TML"/"MCP" values still map to "HYBRID" ✅
117
 
118
 
119
  #### Feb 6, 2026 - Gradio Compat + Hybrid Liveboard Layout Fix
thoughtspot_deployer.py CHANGED
@@ -2123,14 +2123,10 @@ class ThoughtSpotDeployer:
2123
  use_mcp = os.getenv('USE_MCP_LIVEBOARD', 'false').lower() == 'true'
2124
  method = 'MCP' if use_mcp else 'HYBRID'
2125
 
2126
- # Normalize method name and backward compatibility
2127
- method = method.upper()
2128
- # Map old values to current options
2129
- if method in ('TML', 'MCP'):
2130
- log_progress(f"[INFO] Mapping legacy method '{method}' → HYBRID")
2131
- method = 'HYBRID'
2132
- if method not in ['HYBRID', 'SPOTTER_VIZ']:
2133
- log_progress(f"[WARN] Unknown liveboard method '{method}', defaulting to HYBRID")
2134
  method = 'HYBRID'
2135
 
2136
  log_progress(f"Creating liveboard ({method} method)...")
@@ -2144,95 +2140,9 @@ class ThoughtSpotDeployer:
2144
  'use_case': use_case or 'General Analytics'
2145
  }
2146
 
2147
- if method == 'SPOTTER_VIZ':
2148
- # Spotter Viz: Direct REST API + TML — no MCP dependency
2149
- from liveboard_creator import create_liveboard_from_model, enhance_mcp_liveboard
2150
-
2151
- # Get actual column names from ThoughtSpot model
2152
- model_columns = self.get_model_columns(model_guid)
2153
- if not model_columns:
2154
- log_progress(f" ⚠️ Could not get model columns, falling back to DDL")
2155
- model_columns = []
2156
- for table_name, columns_list in tables.items():
2157
- for col in columns_list:
2158
- model_columns.append(col)
2159
-
2160
- # Get outlier patterns from the vertical×function system
2161
- outlier_dicts = []
2162
- try:
2163
- from outlier_system import get_outliers_for_use_case
2164
- from demo_personas import parse_use_case
2165
- uc_vertical, uc_function = parse_use_case(use_case or '')
2166
- if uc_vertical or uc_function:
2167
- outlier_config = get_outliers_for_use_case(
2168
- uc_vertical or "Generic",
2169
- uc_function or "Generic"
2170
- )
2171
- for op in outlier_config.required:
2172
- outlier_dicts.append({
2173
- 'title': op.name,
2174
- 'insight': op.viz_talking_point,
2175
- 'viz_type': op.viz_type,
2176
- 'show_me_query': op.viz_question,
2177
- 'kpi_companion': True,
2178
- 'spotter_questions': op.spotter_questions,
2179
- })
2180
- for op in outlier_config.optional[:2]:
2181
- outlier_dicts.append({
2182
- 'title': op.name,
2183
- 'insight': op.viz_talking_point,
2184
- 'viz_type': op.viz_type,
2185
- 'show_me_query': op.viz_question,
2186
- 'kpi_companion': False,
2187
- 'spotter_questions': op.spotter_questions,
2188
- })
2189
- if outlier_dicts:
2190
- log_progress(f" [SPOTTER] Using {len(outlier_dicts)} outlier patterns from {uc_vertical}×{uc_function}")
2191
- except Exception as outlier_err:
2192
- log_progress(f" [SPOTTER] Outlier loading skipped: {outlier_err}")
2193
-
2194
- log_progress(f" [SPOTTER] Model: {model_name}, GUID: {model_guid}")
2195
- log_progress(f" [SPOTTER] Using {len(model_columns)} columns from ThoughtSpot model")
2196
- log_progress(f" Step 1/2: Building liveboard via TML...")
2197
-
2198
- try:
2199
- liveboard_result = create_liveboard_from_model(
2200
- ts_client=self,
2201
- model_id=model_guid,
2202
- model_name=model_name,
2203
- company_data=company_data,
2204
- use_case=use_case or 'General Analytics',
2205
- num_visualizations=8,
2206
- liveboard_name=liveboard_name,
2207
- llm_model=llm_model,
2208
- outliers=outlier_dicts if outlier_dicts else None,
2209
- model_columns=model_columns
2210
- )
2211
- except Exception as spotter_error:
2212
- import traceback
2213
- error_trace = traceback.format_exc()
2214
- log_progress(f" [SPOTTER ERROR] {type(spotter_error).__name__}: {str(spotter_error)}")
2215
- liveboard_result = {'success': False, 'error': str(spotter_error), 'traceback': error_trace}
2216
-
2217
- # Spotter Viz: Add TML enhancement (same as Hybrid post-processing)
2218
- if liveboard_result.get('success') and liveboard_result.get('liveboard_guid'):
2219
- log_progress(f" Step 2/2: Enhancing with TML post-processing...")
2220
- enhance_result = enhance_mcp_liveboard(
2221
- liveboard_guid=liveboard_result['liveboard_guid'],
2222
- company_data=company_data,
2223
- ts_client=self,
2224
- add_groups=True,
2225
- fix_kpis=True,
2226
- apply_brand_colors=True
2227
- )
2228
- if enhance_result.get('success'):
2229
- log_progress(f" [OK] Enhancement applied: {', '.join(enhance_result.get('enhancements', []))}")
2230
- else:
2231
- log_progress(f" [WARN] Enhancement partial: {enhance_result.get('message', '')[:80]}")
2232
-
2233
- elif method == 'HYBRID':
2234
- # MCP and HYBRID both use MCP for creation
2235
- # HYBRID adds TML post-processing enhancement
2236
  from liveboard_creator import create_liveboard_from_model_mcp, enhance_mcp_liveboard
2237
 
2238
  # Get actual column names from ThoughtSpot model (not DDL)
 
2123
  use_mcp = os.getenv('USE_MCP_LIVEBOARD', 'false').lower() == 'true'
2124
  method = 'MCP' if use_mcp else 'HYBRID'
2125
 
2126
+ # Normalize method HYBRID is the only supported method
2127
+ method = method.upper() if method else 'HYBRID'
2128
+ if method != 'HYBRID':
2129
+ log_progress(f"[INFO] Mapping method '{method}' → HYBRID (only supported method)")
 
 
 
 
2130
  method = 'HYBRID'
2131
 
2132
  log_progress(f"Creating liveboard ({method} method)...")
 
2140
  'use_case': use_case or 'General Analytics'
2141
  }
2142
 
2143
+ if method == 'HYBRID':
2144
+ # HYBRID: MCP creates liveboard, TML post-processes
2145
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2146
  from liveboard_creator import create_liveboard_from_model_mcp, enhance_mcp_liveboard
2147
 
2148
  # Get actual column names from ThoughtSpot model (not DDL)