File size: 14,497 Bytes
4afc8ce
 
 
a3af4f1
4afc8ce
 
9d5041f
 
4afc8ce
 
 
 
 
 
 
9d5041f
4afc8ce
 
 
9d5041f
4afc8ce
 
9d5041f
4afc8ce
 
9d5041f
4afc8ce
 
 
 
 
 
 
 
9d5041f
4afc8ce
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9d5041f
 
4afc8ce
9d5041f
4afc8ce
 
 
9d5041f
4afc8ce
 
 
9d5041f
4afc8ce
 
 
 
 
 
9d5041f
4afc8ce
 
 
9d5041f
4afc8ce
 
 
9d5041f
4afc8ce
 
 
9d5041f
4afc8ce
 
 
 
9d5041f
4afc8ce
 
 
 
 
 
9d5041f
4afc8ce
9d5041f
4afc8ce
9d5041f
4afc8ce
 
 
 
 
9d5041f
 
4afc8ce
d61bc93
4afc8ce
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
09f5bfb
4afc8ce
 
 
 
 
 
 
 
6d11ec6
 
 
 
 
 
 
 
4afc8ce
 
 
 
892a464
 
 
 
4afc8ce
892a464
 
 
 
4afc8ce
892a464
 
 
 
4afc8ce
892a464
 
 
 
 
 
 
 
 
4afc8ce
 
 
 
 
d61bc93
4afc8ce
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9d5041f
4afc8ce
 
 
 
 
 
 
 
 
9d5041f
4afc8ce
 
 
 
 
d61bc93
 
4afc8ce
9d5041f
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
"""Gradio UI for Context Engineering Visualizer"""

import gradio as gr
from typing import List, Tuple

from .agent import ContextEngineeringAgent
from config.settings import Settings
from config import logger_ui, logger_app


