Spaces:
Running
Running
feat: replace Spotter Viz method with Spotter Viz Story tab output
Browse filesRemove 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>
- .gitignore +4 -2
- CLAUDE.md +12 -23
- chat_interface.py +167 -5
- liveboard_creator.py +1 -1
- prompts.py +27 -0
- sprint_2026_02.md +26 -9
- thoughtspot_deployer.py +7 -97
.gitignore
CHANGED
|
@@ -221,5 +221,7 @@ scratch/
|
|
| 221 |
|
| 222 |
# Sprint documents (local working docs)
|
| 223 |
|
| 224 |
-
# LegitData project
|
| 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
|
| 206 |
|
| 207 |
-
**
|
| 208 |
-
**Two options:** HYBRID (default) and SPOTTER_VIZ
|
| 209 |
|
| 210 |
-
|
| 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.
|
| 218 |
- **Entry:** `create_liveboard_from_model_mcp()` → `enhance_mcp_liveboard()`
|
| 219 |
|
| 220 |
-
###
|
| 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 |
-
###
|
| 238 |
-
|
| 239 |
-
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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"
|
| 4137 |
value="HYBRID",
|
| 4138 |
-
info="HYBRID: MCP + TML
|
| 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
|
| 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 |
-
|
| 93 |
-
- [x] **
|
| 94 |
-
-
|
| 95 |
-
-
|
| 96 |
-
-
|
| 97 |
-
- [x] **
|
| 98 |
-
-
|
| 99 |
-
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 2127 |
-
method = method.upper()
|
| 2128 |
-
|
| 2129 |
-
|
| 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 == '
|
| 2148 |
-
#
|
| 2149 |
-
|
| 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)
|