mcikalmerdeka commited on
Commit
4afc8ce
·
1 Parent(s): 4a506b2

refactor Context Engineering Visualizer into modular components with Gradio UI integration, enhancing structure and maintainability.

Browse files
Files changed (10) hide show
  1. app/__init__.py +16 -0
  2. app/agent.py +111 -0
  3. app/knowledge.py +31 -0
  4. app/memory.py +42 -0
  5. app/tools.py +46 -0
  6. app/ui.py +289 -0
  7. app/visualizer.py +34 -0
  8. config/__init__.py +5 -0
  9. config/settings.py +38 -0
  10. main.py +3 -428
app/__init__.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Context Engineering Visualizer Application"""
2
+
3
+ from .agent import ContextEngineeringAgent
4
+ from .visualizer import ContextVisualizer
5
+ from .memory import ConversationMemory
6
+ from .knowledge import KnowledgeBase
7
+ from .tools import calculate_metric, get_current_time
8
+
9
+ __all__ = [
10
+ "ContextEngineeringAgent",
11
+ "ContextVisualizer",
12
+ "ConversationMemory",
13
+ "KnowledgeBase",
14
+ "calculate_metric",
15
+ "get_current_time",
16
+ ]
app/agent.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Main agent implementation"""
2
+
3
+ from langchain_openai import ChatOpenAI
4
+ from langchain.agents import create_agent
5
+
6
+ from .visualizer import ContextVisualizer
7
+ from .memory import ConversationMemory
8
+ from .knowledge import KnowledgeBase
9
+ from .tools import calculate_metric, get_current_time
10
+ from config import Settings
11
+
12
+
13
+ class ContextEngineeringAgent:
14
+ """
15
+ Agent that demonstrates context engineering principles:
16
+ - Relevance: Only includes needed context
17
+ - Structure: Clear separation of context layers
18
+ - Timing: Retrieves information when needed
19
+ - Consistency: Stable system instructions
20
+ """
21
+
22
+ def __init__(self):
23
+ # Initialize components
24
+ self.llm = ChatOpenAI(
25
+ model=Settings.MODEL_NAME,
26
+ temperature=Settings.TEMPERATURE
27
+ )
28
+
29
+ self.knowledge_base = KnowledgeBase(top_k=Settings.RAG_TOP_K)
30
+ self.memory = ConversationMemory(max_messages=Settings.MAX_CONVERSATION_MESSAGES)
31
+ self.visualizer = ContextVisualizer()
32
+
33
+ # System instructions
34
+ self.system_prompt = Settings.SYSTEM_PROMPT
35
+
36
+ # Create tools
37
+ self.tools = [calculate_metric, get_current_time]
38
+
39
+ # Create agent
40
+ self.agent = create_agent(
41
+ model=self.llm,
42
+ tools=self.tools,
43
+ system_prompt=self.system_prompt
44
+ )
45
+
46
+ def process_query(self, user_query: str) -> tuple[str, ContextVisualizer]:
47
+ """
48
+ Process a user query with full context engineering
49
+ Returns: (response, visualizer)
50
+ """
51
+ # Reset visualizer for new query
52
+ self.visualizer = ContextVisualizer()
53
+
54
+ # Layer 1: System Instructions
55
+ self.visualizer.add_layer(
56
+ "System Instructions",
57
+ self.system_prompt,
58
+ )
59
+
60
+ # Layer 2: Conversation History
61
+ history_text = self.memory.get_history_text()
62
+ self.visualizer.add_layer(
63
+ "Conversation History",
64
+ history_text if history_text != "No previous conversation" else "No previous conversation",
65
+ )
66
+
67
+ # Layer 3: Retrieved Knowledge (RAG)
68
+ retrieved_context = self.knowledge_base.retrieve_relevant(user_query)
69
+ self.visualizer.add_layer(
70
+ "Retrieved Knowledge (RAG)",
71
+ retrieved_context,
72
+ )
73
+
74
+ # Layer 4: Current User Query
75
+ self.visualizer.add_layer(
76
+ "User Query",
77
+ user_query,
78
+ )
79
+
80
+ # Layer 5: Available Tools
81
+ tools_context = "\n".join([
82
+ f"- {tool.name}: {tool.description}" for tool in self.tools
83
+ ])
84
+ self.visualizer.add_layer(
85
+ "Available Tools",
86
+ tools_context,
87
+ )
88
+
89
+ # Build the context structure
90
+ context_message = f"""Context from Knowledge Base:
91
+ {retrieved_context}
92
+
93
+ Previous Conversation:
94
+ {history_text}
95
+
96
+ Current Question:
97
+ {user_query}"""
98
+
99
+ # Invoke agent
100
+ result = self.agent.invoke({
101
+ "messages": [{"role": "user", "content": context_message}]
102
+ })
103
+
104
+ # Extract response
105
+ response = result["messages"][-1].content
106
+
107
+ # Update conversation memory
108
+ self.memory.add_user_message(user_query)
109
+ self.memory.add_ai_message(response)
110
+
111
+ return response, self.visualizer
app/knowledge.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Knowledge base with RAG capabilities"""
2
+
3
+ from langchain_openai import OpenAIEmbeddings
4
+ from langchain_community.vectorstores import FAISS
5
+
6
+
7
+ class KnowledgeBase:
8
+ """Simulates a knowledge base with RAG capabilities"""
9
+
10
+ def __init__(self, top_k: int = 2):
11
+ # Sample knowledge documents about data analytics
12
+ self.documents = [
13
+ "Gross Revenue is defined as total sales before refunds and returns.",
14
+ "Net Revenue equals gross revenue minus refunds, returns, and discounts.",
15
+ "AOV (Average Order Value) is calculated as total revenue divided by number of orders.",
16
+ "Customer Lifetime Value (CLV) is the total revenue expected from a customer over their entire relationship.",
17
+ "Conversion Rate is the percentage of visitors who complete a desired action.",
18
+ "Churn Rate measures the percentage of customers who stop using your service over a period.",
19
+ "Monthly Recurring Revenue (MRR) is the predictable revenue generated each month.",
20
+ "CAC (Customer Acquisition Cost) is the total cost of acquiring a new customer.",
21
+ ]
22
+
23
+ # Create embeddings and vector store
24
+ self.embeddings = OpenAIEmbeddings()
25
+ self.vectorstore = FAISS.from_texts(self.documents, self.embeddings)
26
+ self.retriever = self.vectorstore.as_retriever(search_kwargs={"k": top_k})
27
+
28
+ def retrieve_relevant(self, query: str) -> str:
29
+ """Retrieve relevant documents for a query"""
30
+ docs = self.retriever.invoke(query)
31
+ return "\n".join([f"- {doc.page_content}" for doc in docs])
app/memory.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Conversation memory management"""
2
+
3
+ from typing import List
4
+ from langchain.messages import HumanMessage, AIMessage
5
+
6
+
7
+ class ConversationMemory:
8
+ """Manages conversation history with smart truncation"""
9
+
10
+ def __init__(self, max_messages: int = 4):
11
+ self.messages = []
12
+ self.max_messages = max_messages
13
+
14
+ def add_user_message(self, content: str):
15
+ """Add user message to history"""
16
+ self.messages.append(HumanMessage(content=content))
17
+ self._truncate()
18
+
19
+ def add_ai_message(self, content: str):
20
+ """Add AI message to history"""
21
+ self.messages.append(AIMessage(content=content))
22
+ self._truncate()
23
+
24
+ def _truncate(self):
25
+ """Keep only recent messages to avoid context bloat"""
26
+ if len(self.messages) > self.max_messages:
27
+ self.messages = self.messages[-self.max_messages:]
28
+
29
+ def get_history(self) -> List:
30
+ """Get formatted conversation history"""
31
+ return self.messages
32
+
33
+ def get_history_text(self) -> str:
34
+ """Get history as formatted text"""
35
+ if not self.messages:
36
+ return "No previous conversation"
37
+
38
+ history_text = []
39
+ for msg in self.messages:
40
+ role = "User" if isinstance(msg, HumanMessage) else "Assistant"
41
+ history_text.append(f"{role}: {msg.content}")
42
+ return "\n".join(history_text)
app/tools.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Agent tools for external operations"""
2
+
3
+ from datetime import datetime
4
+ from langchain.tools import tool
5
+
6
+
7
+ @tool
8
+ def calculate_metric(metric_name: str, values: str) -> str:
9
+ """
10
+ Calculate a business metric.
11
+
12
+ Args:
13
+ metric_name: Name of metric (aov, conversion_rate, clv, etc.)
14
+ values: Comma-separated values needed for calculation
15
+ """
16
+ try:
17
+ nums = [float(x.strip()) for x in values.split(",")]
18
+
19
+ if metric_name.lower() == "aov":
20
+ # Average Order Value = total revenue / number of orders
21
+ if len(nums) >= 2:
22
+ result = nums[0] / nums[1]
23
+ return f"AOV: ${result:.2f}"
24
+
25
+ elif metric_name.lower() == "conversion_rate":
26
+ # Conversion Rate = (conversions / visitors) * 100
27
+ if len(nums) >= 2:
28
+ result = (nums[0] / nums[1]) * 100
29
+ return f"Conversion Rate: {result:.2f}%"
30
+
31
+ elif metric_name.lower() == "churn_rate":
32
+ # Churn Rate = (customers lost / total customers) * 100
33
+ if len(nums) >= 2:
34
+ result = (nums[0] / nums[1]) * 100
35
+ return f"Churn Rate: {result:.2f}%"
36
+
37
+ return f"Calculated {metric_name} with values {values}"
38
+
39
+ except Exception as e:
40
+ return f"Error calculating metric: {str(e)}"
41
+
42
+
43
+ @tool
44
+ def get_current_time() -> str:
45
+ """Get the current date and time"""
46
+ return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
app/ui.py ADDED
@@ -0,0 +1,289 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Gradio UI for Context Engineering Visualizer"""
2
+
3
+ import gradio as gr
4
+ from typing import Tuple, List
5
+
6
+ from .agent import ContextEngineeringAgent
7
+ from config import Settings
8
+
9
+
10
+ class ContextVisualizerUI:
11
+ """Gradio interface for the Context Engineering Visualizer"""
12
+
13
+ def __init__(self):
14
+ self.agent = None
15
+ self.chat_history = []
16
+
17
+ def initialize_agent(self) -> str:
18
+ """Initialize the agent"""
19
+ try:
20
+ self.agent = ContextEngineeringAgent()
21
+ return "Agent initialized successfully"
22
+ except Exception as e:
23
+ return f"Error initializing agent: {str(e)}"
24
+
25
+ def format_context_layers(self, visualizer) -> str:
26
+ """Format context layers for display as stacked container visualization"""
27
+ if not visualizer.context_layers:
28
+ return "<div style='text-align: center; padding: 20px;'>No context layers available</div>"
29
+
30
+ total_tokens = sum(visualizer.token_counts.values())
31
+
32
+ # Color palette for different layers
33
+ colors = [
34
+ "#4A90E2", # Blue - System Instructions
35
+ "#7B68EE", # Purple - Conversation History
36
+ "#50C878", # Green - Retrieved Knowledge
37
+ "#F39C12", # Orange - User Query
38
+ "#E74C3C" # Red - Available Tools
39
+ ]
40
+
41
+ # Build HTML for stacked container
42
+ html = f"""
43
+ <div style="max-width: 800px; margin: 0 auto; font-family: 'Inter', sans-serif;">
44
+ <div style="text-align: center; margin-bottom: 20px;">
45
+ <h3 style="margin: 0; color: #2c3e50;">Context Window Structure</h3>
46
+ <p style="margin: 5px 0; color: #7f8c8d; font-size: 14px;">Total: {total_tokens} tokens</p>
47
+ </div>
48
+
49
+ <div style="border: 2px solid #34495e; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 6px rgba(0,0,0,0.1);">
50
+ """
51
+
52
+ for i, layer in enumerate(visualizer.context_layers):
53
+ percentage = (layer["tokens"] / total_tokens * 100) if total_tokens > 0 else 0
54
+ color = colors[i % len(colors)]
55
+
56
+ # Create stacked section
57
+ html += f"""
58
+ <div style="background: linear-gradient(135deg, {color} 0%, {color}dd 100%);
59
+ padding: 15px 20px;
60
+ border-bottom: 1px solid rgba(255,255,255,0.1);
61
+ position: relative;
62
+ height: {max(percentage * 3, 30)}px;
63
+ display: flex;
64
+ align-items: center;
65
+ transition: all 0.3s ease;">
66
+ <div style="flex: 1;">
67
+ <div style="color: white; font-weight: 600; font-size: 14px; margin-bottom: 3px;">
68
+ {layer['layer'].upper()}
69
+ </div>
70
+ <div style="color: rgba(255,255,255,0.9); font-size: 12px;">
71
+ {layer['tokens']} tokens ({percentage:.1f}%)
72
+ </div>
73
+ </div>
74
+ <div style="color: rgba(255,255,255,0.8); font-size: 24px; font-weight: bold;">
75
+ {percentage:.0f}%
76
+ </div>
77
+ </div>
78
+ """
79
+
80
+ html += """
81
+ </div>
82
+ </div>
83
+ """
84
+
85
+ return html
86
+
87
+ def format_context_details(self, visualizer) -> str:
88
+ """Format detailed context layer contents for markdown display"""
89
+ if not visualizer.context_layers:
90
+ return "No context layers available"
91
+
92
+ output = []
93
+ total_tokens = sum(visualizer.token_counts.values())
94
+
95
+ for i, layer in enumerate(visualizer.context_layers, 1):
96
+ percentage = (layer["tokens"] / total_tokens * 100) if total_tokens > 0 else 0
97
+
98
+ output.append(f"### {i}. {layer['layer'].upper()}")
99
+ output.append(f"**Tokens:** {layer['tokens']} ({percentage:.1f}%)")
100
+ output.append(f"\n**Content:**")
101
+ output.append(f"```\n{layer['content']}\n```")
102
+ output.append("")
103
+
104
+ return "\n".join(output)
105
+
106
+ def format_token_breakdown(self, visualizer) -> List[Tuple[str, int]]:
107
+ """Format token breakdown for chart"""
108
+ if not visualizer.token_counts:
109
+ return []
110
+
111
+ return [(layer, tokens) for layer, tokens in visualizer.token_counts.items()]
112
+
113
+ def process_query(
114
+ self,
115
+ query: str,
116
+ history: List,
117
+ show_visualization: bool
118
+ ) -> Tuple[List, str, str, str]:
119
+ """Process user query and return results"""
120
+
121
+ if not self.agent:
122
+ self.initialize_agent()
123
+
124
+ if not query.strip():
125
+ return history, "", "", ""
126
+
127
+ try:
128
+ # Process query
129
+ response, visualizer = self.agent.process_query(query)
130
+
131
+ # Add to chat history (format: list of dicts with role and content)
132
+ history.append({"role": "user", "content": query})
133
+ history.append({"role": "assistant", "content": response})
134
+
135
+ # Format outputs
136
+ if show_visualization:
137
+ context_viz_html = self.format_context_layers(visualizer)
138
+ context_details = self.format_context_details(visualizer)
139
+ else:
140
+ context_viz_html = "<div style='text-align: center; padding: 20px; color: #7f8c8d;'>Visualization disabled</div>"
141
+ context_details = "Visualization disabled"
142
+
143
+ return history, "", context_viz_html, context_details
144
+
145
+ except Exception as e:
146
+ error_msg = f"Error processing query: {str(e)}"
147
+ history.append({"role": "user", "content": query})
148
+ history.append({"role": "assistant", "content": error_msg})
149
+ return history, "", "", ""
150
+
151
+ def clear_conversation(self) -> Tuple[List, str, str]:
152
+ """Clear conversation history"""
153
+ if self.agent:
154
+ self.agent.memory.messages = []
155
+ return [], "", ""
156
+
157
+ def create_interface(self) -> gr.Blocks:
158
+ """Create the Gradio interface"""
159
+
160
+ with gr.Blocks(
161
+ title="Context Engineering Visualizer"
162
+ ) as interface:
163
+
164
+ gr.Markdown("""
165
+ # Context Engineering Visualizer
166
+
167
+ This tool demonstrates how information flows into an AI agent's context window before inference.
168
+ Ask questions about business metrics and data analysis to see the context engineering in action.
169
+ """)
170
+
171
+ with gr.Accordion("About Context Engineering", open=False):
172
+ gr.Markdown("""
173
+ **Context Engineering** is the practice of carefully managing what information goes into an AI model's context window.
174
+
175
+ This visualizer shows five key layers:
176
+
177
+ 1. **System Instructions**: Stable guidelines that define the agent's role and behavior
178
+ 2. **Conversation History**: Recent messages to maintain conversational coherence
179
+ 3. **Retrieved Knowledge (RAG)**: Relevant information retrieved from a knowledge base
180
+ 4. **User Query**: The current question or request
181
+ 5. **Available Tools**: External functions the agent can use
182
+
183
+ Each layer contributes tokens to the context window. Good context engineering ensures:
184
+ - **Relevance**: Only necessary information is included
185
+ - **Structure**: Clear separation and organization of context layers
186
+ - **Efficiency**: Optimal use of limited context window space
187
+ - **Consistency**: Stable system instructions across interactions
188
+ """)
189
+
190
+ with gr.Sidebar(label="Settings & Examples", open=True, width=320):
191
+ gr.Markdown("### Settings")
192
+
193
+ show_viz = gr.Checkbox(
194
+ label="Show Context Visualization",
195
+ value=True,
196
+ info="Display detailed breakdown of context layers"
197
+ )
198
+
199
+ gr.Markdown("### Example Questions")
200
+ gr.Markdown("""
201
+ **Try these sequential scenarios to see context engineering in action:**
202
+
203
+ **Scenario 1: Understanding AOV (Average Order Value)**
204
+ 1. What is Average Order Value and how is it calculated?
205
+ 2. Calculate the AOV if total revenue is $50000 and we had 500 orders
206
+ 3. What if we had 700 orders with the same revenue instead?
207
+
208
+ **Scenario 2: Conversion Rate Analysis**
209
+ 1. What is Conversion Rate?
210
+ 2. Calculate conversion rate with 250 conversions and 10000 visitors
211
+ 3. How would the rate change if we got 400 conversions?
212
+
213
+ **Scenario 3: Understanding Revenue Metrics**
214
+ 1. What is the difference between gross and net revenue?
215
+ 2. If gross revenue is $100000 with $15000 in refunds and $5000 in discounts, what's the net revenue?
216
+
217
+ **Scenario 4: Churn Rate**
218
+ 1. Explain what Churn Rate means
219
+ 2. Calculate churn rate if we lost 50 customers out of 1000 total customers
220
+ """)
221
+
222
+ chatbot = gr.Chatbot(
223
+ label="Conversation",
224
+ height=500,
225
+ avatar_images=(None, None)
226
+ )
227
+
228
+ query_input = gr.Textbox(
229
+ label="Your Question",
230
+ placeholder="e.g., What is Average Order Value and how is it calculated?",
231
+ lines=2,
232
+ show_label=False
233
+ )
234
+
235
+ with gr.Row():
236
+ submit_btn = gr.Button("Submit", variant="primary")
237
+ clear_btn = gr.Button("Clear Conversation")
238
+
239
+ with gr.Accordion("Context Window Breakdown", open=True):
240
+ context_viz = gr.HTML(
241
+ value="<div style='text-align: center; padding: 20px; color: #7f8c8d;'>Submit a query to see context breakdown</div>"
242
+ )
243
+
244
+ with gr.Accordion("Detailed Layer Contents", open=False):
245
+ context_details = gr.Markdown(
246
+ value="Submit a query to see detailed breakdown"
247
+ )
248
+
249
+ # Event handlers
250
+ submit_btn.click(
251
+ fn=self.process_query,
252
+ inputs=[query_input, chatbot, show_viz],
253
+ outputs=[chatbot, query_input, context_viz, context_details]
254
+ )
255
+
256
+ query_input.submit(
257
+ fn=self.process_query,
258
+ inputs=[query_input, chatbot, show_viz],
259
+ outputs=[chatbot, query_input, context_viz, context_details]
260
+ )
261
+
262
+ clear_btn.click(
263
+ fn=self.clear_conversation,
264
+ inputs=[],
265
+ outputs=[chatbot, context_viz, context_details]
266
+ )
267
+
268
+ gr.Markdown("""
269
+ ---
270
+ **Note**: This visualizer uses OpenAI's GPT model and requires an API key in your environment.
271
+ """)
272
+
273
+ return interface
274
+
275
+
276
+ def launch_ui(
277
+ share: bool = Settings.GRADIO_SHARE,
278
+ server_name: str = Settings.GRADIO_SERVER_NAME,
279
+ server_port: int = Settings.GRADIO_SERVER_PORT
280
+ ):
281
+ """Launch the Gradio interface"""
282
+ ui = ContextVisualizerUI()
283
+ interface = ui.create_interface()
284
+ interface.launch(
285
+ share=share,
286
+ server_name=server_name,
287
+ server_port=server_port,
288
+ theme=gr.themes.Soft()
289
+ )
app/visualizer.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Context window visualization component"""
2
+
3
+ from typing import Dict, Any
4
+ from datetime import datetime
5
+
6
+
7
+ class ContextVisualizer:
8
+ """Tracks and visualizes what goes into the context window"""
9
+
10
+ def __init__(self):
11
+ self.context_layers = []
12
+ self.token_counts = {}
13
+
14
+ def add_layer(self, layer_name: str, content: str, token_estimate: int = None):
15
+ """Add a context layer for visualization"""
16
+ if token_estimate is None:
17
+ # Rough estimate: ~4 chars per token
18
+ token_estimate = len(content) // 4
19
+
20
+ self.context_layers.append({
21
+ "layer": layer_name,
22
+ "content": content,
23
+ "tokens": token_estimate,
24
+ "timestamp": datetime.now().isoformat()
25
+ })
26
+ self.token_counts[layer_name] = token_estimate
27
+
28
+ def get_summary(self) -> Dict[str, Any]:
29
+ """Get structured summary of context"""
30
+ return {
31
+ "layers": [l["layer"] for l in self.context_layers],
32
+ "total_tokens": sum(self.token_counts.values()),
33
+ "breakdown": self.token_counts
34
+ }
config/__init__.py ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ """Configuration module for Context Engineering Visualizer"""
2
+
3
+ from .settings import Settings
4
+
5
+ __all__ = ["Settings"]
config/settings.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Application settings and configuration"""
2
+
3
+ import os
4
+ from dotenv import load_dotenv
5
+
6
+ load_dotenv()
7
+
8
+
9
+ class Settings:
10
+ """Application configuration"""
11
+
12
+ # OpenAI Settings
13
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
14
+ MODEL_NAME = "gpt-4.1-mini"
15
+ TEMPERATURE = 0
16
+
17
+ # Memory Settings
18
+ MAX_CONVERSATION_MESSAGES = 4
19
+
20
+ # RAG Settings
21
+ RAG_TOP_K = 2
22
+
23
+ # UI Settings
24
+ GRADIO_SHARE = False
25
+ GRADIO_SERVER_NAME = "127.0.0.1"
26
+ GRADIO_SERVER_PORT = 7860
27
+
28
+ # System Prompt
29
+ SYSTEM_PROMPT = """You are a data analyst assistant.
30
+
31
+ Your role:
32
+ - Answer questions about business metrics and data analysis
33
+ - Use the provided context and knowledge to give accurate answers
34
+ - If context is insufficient, clearly state what information is missing
35
+ - Always explain your reasoning briefly
36
+ - Use the calculator tool for numeric computations
37
+
38
+ Be concise, accurate, and helpful."""
main.py CHANGED
@@ -1,434 +1,9 @@
1
  """
2
  Context Engineering Visualizer
3
- Demonstrates how information flows into an agent's context window before inference
4
  """
