jzou19950715 commited on
Commit
1666a21
·
verified ·
1 Parent(s): 4dec0f2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +166 -265
app.py CHANGED
@@ -1,298 +1,199 @@
1
- import os
2
- import logging
3
- import pandas as pd
4
- import google.generativeai as genai
5
  import gradio as gr
6
- from typing import Dict, List, Any, Tuple
 
7
  import json
8
- import matplotlib.pyplot as plt
9
- import seaborn as sns
10
- import io
11
- import base64
12
 
13
- # Configure logging
14
- logging.basicConfig(level=logging.INFO)
15
- logger = logging.getLogger(__name__)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
- class DataTools:
18
- """Tools for data analysis that can be called by the AI"""
19
- def __init__(self, df: pd.DataFrame):
20
- self.df = df
21
-
22
- def describe_column(self, column: str) -> dict:
23
- """Get statistical description of a column"""
24
- if column not in self.df.columns:
25
- return {"error": f"Column {column} not found"}
26
-
27
- stats = self.df[column].describe().to_dict()
28
- null_count = self.df[column].isnull().sum()
29
- return {
30
- "statistics": stats,
31
- "null_count": int(null_count),
32
- "dtype": str(self.df[column].dtype)
33
- }
34
-
35
- def create_visualization(self, plot_type: str, x: str, y: str = None, title: str = None) -> str:
36
- """Create a visualization and return as base64 string"""
37
- try:
38
- plt.figure(figsize=(10, 6))
39
- if plot_type == "histogram":
40
- sns.histplot(data=self.df, x=x)
41
- elif plot_type == "scatter":
42
- sns.scatterplot(data=self.df, x=x, y=y)
43
- elif plot_type == "boxplot":
44
- sns.boxplot(data=self.df, x=x, y=y)
45
- elif plot_type == "bar":
46
- sns.barplot(data=self.df, x=x, y=y)
47
-
48
- if title:
49
- plt.title(title)
50
-
51
- # Save plot to bytes buffer
52
- buf = io.BytesIO()
53
- plt.savefig(buf, format='png')
54
- buf.seek(0)
55
- plt.close()
56
-
57
- # Convert to base64
58
- return base64.b64encode(buf.read()).decode('utf-8')
59
- except Exception as e:
60
- return f"Error creating visualization: {str(e)}"
61
-
62
- def get_correlation(self, columns: List[str]) -> dict:
63
- """Get correlation between specified columns"""
64
- try:
65
- corr = self.df[columns].corr().to_dict()
66
- return {"correlation_matrix": corr}
67
- except Exception as e:
68
- return {"error": f"Error calculating correlation: {str(e)}"}
69
 
70
- class DataAnalyzer:
 
71
  def __init__(self):
72
- self.model = None
73
- self.api_key = None
74
- self.system_prompt = None
75
- self.df = None
76
- self.tools = None
77
-
78
- def configure_api(self, api_key: str):
79
- """Configure the Gemini API with the provided key"""
80
- try:
81
- genai.configure(api_key=api_key)
82
- self.model = genai.GenerativeModel('gemini-1.5-pro')
83
- self.api_key = api_key
84
- return True
85
- except Exception as e:
86
- logger.error(f"API configuration failed: {str(e)}")
87
- return False
88
 
89
- def load_data(self, file) -> Tuple[bool, str]:
90
- """Load data from uploaded CSV file"""
91
  try:
92
- self.df = pd.read_csv(file.name)
93
- self.tools = DataTools(self.df)
94
- return True, f"Loaded CSV with {len(self.df)} rows and {len(self.df.columns)} columns"
 
 
 
 
 
95
  except Exception as e:
96
- logger.error(f"Data loading failed: {str(e)}")
97
- return False, f"Error loading data: {str(e)}"
98
-
99
- def get_data_info(self) -> Dict[str, Any]:
100
- """Get information about the loaded data"""
101
- if self.df is None:
102
- return {"error": "No data loaded"}
103
-
104
- info = {
105
- "columns": list(self.df.columns),
106
- "rows": len(self.df),
107
- "sample": self.df.head(5).to_dict('records'),
108
- "dtypes": self.df.dtypes.astype(str).to_dict()
109
- }
110
- return info
111
 
112
- def analyze(self, query: str) -> Dict[str, Any]:
113
- """Analyze data based on user query with structured output"""
114
- if self.model is None:
115
- return {"error": "Please configure API key first"}
116
- if self.df is None:
117
- return {"error": "Please upload a CSV file first"}
118
 
