#!/usr/bin/env python3
"""
Demonstrate Variation Generation with Google Chat webhook.
Shows:
1. DAG-based structural variations
2. Parameter variations
3. Caching of variation families
4. Sending sample variations to webhook
"""
import json
import os
import sys
import time
import httpx
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
WEBHOOK_URL = os.environ.get(
"TEST_CHAT_WEBHOOK",
"https://chat.googleapis.com/v1/spaces/AAQAKl_yP9Y/messages?key=AIzaSyDdI0hCZtE6vySjMm-WEfRq3CPzqKqqsHI&token=Ie8-brhWHA9kE_2JiqKRDhqjadPHK4RNe15UcWwLXDA",
)
def snake_to_camel(s: str) -> str:
components = s.split("_")
return components[0] + "".join(x.title() for x in components[1:])
def convert_keys_to_camel(obj):
if isinstance(obj, dict):
return {snake_to_camel(k): convert_keys_to_camel(v) for k, v in obj.items()}
elif isinstance(obj, list):
return [convert_keys_to_camel(item) for item in obj]
return obj
def send_card(payload: dict, description: str = "") -> bool:
"""Send a card to the webhook."""
print(f"\n{'='*60}")
print(f"SENDING: {description}")
print(f"{'='*60}")
try:
response = httpx.post(
WEBHOOK_URL,
json=payload,
headers={"Content-Type": "application/json"},
timeout=30.0,
)
if response.status_code == 200:
print(f"✅ SUCCESS")
return True
else:
print(f"❌ FAILED ({response.status_code})")
print(f"Error: {response.text[:300]}")
return False
except Exception as e:
print(f"❌ ERROR: {e}")
return False
def demo_variation_generation():
"""Demonstrate variation generation from a base pattern."""
print("\n" + "=" * 60)
print("VARIATION GENERATION DEMO")
print("=" * 60)
from adapters.module_wrapper.variation_generator import (
VariationGenerator,
get_variation_generator,
)
from gchat.card_framework_wrapper import get_card_framework_wrapper
wrapper = get_card_framework_wrapper()
generator = get_variation_generator()
# Base pattern
base_pattern = {
"card_id": "demo_base_pattern",
"component_paths": ["Section", "DecoratedText", "ButtonList", "Button"],
"instance_params": {
"text": "Server Status",
"top_label": "Status",
"buttons": [
{"text": "Refresh", "url": "https://example.com/refresh"},
{"text": "Details", "url": "https://example.com/details"},
],
},
"card_description": "A status card with action buttons",
}
print(f"\n1️⃣ Base pattern:")
print(f" Components: {base_pattern['component_paths']}")
print(f" Params: {list(base_pattern['instance_params'].keys())}")
# Generate variations
print(f"\n2️⃣ Generating variations...")
family = generator.generate_variations(
pattern=base_pattern,
num_structure_variations=3,
num_param_variations=2,
)
print(f" Generated {family.size} variations")
print(f" Parent key: {family.parent_key}")
# Show variations by type
print(f"\n3️⃣ Variations by type:")
type_counts = {}
for v in family.variations:
type_counts[v.variation_type] = type_counts.get(v.variation_type, 0) + 1
if v.variation_type != "original":
print(f" [{v.variation_type}] {v.cache_key}")
print(f" Components: {v.component_paths}")
print(f"\n Type counts: {type_counts}")
# Cache stats
from adapters.module_wrapper.component_cache import get_component_cache
cache = get_component_cache()
print(f"\n4️⃣ Cache stats after variation generation:")
print(f" {cache}")
return family
def demo_feedbackloop_variations():
"""Demonstrate variation generation via FeedbackLoop."""
print("\n" + "=" * 60)
print("FEEDBACKLOOP VARIATION GENERATION")
print("=" * 60)
from gchat.feedback_loop import get_feedback_loop
fl = get_feedback_loop()
# Store a pattern with variations
print("\n1️⃣ Storing pattern with variation generation...")
point_id = fl.store_instance_pattern(
card_description="Dashboard status card showing system health",
component_paths=["Section", "DecoratedText", "DecoratedText", "ButtonList"],
instance_params={
"title": "System Health",
"items": [
{"text": "CPU: 45%", "top_label": "CPU"},
{"text": "Memory: 67%", "top_label": "Memory"},
],
"buttons": [{"text": "View Details"}],
},
content_feedback="positive",
form_feedback="positive",
card_id="fl_variation_demo",
structure_description="Dashboard with multiple status items",
generate_variations=True,
num_structure_variations=3,
num_param_variations=2,
)
print(f" Stored pattern: {point_id[:8] if point_id else 'FAILED'}...")
# Get a random variation
print("\n2️⃣ Retrieving random variation...")
variation = fl.get_cached_variation("fl_variation_demo")
if variation:
print(f" Key: {variation['key']}")
print(f" Components: {variation['component_paths']}")
print(f" From cache: {variation.get('_from_cache', False)}")
else:
print(" No variation found")
# Get a structure variation specifically
print("\n3️⃣ Retrieving structure variation...")
struct_var = fl.get_cached_variation("fl_variation_demo", "structure")
if struct_var:
print(f" Key: {struct_var['key']}")
print(f" Components: {struct_var['component_paths']}")
else:
print(" No structure variation found")
return point_id
def build_variation_showcase_card(family):
"""Build a card showing the variation family."""
from card_framework.v2.card import CardHeader, CardWithId
from card_framework.v2.message import Message
from card_framework.v2.widgets.text_paragraph import TextParagraph
from gchat.card_framework_wrapper import get_card_framework_wrapper
wrapper = get_card_framework_wrapper()
# Get cached classes
Section = wrapper.get_cached_class("Section")
DecoratedText = wrapper.get_cached_class("DecoratedText")
# Build sections for each variation type
sections = []
# Header section
sections.append(
Section(
header="Overview",
widgets=[
TextParagraph(
text=f"<b>Parent Key:</b> {family.parent_key}\n"
f"<b>Total Variations:</b> {family.size}"
),
],
)
)
# Group by type
by_type = {}
for v in family.variations:
by_type.setdefault(v.variation_type, []).append(v)
for vtype, variations in by_type.items():
widgets = []
for i, v in enumerate(variations[:3]): # Show max 3 per type
components_str = " → ".join(v.component_paths[:4])
if len(v.component_paths) > 4:
components_str += "..."
widgets.append(
DecoratedText(
text=f"<code>{components_str}</code>",
top_label=f"v{i}: {v.cache_key.split(':')[-1]}",
)
)
if widgets:
emoji = {"original": "🎯", "structure": "🏗️", "parameter": "⚙️"}.get(
vtype, "📦"
)
sections.append(
Section(
header=f"{emoji} {vtype.title()} ({len(variations)})",
widgets=widgets,
)
)
card = CardWithId(
header=CardHeader(
title="🔀 Variation Family",
subtitle=f"{family.size} variations generated",
),
sections=sections,
_CardWithId__card_id="variation-showcase",
)
message = Message(cards_v2=[card])
rendered = message.render()
return convert_keys_to_camel(rendered)
def build_sample_variation_cards(family, max_cards: int = 2):
"""Build cards from actual variations to show they're valid."""
from card_framework.v2.card import CardHeader, CardWithId
from card_framework.v2.message import Message
from card_framework.v2.widgets.on_click import OnClick, OpenLink
from card_framework.v2.widgets.text_paragraph import TextParagraph
from gchat.card_framework_wrapper import get_card_framework_wrapper
wrapper = get_card_framework_wrapper()
cards = []
# Get one structure variation and one parameter variation
variations_to_show = []
for vtype in ["structure", "parameter"]:
for v in family.variations:
if v.variation_type == vtype:
variations_to_show.append(v)
break
for v in variations_to_show[:max_cards]:
# Get cached classes for this variation's components
Section = wrapper.get_cached_class("Section")
DecoratedText = wrapper.get_cached_class("DecoratedText")
ButtonList = wrapper.get_cached_class("ButtonList")
Button = wrapper.get_cached_class("Button")
# Build widgets based on variation params
params = v.instance_params
widgets = []
# Status widget
text = params.get("text", "Sample Status")
label = params.get("top_label", "Status")
widgets.append(
DecoratedText(
text=f"<b>{text}</b>",
top_label=label,
)
)
# DSL notation
if v.dsl_notation:
widgets.append(TextParagraph(text=f"<i>DSL: {v.dsl_notation[:60]}...</i>"))
# Add button if ButtonList in components
if "ButtonList" in v.component_paths or "Button" in v.component_paths:
buttons_data = params.get("buttons", [{"text": "Action"}])
buttons = []
for btn in buttons_data[:2]:
btn_text = (
btn.get("text", "Click") if isinstance(btn, dict) else str(btn)
)
buttons.append(
Button(
text=btn_text,
on_click=OnClick(open_link=OpenLink(url="https://example.com")),
)
)
if buttons:
widgets.append(ButtonList(buttons=buttons))
# Build card
emoji = "🏗️" if v.variation_type == "structure" else "⚙️"
card = CardWithId(
header=CardHeader(
title=f"{emoji} {v.variation_type.title()} Variation",
subtitle=f"Cache key: {v.cache_key}",
),
sections=[Section(widgets=widgets)],
_CardWithId__card_id=f"variation-{v.variation_id}",
)
message = Message(cards_v2=[card])
rendered = message.render()
cards.append(convert_keys_to_camel(rendered))
return cards
def demo_summary_card(family, generator_stats):
"""Build a summary card for the variation system."""
from card_framework.v2.card import CardHeader, CardWithId
from card_framework.v2.message import Message
from card_framework.v2.widgets.text_paragraph import TextParagraph
from gchat.card_framework_wrapper import get_card_framework_wrapper
wrapper = get_card_framework_wrapper()
Section = wrapper.get_cached_class("Section")
DecoratedText = wrapper.get_cached_class("DecoratedText")
from adapters.module_wrapper.component_cache import get_component_cache
cache = get_component_cache()
cache_stats = cache.stats
card = CardWithId(
header=CardHeader(
title="🎯 Variation System Summary",
subtitle="DAG-based pattern expansion",
),
sections=[
Section(
header="📊 Generator Stats",
widgets=[
DecoratedText(
text=f"<b>{generator_stats['num_families']}</b>",
top_label="Pattern Families",
),
DecoratedText(
text=f"<b>{generator_stats['total_variations']}</b>",
top_label="Total Variations",
),
DecoratedText(
text=f"<b>{generator_stats['avg_variations_per_family']:.1f}</b>",
top_label="Avg per Family",
),
],
),
Section(
header="💾 Cache Stats",
widgets=[
DecoratedText(
text=f"<b>{cache_stats['l1_size']}</b> items",
top_label="L1 (Memory)",
),
DecoratedText(
text=f"<b>{cache_stats['hit_rate']:.1%}</b>",
top_label="Hit Rate",
),
],
),
Section(
header="🏗️ Architecture",
widgets=[
TextParagraph(
text="<b>Structural Variations:</b> Swap siblings, add/remove widgets\n"
"<b>Parameter Variations:</b> Text, color, list modifications\n"
"<b>Caching:</b> All variations pre-cached for instant retrieval"
),
],
),
],
_CardWithId__card_id="variation-summary",
)
message = Message(cards_v2=[card])
rendered = message.render()
return convert_keys_to_camel(rendered)
if __name__ == "__main__":
print("=" * 60)
print("VARIATION GENERATION WEBHOOK DEMO")
print("=" * 60)
print(f"Webhook: {WEBHOOK_URL[:50]}...")
results = []
# 1. Generate variations
family = demo_variation_generation()
# 2. FeedbackLoop integration
demo_feedbackloop_variations()
# 3. Send showcase card
payload1 = build_variation_showcase_card(family)
result = send_card(payload1, "Variation Family Overview")
results.append(("Family Overview", result))
time.sleep(1)
# 4. Send sample variation cards
sample_cards = build_sample_variation_cards(family, max_cards=2)
for i, payload in enumerate(sample_cards):
result = send_card(payload, f"Sample Variation {i + 1}")
results.append((f"Sample Variation {i + 1}", result))
time.sleep(1)
# 5. Send summary card
from adapters.module_wrapper.variation_generator import get_variation_generator
generator = get_variation_generator()
payload3 = demo_summary_card(family, generator.stats)
result = send_card(payload3, "Variation System Summary")
results.append(("Summary Card", result))
# Final summary
print("\n" + "=" * 60)
print("DEMO SUMMARY")
print("=" * 60)
for name, success in results:
status = "✅" if success else "❌"
print(f" {status} {name}")
print(f"\nTotal: {sum(1 for _, r in results if r)}/{len(results)} cards sent")