5
 
6
- import os
7
- from typing import List, Dict, Any
8
- from datetime import datetime
9
- from dotenv import load_dotenv
10
-
11
- from langchain_openai import ChatOpenAI, OpenAIEmbeddings
12
- from langchain.agents import create_agent
13
- from langchain.tools import tool
14
- from langchain.messages import HumanMessage, AIMessage
15
- from langchain_community.vectorstores import FAISS
16
-
17
- # Load environment variables
18
- load_dotenv()
19
-
20
- # ============================================
21
- # CONTEXT ENGINEERING COMPONENTS
22
- # ============================================
23
-
24
- class ContextVisualizer:
25
- """Tracks and visualizes what goes into the context window"""
26
-
27
- def __init__(self):
28
- self.context_layers = []
29
- self.token_counts = {}
30
-
31
- def add_layer(self, layer_name: str, content: str, token_estimate: int = None):
32
- """Add a context layer for visualization"""
33
- if token_estimate is None:
34
- # Rough estimate: ~4 chars per token
35
- token_estimate = len(content) // 4
36
-
37
- self.context_layers.append({
38
- "layer": layer_name,
39
- "content": content,
40
- "tokens": token_estimate,
41
- "timestamp": datetime.now().isoformat()
42
- })
43
- self.token_counts[layer_name] = token_estimate
44
-
45
- def visualize(self):
46
- """Display the context window structure"""
47
- print("\n" + "="*80)
48
- print("CONTEXT WINDOW VISUALIZATION")
49
- print("="*80)
50
-
51
- total_tokens = sum(self.token_counts.values())
52
-
53
- for i, layer in enumerate(self.context_layers, 1):
54
- percentage = (layer["tokens"] / total_tokens * 100) if total_tokens > 0 else 0
55
- bar_length = int(percentage / 2)
56
- bar = "█" * bar_length
57
-
58
- print(f"\n{i}. {layer['layer'].upper()}")
59
- print(f" Tokens: {layer['tokens']} ({percentage:.1f}%)")
60
- print(f" [{bar:<50}]")
61
- print(f" Content:\n{layer['content']}")
62
-
63
- print(f"\n{'='*80}")
64
- print(f"TOTAL CONTEXT TOKENS: {total_tokens}")
65
- print(f"{'='*80}\n")
66
-
67
- def get_summary(self) -> Dict[str, Any]:
68
- """Get structured summary of context"""
69
- return {
70
- "layers": [l["layer"] for l in self.context_layers],
71
- "total_tokens": sum(self.token_counts.values()),
72
- "breakdown": self.token_counts
73
- }
74
-
75
-
76
- # ============================================
77
- # KNOWLEDGE BASE (RAG Component)
78
- # ============================================
79
-
80
- class KnowledgeBase:
81
- """Simulates a knowledge base with RAG capabilities"""
82
-
83
- def __init__(self):
84
- # Sample knowledge documents about data analytics
85
- self.documents = [
86
- "Gross Revenue is defined as total sales before refunds and returns.",
87
- "Net Revenue equals gross revenue minus refunds, returns, and discounts.",
88
- "AOV (Average Order Value) is calculated as total revenue divided by number of orders.",
89
- "Customer Lifetime Value (CLV) is the total revenue expected from a customer over their entire relationship.",
90
- "Conversion Rate is the percentage of visitors who complete a desired action.",
91
- "Churn Rate measures the percentage of customers who stop using your service over a period.",
92
- "Monthly Recurring Revenue (MRR) is the predictable revenue generated each month.",
93
- "CAC (Customer Acquisition Cost) is the total cost of acquiring a new customer.",
94
- ]
95
-
96
- # Create embeddings and vector store
97
- self.embeddings = OpenAIEmbeddings()
98
- self.vectorstore = FAISS.from_texts(self.documents, self.embeddings)
99
- self.retriever = self.vectorstore.as_retriever(search_kwargs={"k": 2})
100
-
101
- def retrieve_relevant(self, query: str) -> str:
102
- """Retrieve relevant documents for a query"""
103
- docs = self.retriever.invoke(query)
104
- return "\n".join([f"- {doc.page_content}" for doc in docs])
105
-
106
-
107
- # ============================================
108
- # CONVERSATION MEMORY
109
- # ============================================
110
-
111
- class ConversationMemory:
112
- """Manages conversation history with smart truncation"""
113
-
114
- def __init__(self, max_messages: int = 4):
115
- self.messages = []
116
- self.max_messages = max_messages
117
-
118
- def add_user_message(self, content: str):
119
- """Add user message to history"""
120
- self.messages.append(HumanMessage(content=content))
121
- self._truncate()
122
-
123
- def add_ai_message(self, content: str):
124
- """Add AI message to history"""
125
- self.messages.append(AIMessage(content=content))
126
- self._truncate()
127
-
128
- def _truncate(self):
129
- """Keep only recent messages to avoid context bloat"""
130
- if len(self.messages) > self.max_messages:
131
- # Keep the most recent messages
132
- self.messages = self.messages[-self.max_messages:]
133
-
134
- def get_history(self) -> List:
135
- """Get formatted conversation history"""
136
- return self.messages
137
-
138
- def get_history_text(self) -> str:
139
- """Get history as formatted text"""
140
- if not self.messages:
141
- return "No previous conversation"
142
-
143
- history_text = []
144
- for msg in self.messages:
145
- role = "User" if isinstance(msg, HumanMessage) else "Assistant"
146
- history_text.append(f"{role}: {msg.content}")
147
- return "\n".join(history_text)
148
-
149
-
150
- # ============================================
151
- # TOOLS (External Connections)
152
- # ============================================
153
-
154
- @tool
155
- def calculate_metric(
156
- metric_name: str,
157
- values: str
158
- ) -> str:
159
- """
160
- Calculate a business metric.
161
-
162
- Args:
163
- metric_name: Name of metric (aov, conversion_rate, clv, etc.)
164
- values: Comma-separated values needed for calculation
165
- """
166
- try:
167
- nums = [float(x.strip()) for x in values.split(",")]
168
-
169
- if metric_name.lower() == "aov":
170
- # Average Order Value = total revenue / number of orders
171
- if len(nums) >= 2:
172
- result = nums[0] / nums[1]
173
- return f"AOV: ${result:.2f}"
174
-
175
- elif metric_name.lower() == "conversion_rate":
176
- # Conversion Rate = (conversions / visitors) * 100
177
- if len(nums) >= 2:
178
- result = (nums[0] / nums[1]) * 100
179
- return f"Conversion Rate: {result:.2f}%"
180
-
181
- elif metric_name.lower() == "churn_rate":
182
- # Churn Rate = (customers lost / total customers) * 100
183
- if len(nums) >= 2:
184
- result = (nums[0] / nums[1]) * 100
185
- return f"Churn Rate: {result:.2f}%"
186
-
187
- return f"Calculated {metric_name} with values {values}"
188
-
189
- except Exception as e:
190
- return f"Error calculating metric: {str(e)}"
191
-
192
-
193
- @tool
194
- def get_current_time() -> str:
195
- """Get the current date and time"""
196
- return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
197
-
198
-
199
- # ============================================
200
- # CONTEXT ENGINEERING AGENT
201
- # ============================================
202
-
203
- class ContextEngineeringAgent:
204
- """
205
- Agent that demonstrates context engineering principles:
206
- - Relevance: Only includes needed context
207
- - Structure: Clear separation of context layers
208
- - Timing: Retrieves information when needed
209
- - Consistency: Stable system instructions
210
- """
211
-
212
- def __init__(self):
213
- # Initialize components
214
- self.llm = ChatOpenAI(
215
- model="gpt-4.1-mini",
216
- temperature=0 # Low temp for consistency
217
- )
218
-
219
- self.knowledge_base = KnowledgeBase()
220
- self.memory = ConversationMemory(max_messages=4)
221
- self.visualizer = ContextVisualizer()
222
-
223
- # System instructions (STABLE CONTEXT)
224
- self.system_prompt = """You are a data analyst assistant.
225
-
226
- Your role:
227
- - Answer questions about business metrics and data analysis
228
- - Use the provided context and knowledge to give accurate answers
229
- - If context is insufficient, clearly state what information is missing
230
- - Always explain your reasoning briefly
231
- - Use the calculator tool for numeric computations
232
-
233
- Be concise, accurate, and helpful."""
234
-
235
- # Create tools
236
- self.tools = [calculate_metric, get_current_time]
237
-
238
- # Create agent
239
- self.agent = create_agent(
240
- model=self.llm,
241
- tools=self.tools,
242
- system_prompt=self.system_prompt
243
- )
244
-
245
- def process_query(self, user_query: str, show_visualization: bool = True) -> str:
246
- """
247
- Process a user query with full context engineering visualization
248
- """
249
- # Reset visualizer for new query
250
- self.visualizer = ContextVisualizer()
251
-
252
- print(f"\n{'='*80}")
253
- print(f"USER QUERY: {user_query}")
254
- print(f"{'='*80}\n")
255
-
256
- # ============================================
257
- # LAYER 1: System Instructions
258
- # ============================================
259
- self.visualizer.add_layer(
260
- "System Instructions",
261
- self.system_prompt,
262
- )
263
-
264
- # ============================================
265
- # LAYER 2: Conversation History
266
- # ============================================
267
- history_text = self.memory.get_history_text()
268
- self.visualizer.add_layer(
269
- "Conversation History",
270
- history_text if history_text != "No previous conversation" else "No previous conversation",
271
- )
272
-
273
- # ============================================
274
- # LAYER 3: Retrieved Knowledge (RAG)
275
- # ============================================
276
- retrieved_context = self.knowledge_base.retrieve_relevant(user_query)
277
- self.visualizer.add_layer(
278
- "Retrieved Knowledge (RAG)",
279
- retrieved_context,
280
- )
281
-
282
- # ============================================
283
- # LAYER 4: Current User Query
284
- # ============================================
285
- self.visualizer.add_layer(
286
- "User Query",
287
- user_query,
288
- )
289
-
290
- # ============================================
291
- # LAYER 5: Available Tools
292
- # ============================================
293
- tools_context = "\n".join([
294
- f"- {tool.name}: {tool.description}" for tool in self.tools
295
- ])
296
- self.visualizer.add_layer(
297
- "Available Tools",
298
- tools_context,
299
- )
300
-
301
- # Show visualization BEFORE inference
302
- if show_visualization:
303
- self.visualizer.visualize()
304
- print("\n🤖 Sending context to model for inference...\n")
305
-
306
- # ============================================
307
- # INFERENCE: Agent processes with full context
308
- # ============================================
309
-
310
- # Build the context structure
311
- context_message = f"""Context from Knowledge Base:
312
- {retrieved_context}
313
-
314
- Previous Conversation:
315
- {history_text}
316
-
317
- Current Question:
318
- {user_query}"""
319
-
320
- # Invoke agent
321
- result = self.agent.invoke({
322
- "messages": [{"role": "user", "content": context_message}]
323
- })
324
-
325
- # Extract response
326
- response = result["messages"][-1].content
327
-
328
- # Update conversation memory
329
- self.memory.add_user_message(user_query)
330
- self.memory.add_ai_message(response)
331
-
332
- # Show response
333
- print(f"\n{'='*80}")
334
- print("AGENT RESPONSE:")
335
- print(f"{'='*80}")
336
- print(response)
337
- print(f"{'='*80}\n")
338
-
339
- return response
340
-
341
-
342
- # ============================================
343
- # DEMO SCENARIOS
344
- # ============================================
345
-
346
- def run_demo():
347
- """Run demonstration scenarios"""
348
-
349
- print("\n" + "🎯" * 40)
350
- print("CONTEXT ENGINEERING VISUALIZER")
351
- print("Demonstrates how information flows into an agent's context window")
352
- print("🎯" * 40)
353
-
354
- agent = ContextEngineeringAgent()
355
-
356
- # Scenario 1: Simple query with RAG
357
- print("\n\n" + "📊 SCENARIO 1: RAG-based Query" + "\n")
358
- agent.process_query(
359
- "What is Average Order Value and how is it calculated?"
360
- )
361
-
362
- input("\n⏸️ Press Enter to continue to next scenario...")
363
-
364
- # Scenario 2: Query with tool use
365
- print("\n\n" + "🔧 SCENARIO 2: Query Requiring Tool Use" + "\n")
366
- agent.process_query(
367
- "Calculate the AOV if total revenue is $50000 and we had 500 orders"
368
- )
369
-
370
- input("\n⏸️ Press Enter to continue to next scenario...")
371
-
372
- # Scenario 3: Query with conversation history
373
- print("\n\n" + "💬 SCENARIO 3: Query Using Conversation Context" + "\n")
374
- agent.process_query(
375
- "What about if we had 750 orders instead?"
376
- )
377
-
378
- input("\n⏸️ Press Enter to see context summary...")
379
-
380
- # Show final summary
381
- print("\n\n" + "📈 CONTEXT ENGINEERING SUMMARY" + "\n")
382
- summary = agent.visualizer.get_summary()
383
- print(f"Context Layers Used: {', '.join(summary['layers'])}")
384
- print(f"Total Context Tokens: {summary['total_tokens']}")
385
- print(f"\nToken Breakdown:")
386
- for layer, tokens in summary['breakdown'].items():
387
- print(f" - {layer}: {tokens} tokens")
388
-
389
-
390
- # ============================================
391
- # INTERACTIVE MODE
392
- # ============================================
393
-
394
- def run_interactive():
395
- """Run interactive mode"""
396
-
397
- print("\n" + "💡" * 40)
398
- print("CONTEXT ENGINEERING - INTERACTIVE MODE")
399
- print("💡" * 40)
400
- print("\nType your questions. Type 'quit' to exit.\n")
401
-
402
- agent = ContextEngineeringAgent()
403
-
404
- while True:
405
- try:
406
- user_input = input("\n📝 Your question: ").strip()
407
-
408
- if user_input.lower() in ['quit', 'exit', 'q']:
409
- print("\n👋 Goodbye!")
410
- break
411
-
412
- if not user_input:
413
- continue
414
-
415
- agent.process_query(user_input, show_visualization=True)
416
-
417
- except KeyboardInterrupt:
418
- print("\n\n👋 Goodbye!")
419
- break
420
- except Exception as e:
421
- print(f"\n❌ Error: {str(e)}")
422
-
423
-
424
- # ============================================
425
- # MAIN
426
- # ============================================
427
 
428
  if __name__ == "__main__":
429
- import sys
430
-
431
- if len(sys.argv) > 1 and sys.argv[1] == "interactive":
432
- run_interactive()
433
- else:
434
- run_demo()
 
1
  """
2
  Context Engineering Visualizer
3
+ Main entry point for the application
4
  """
5
 
6
+ from app.ui import launch_ui
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
  if __name__ == "__main__":
9
+ launch_ui()