class ContextVisualizerUI:
    """Gradio interface for the Context Engineering Visualizer"""
    
    def __init__(self):
        self.agent = None
        logger_ui.info("ContextVisualizerUI initialized")
        
    def initialize_agent(self) -> str:
        """Initialize the agent"""
        logger_ui.info("Initializing agent from UI")
        try:
            self.agent = ContextEngineeringAgent()
            logger_ui.info("Agent initialized successfully from UI")
            return "Agent initialized successfully"
        except Exception as e:
            logger_ui.error(f"Error initializing agent: {str(e)}")
            return f"Error initializing agent: {str(e)}"
    
    def format_context_layers(self, visualizer) -> str:
        """Format context layers for display as stacked container visualization"""
        if not visualizer.context_layers:
            return "<div style='text-align: center; padding: 20px;'>No context layers available</div>"
        
        total_tokens = sum(visualizer.token_counts.values())
        logger_ui.debug(f"Formatting context layers: {len(visualizer.context_layers)} layers, {total_tokens} total tokens")
        
        # Color palette for different layers
        colors = [
            "#4A90E2",  # Blue - System Instructions
            "#7B68EE",  # Purple - Conversation History
            "#50C878",  # Green - Retrieved Knowledge
            "#F39C12",  # Orange - User Query
            "#E74C3C"   # Red - Available Tools
        ]
        
        # Build HTML for stacked container
        html = f"""
        <div style="max-width: 800px; margin: 0 auto; font-family: 'Inter', sans-serif;">
            <div style="text-align: center; margin-bottom: 20px;">
                <h3 style="margin: 0; color: #2c3e50;">Context Window Structure</h3>
                <p style="margin: 5px 0; color: #7f8c8d; font-size: 14px;">Total: {total_tokens} tokens</p>
            </div>
            
            <div style="border: 2px solid #34495e; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 6px rgba(0,0,0,0.1);">
        """
        
        for i, layer in enumerate(visualizer.context_layers):
            percentage = (layer["tokens"] / total_tokens * 100) if total_tokens > 0 else 0
            color = colors[i % len(colors)]
            
            # Create stacked section
            html += f"""
                <div style="background: linear-gradient(135deg, {color} 0%, {color}dd 100%); 
                            padding: 15px 20px; 
                            border-bottom: 1px solid rgba(255,255,255,0.1);
                            position: relative;
                            height: {max(percentage * 3, 30)}px;
                            display: flex;
                            align-items: center;
                            transition: all 0.3s ease;">
                    <div style="flex: 1;">
                        <div style="color: white; font-weight: 600; font-size: 14px; margin-bottom: 3px;">
                            {layer['layer'].upper()}
                        </div>
                        <div style="color: rgba(255,255,255,0.9); font-size: 12px;">
                            {layer['tokens']} tokens ({percentage:.1f}%)
                        </div>
                    </div>
                    <div style="color: rgba(255,255,255,0.8); font-size: 24px; font-weight: bold;">
                        {percentage:.0f}%
                    </div>
                </div>
            """
        
        html += """
            </div>
        </div>
        """
        
        return html
    
    def format_context_details(self, visualizer) -> str:
        """Format detailed context layer contents for markdown display"""
        if not visualizer.context_layers:
            return "No context layers available"
        
        output = []
        total_tokens = sum(visualizer.token_counts.values())
        
        for i, layer in enumerate(visualizer.context_layers, 1):
            percentage = (layer["tokens"] / total_tokens * 100) if total_tokens > 0 else 0
            
            output.append(f"### {i}. {layer['layer'].upper()}")
            output.append(f"**Tokens:** {layer['tokens']} ({percentage:.1f}%)")
            output.append(f"\n**Content:**")
            output.append(f"```\n{layer['content']}\n```")
            output.append("")
        
        return "\n".join(output)
    
    def process_query(
        self, 
        query: str,
        history: List,
        show_visualization: bool
    ) -> Tuple[List, str, str, str]:
        """Process user query and return results"""
        
        logger_ui.info(f"Processing query from UI: {query[:50]}..." if len(query) > 50 else f"Processing query from UI: {query}")
        
        if not self.agent:
            logger_ui.info("Agent not initialized, initializing now")
            self.initialize_agent()
        
        if not query.strip():
            logger_ui.warning("Empty query received, skipping")
            return history, "", "", ""
        
        try:
            logger_ui.info("Delegating query processing to agent")
            # Process query
            response, visualizer = self.agent.process_query(query)
            
            # Add to chat history (format: list of dicts with role and content)
            history.append({"role": "user", "content": query})
            history.append({"role": "assistant", "content": response})
            logger_ui.info(f"Added exchange to chat history. Total messages: {len(history)}")
            
            # Format outputs
            if show_visualization:
                logger_ui.debug("Formatting context visualization")
                context_viz_html = self.format_context_layers(visualizer)
                context_details = self.format_context_details(visualizer)
            else:
                logger_ui.debug("Visualization disabled by user")
                context_viz_html = "<div style='text-align: center; padding: 20px; color: #7f8c8d;'>Visualization disabled</div>"
                context_details = "Visualization disabled"
            
            logger_ui.info("Query processed successfully")
            return history, "", context_viz_html, context_details
            
        except Exception as e:
            error_msg = f"Error processing query: {str(e)}"
            logger_ui.error(error_msg)
            history.append({"role": "user", "content": query})
            history.append({"role": "assistant", "content": error_msg})
            return history, "", "", ""
    
    def clear_conversation(self) -> Tuple[List, str, str]:
        """Clear conversation history"""
        logger_ui.info("Clearing conversation history")
        if self.agent:
            previous_count = len(self.agent.memory.messages)
            self.agent.memory.messages = []
            logger_ui.info(f"Cleared {previous_count} messages from conversation memory")
        return [], "", ""
    
    def create_interface(self) -> gr.Blocks:
        """Create the Gradio interface"""
        
        logger_ui.info("Creating Gradio interface")
        
        with gr.Blocks(
            title="Context Engineering Visualizer"
        ) as interface:
            
            gr.Markdown("""
            # Context Engineering Visualizer
            
            This tool demonstrates how information flows into an AI agent's context window before inference.
            Ask questions about business metrics and data analysis to see the context engineering in action.
            """)
            
            with gr.Accordion("About Context Engineering", open=False):
                gr.Markdown("""
                **Context Engineering** is the practice of carefully managing what information goes into an AI model's context window.
                
                This visualizer shows five key layers:
                
                1. **System Instructions**: Stable guidelines that define the agent's role and behavior
                2. **Conversation History**: Recent messages to maintain conversational coherence
                3. **Retrieved Knowledge (RAG)**: Relevant information retrieved from a knowledge base
                4. **User Query**: The current question or request
                5. **Available Tools**: External functions the agent can use
                
                Each layer contributes tokens to the context window. Good context engineering ensures:
                - **Relevance**: Only necessary information is included
                - **Structure**: Clear separation and organization of context layers
                - **Efficiency**: Optimal use of limited context window space
                - **Consistency**: Stable system instructions across interactions
                """)
            
            with gr.Sidebar(label="Settings & Examples", open=True, width=320):
                gr.Markdown("### Settings")
                
                show_viz = gr.Checkbox(
                    label="Show Context Visualization",
                    value=True,
                    info="Display detailed breakdown of context layers"
                )
                
                gr.Markdown("""
                ### Knowledge Base Context
                
                The examples below use a synthetic data of a company internal document: **Product Strategy & Decision Handbook** from Atlas Pay. 
                
                📄 [View the source document on Hugging Face](https://huggingface.co/spaces/mcikalmerdeka/context-engineering-visualizer/blob/main/data/Product%20Strategy%20%26%20Decision%20Handbook%20%E2%80%94%20Atlas%20Pay.pdf)
                """)
                
                gr.Markdown("### Example Questions")
                gr.Markdown("""
                **Try these sequential scenarios to see context engineering in action:**
                
                **Scenario 1: Understanding STAM (North Star Metric)**
                1. What is STAM and why is it our North Star metric?
                2. Calculate STAM if we have 125,000 successful transactions and 500 active merchants
                3. What does this STAM value tell us about merchant engagement?
                
                **Scenario 2: Net Revenue Retention Analysis**
                1. What is Net Revenue Retention (NRR) and why is it important?
                2. Calculate NRR if we have $2.5M retained revenue from $2M starting revenue
                3. Is this NRR performance good based on our product goals?
                
                **Scenario 3: Payment Success Rate Monitoring**
                1. What is Adjusted Payment Success Rate and how is it used?
                2. Calculate the payment success rate with 48,500 successful payments out of 50,000 valid attempts
                3. Does this meet our platform reliability standards?
                
                **Scenario 4: Product Strategy & Decision Making**
                1. What are AtlasPay's core product principles?
                2. Why did we decide to build our fraud detection in-house instead of buying a vendor solution?
                3. What were the trade-offs in that decision?
                
                **Scenario 5: Feature Prioritization**
                1. How does AtlasPay prioritize features?
                2. What are our strategic goals for 2025-2027?
                3. Should we prioritize a feature with Customer Impact=5, Revenue Impact=4, Strategic Alignment=5, Engineering Effort=3?
                """)
            
            chatbot = gr.Chatbot(
                label="Conversation",
                height=500,
                avatar_images=(None, None)
            )
            
            query_input = gr.Textbox(
                label="Your Question",
                placeholder="e.g., What is Average Order Value and how is it calculated?",
                lines=2,
                show_label=False
            )
            
            with gr.Row():
                submit_btn = gr.Button("Submit", variant="primary")
                clear_btn = gr.Button("Clear Conversation")
            
            with gr.Accordion("Context Window Breakdown", open=True):
                context_viz = gr.HTML(
                    value="<div style='text-align: center; padding: 20px; color: #7f8c8d;'>Submit a query to see context breakdown</div>"
                )
                
                with gr.Accordion("Detailed Layer Contents", open=False):
                    context_details = gr.Markdown(
                        value="Submit a query to see detailed breakdown"
                    )
            
            # Event handlers
            submit_btn.click(
                fn=self.process_query,
                inputs=[query_input, chatbot, show_viz],
                outputs=[chatbot, query_input, context_viz, context_details]
            )
            
            query_input.submit(
                fn=self.process_query,
                inputs=[query_input, chatbot, show_viz],
                outputs=[chatbot, query_input, context_viz, context_details]
            )
            
            clear_btn.click(
                fn=self.clear_conversation,
                inputs=[],
                outputs=[chatbot, context_viz, context_details]
            )
            
            gr.Markdown("""
            ---
            **Note**: This visualizer uses OpenAI's GPT model and requires an API key in your environment.
            """)
        
        logger_ui.info("Gradio interface created successfully")
        return interface


def launch_ui(
    share: bool = Settings.GRADIO_SHARE,
    server_name: str = Settings.GRADIO_SERVER_NAME,
    server_port: int = Settings.GRADIO_SERVER_PORT
):
    """Launch the Gradio interface"""
    logger_app.info(f"Launching UI with settings: share={share}, server_name={server_name}, server_port={server_port}")
    ui = ContextVisualizerUI()
    interface = ui.create_interface()
    interface.launch(
        share=share,
        server_name=server_name,
        server_port=server_port,
        theme=gr.themes.Soft()
    )
    logger_app.info("UI launched successfully")