119
- data_info = self.get_data_info()
120
-
121
- # Combine system prompt with data context and tool instructions
122
- prompt = f"""{self.system_prompt}
123
-
124
- Data Information:
125
- - Columns: {data_info['columns']}
126
- - Number of rows: {data_info['rows']}
127
- - Sample data: {json.dumps(data_info['sample'], indent=2)}
128
-
129
- Available Tools:
130
- 1. describe_column(column: str) - Get statistical description of a column
131
- 2. create_visualization(plot_type: str, x: str, y: str = None, title: str = None)
132
- - Create visualizations (types: histogram, scatter, boxplot, bar)
133
- 3. get_correlation(columns: List[str]) - Get correlation between columns
134
-
135
- User Query: {query}
136
-
137
- Please provide a structured analysis in the following JSON format:
138
- {
139
- "answer": "Direct answer to the query",
140
- "tools_used": [
141
- {
142
- "tool": "tool_name",
143
- "parameters": {"param1": "value1"},
144
- "purpose": "Why this tool was used"
145
- }
146
- ],
147
- "insights": ["List of key insights"],
148
- "visualizations": ["List of suggested visualizations"],
149
- "recommendations": ["List of recommendations"],
150
- "limitations": ["Any limitations in the analysis"]
151
- }
152
-
153
- Important:
154
- - Be specific about which tools to use
155
- - Provide clear reasoning for each tool choice
156
- - Structure the output exactly as shown above
157
- """
158
  try:
159
- # Get initial response from Gemini
160
- response = self.model.generate_content(prompt)
161
- response_text = response.text
162
 
163
- try:
164
- # Parse the response as JSON
165
- structured_response = json.loads(response_text)
166
-
167
- # Execute tool calls based on response
168
- results = {"response": structured_response, "tool_outputs": []}
169
-
170
- for tool_call in structured_response.get("tools_used", []):
171
- tool_name = tool_call["tool"]
172
- parameters = tool_call["parameters"]
173
-
174
- if hasattr(self.tools, tool_name):
175
- tool_method = getattr(self.tools, tool_name)
176
- tool_result = tool_method(**parameters)
177
- results["tool_outputs"].append({
178
- "tool": tool_name,
179
- "parameters": parameters,
180
- "result": tool_result
181
- })
182
-
183
- # Format output for Gradio
184
- formatted_output = f"""## Analysis Results
185
-
186
- {structured_response['answer']}
187
-
188
- ### Key Insights
189
- {"".join(['- ' + insight + '\\n' for insight in structured_response['insights']])}
190
-
191
- ### Visualizations
192
- {"".join(['- ' + viz + '\\n' for viz in structured_response['visualizations']])}
193
-
194
- ### Recommendations
195
- {"".join(['- ' + rec + '\\n' for rec in structured_response['recommendations']])}
196
 
197
- ### Limitations
198
- {"".join(['- ' + lim + '\\n' for lim in structured_response['limitations']])}
199
 
200
- ---
201
- Tool Outputs:
202
- {"".join([f'\\n**{out["tool"]}**:\\n```json\\n{json.dumps(out["result"], indent=2)}\\n```' for out in results['tool_outputs']])}
203
- """
204
- return formatted_output
205
-
206
- except json.JSONDecodeError:
207
- return f"Error: Could not parse structured response\\n\\nRaw response:\\n{response_text}"
208
-
209
  except Exception as e:
210
- logger.error(f"Analysis failed: {str(e)}")
211
- return f"Error during analysis: {str(e)}"
 
 
 
212
 
213
- def create_interface():
214
  """Create the Gradio interface"""
215
- analyzer = DataAnalyzer()
216
-
217
- def process_inputs(api_key: str, system_prompt: str, file, query: str):
218
- """Process user inputs and return analysis results"""
219
- if api_key != analyzer.api_key:
220
- if not analyzer.configure_api(api_key):
221
- return "Failed to configure API. Please check your API key."
222
-
223
- analyzer.system_prompt = system_prompt
224
 
225
- if file is not None:
226
- success, message = analyzer.load_data(file)
227
- if not success:
228
- return message
 
229
 
230
- return analyzer.analyze(query)
 
231
 
232
- # Create Gradio interface
233
- with gr.Blocks(title="Advanced Data Analysis Assistant") as interface:
234
- gr.Markdown("# Advanced Data Analysis Assistant")
235
- gr.Markdown("Upload your CSV file and get AI-powered analysis with visualizations")
236
-
237
  with gr.Row():
238
  api_key_input = gr.Textbox(
239
- label="Gemini API Key",
240
- placeholder="Enter your Gemini API key",
241
- type="password"
242
- )
243
-
244
- with gr.Row():
245
- system_prompt_input = gr.Textbox(
246
- label="System Prompt",
247
- placeholder="Enter system prompt for the AI",
248
- value="""You are an advanced data analysis expert. Analyze the provided data and answer the query.
249
- Focus on:
250
- 1. Clear, structured analysis
251
- 2. Statistical insights
252
- 3. Appropriate visualizations
253
- 4. Actionable recommendations""",
254
- lines=4
255
- )
256
-
257
- with gr.Row():
258
- file_input = gr.File(
259
- label="Upload CSV",
260
- file_types=[".csv"]
261
  )
 
 
 
 
 
 
 
 
 
 
262
 
263
  with gr.Row():
264
- query_input = gr.Textbox(
265
- label="Analysis Query",
266
- placeholder="What would you like to know about the data?",
267
- lines=2
 
268
  )
269
-
270
- with gr.Row():
271
- submit_btn = gr.Button("Analyze")
272
-
273
- with gr.Row():
274
- output = gr.Markdown(label="Analysis Results")
275
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
  submit_btn.click(
277
- fn=process_inputs,
278
- inputs=[api_key_input, system_prompt_input, file_input, query_input],
279
- outputs=output
 
 
 
 
 
 
280
  )
281
-
282
- return interface
283
 
284
- def main():
285
- """Main application entry point"""
286
- try:
287
- interface = create_interface()
288
- interface.launch(
289
- share=True,
290
- server_name="0.0.0.0",
291
- server_port=7860
292
  )
293
- except Exception as e:
294
- logger.error(f"Application startup failed: {str(e)}")
295
- raise
296
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
  if __name__ == "__main__":
298
- main()
 
 
 
 
 
1
  import gradio as gr
2
+ import anthropic
3
+ from typing import List, Dict, Optional, Tuple
4
  import json
 
 
 
 
5
 
6
+ # Constants
7
+ DEFAULT_SYSTEM_PROMPT = """You are a specialized coding assistant powered by Claude 3.5 Sonnet. Your primary focus is helping with programming tasks, code review, debugging, and software architecture. You should:
8
+
9
+ 1. Always provide complete, production-ready code solutions
10
+ 2. Follow language-specific best practices and conventions
11
+ 3. Include comprehensive error handling
12
+ 4. Add detailed comments and documentation
13
+ 5. Suggest optimizations and improvements
14
+ 6. Explain complex concepts with practical examples
15
+ 7. Consider security implications in your solutions
16
+ 8. Reference relevant design patterns when applicable
17
+
18
+ When giving explanations:
19
+ - Break down complex problems into smaller steps
20
+ - Provide context for your decisions
21
+ - Include example use cases
22
+ - Mention potential pitfalls and how to avoid them
23
+
24
+ For all code examples:
25
+ - Ensure proper error handling
26
+ - Follow SOLID principles
27
+ - Include type hints where applicable
28
+ - Add unit test examples for critical functionality
29
+ - Consider edge cases
30
+ """
31
 
32
+ MAX_TOKENS = 4096 # Claude 3.5 Sonnet maximum
33
+ TEMPERATURE = 0.7 # Good balance between creativity and precision for coding
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
+ class ChatState:
36
+ """Manages the state of the chat session"""
37
  def __init__(self):
38
+ self.client: Optional[anthropic.Client] = None
39
+ self.system_prompt: str = DEFAULT_SYSTEM_PROMPT
40
+ self.history: List[Tuple[str, str]] = []
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
+ def initialize_client(self, api_key: str) -> Tuple[bool, str]:
43
+ """Initialize the Anthropic client with the provided API key"""
44
  try:
45
+ self.client = anthropic.Client(api_key=api_key)
46
+ # Test the API key with a minimal request
47
+ self.client.messages.create(
48
+ model="claude-3-sonnet-20240229",
49
+ max_tokens=10,
50
+ messages=[{"role": "user", "content": "test"}]
51
+ )
52
+ return True, "API key validated successfully!"
53
  except Exception as e:
54
+ self.client = None
55
+ return False, f"Error validating API key: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
56
 
57
+ def generate_response(self, message: str) -> str:
58
+ """Generate a response using the Claude API"""
59
+ if not self.client:
60
+ return "Please enter a valid API key first."
 
 
61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  try:
63
+ # Construct messages including system prompt and history
64
+ messages = [{"role": "system", "content": self.system_prompt}]
 
65
 
66
+ # Add conversation history
67
+ for human_msg, assistant_msg in self.history:
68
+ messages.extend([
69
+ {"role": "user", "content": human_msg},
70
+ {"role": "assistant", "content": assistant_msg}
71
+ ])
72
+
73
+ # Add current message
74
+ messages.append({"role": "user", "content": message})
75
+
76
+ # Make API call
77
+ response = self.client.messages.create(
78
+ model="claude-3-sonnet-20240229",
79
+ max_tokens=MAX_TOKENS,
80
+ temperature=TEMPERATURE,
81
+ messages=messages
82
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
 
84
+ return response.content[0].text
 
85
 
 
 
 
 
 
 
 
 
 
86
  except Exception as e:
87
+ return f"Error generating response: {str(e)}"
88
+
89
+ def clear_history(self) -> None:
90
+ """Clear the conversation history"""
91
+ self.history = []
92
 
93
+ def create_demo() -> gr.Blocks:
94
  """Create the Gradio interface"""
95
+ chat_state = ChatState()
96
+
97
+ with gr.Blocks(title="Claude Coding Assistant", theme=gr.themes.Soft()) as demo:
98
+ gr.Markdown("""
99
+ # Claude Coding Assistant 🤖💻
 
 
 
 
100
 
101
+ A specialized coding assistant powered by Claude 3.5 Sonnet. Features:
102
+ - Complete code solutions with best practices
103
+ - Detailed explanations and documentation
104
+ - Code review and optimization suggestions
105
+ - Security considerations and design patterns
106
 
107
+ Please enter your Anthropic API key to begin.
108
+ """)
109
 
110
+ # API Key input section
 
 
 
 
111
  with gr.Row():
112
  api_key_input = gr.Textbox(
113
+ label="Anthropic API Key",
114
+ placeholder="Enter your API key...",
115
+ type="password",
116
+ scale=4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  )
118
+ validate_btn = gr.Button("Validate API Key", scale=1)
119
+ status_text = gr.Textbox(label="Status", interactive=False, scale=2)
120
+
121
+ # Chat interface
122
+ chatbot = gr.Chatbot(
123
+ height=600,
124
+ show_label=False,
125
+ container=True,
126
+ show_copy_button=True
127
+ )
128
 
129
  with gr.Row():
130
+ message_input = gr.Textbox(
131
+ label="Your message",
132
+ placeholder="Ask me about coding, software design, or technical problems...",
133
+ lines=3,
134
+ scale=4
135
  )
136
+ submit_btn = gr.Button("Send", scale=1)
137
+ clear_btn = gr.Button("Clear Chat", scale=1)
138
+
139
+ # Event handlers
140
+ def validate_api_key(api_key: str) -> str:
141
+ success, message = chat_state.initialize_client(api_key)
142
+ return message
143
+
144
+ def respond(message: str, history: List[Tuple[str, str]]) -> Tuple[List[Tuple[str, str]], str]:
145
+ if not chat_state.client:
146
+ return history + [("", "Please validate your API key first.")], ""
147
+
148
+ response = chat_state.generate_response(message)
149
+ chat_state.history = history + [(message, response)]
150
+ return chat_state.history, ""
151
+
152
+ def clear_chat() -> Tuple[List[Tuple[str, str]], str]:
153
+ chat_state.clear_history()
154
+ return [], ""
155
+
156
+ # Connect event handlers
157
+ validate_btn.click(
158
+ validate_api_key,
159
+ inputs=[api_key_input],
160
+ outputs=[status_text]
161
+ )
162
+
163
  submit_btn.click(
164
+ respond,
165
+ inputs=[message_input, chatbot],
166
+ outputs=[chatbot, message_input]
167
+ )
168
+
169
+ message_input.submit(
170
+ respond,
171
+ inputs=[message_input, chatbot],
172
+ outputs=[chatbot, message_input]
173
  )
 
 
174
 
175
+ clear_btn.click(
176
+ clear_chat,
177
+ outputs=[chatbot, message_input]
 
 
 
 
 
178
  )
 
 
 
179
 
180
+ # Add examples
181
+ gr.Examples(
182
+ examples=[
183
+ "Can you help me implement a binary search tree in Python with type hints?",
184
+ "Review this code for security issues:\n```python\ndef process_user_input(data):\n result = eval(data)\n return result```",
185
+ "Explain the SOLID principles with practical examples in TypeScript.",
186
+ "How would you implement rate limiting in a REST API?",
187
+ ],
188
+ inputs=message_input,
189
+ label="Example Questions"
190
+ )
191
+
192
+ return demo
193
+
194
+ # Create and launch the demo
195
+ demo = create_demo()
196
+
197
+ # For local testing
198
  if __name__ == "__main__":
199
+ demo.launch()