sebastianfrench commited on
Commit
ef61e79
·
1 Parent(s): 81917a3
.gitignore ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ .vscode
2
+ .venv
agents/__init__.py ADDED
File without changes
agents/agent.py ADDED
@@ -0,0 +1,239 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from typing import TypedDict, List, Dict, Any, Optional
3
+ from langgraph.graph import StateGraph, START, END
4
+ from langchain_anthropic import ChatAnthropic
5
+ from langchain_groq import ChatGroq
6
+ from langchain_core.messages import HumanMessage
7
+ import getpass
8
+
9
+ if "ANTHROPIC_API_KEY" not in os.environ:
10
+ os.environ["ANTHROPIC_API_KEY"] = getpass.getpass("Enter your Anthropic API key: ")
11
+
12
+ class EmailState(TypedDict):
13
+ # The email being processed
14
+ email: Dict[str, Any] # Contains subject, sender, body, etc.
15
+
16
+ # Category of the email (inquiry, complaint, etc.)
17
+ email_category: Optional[str]
18
+
19
+ # Reason why the email was marked as spam
20
+ spam_reason: Optional[str]
21
+
22
+ # Analysis and decisions
23
+ is_spam: Optional[bool]
24
+
25
+ # Response generation
26
+ email_draft: Optional[str]
27
+
28
+ # Processing metadata
29
+ messages: List[Dict[str, Any]] # Track conversation with LLM for analysis
30
+
31
+ # Initialize our LLM
32
+ model = ChatAnthropic(
33
+ model="claude-3-5-haiku-latest",
34
+ temperature=0
35
+ )
36
+
37
+ def read_email(state: EmailState):
38
+ """Alfred reads and logs the incoming email"""
39
+ email = state["email"]
40
+
41
+ # Here we might do some initial preprocessing
42
+ print(f"Alfred is processing an email from {email['sender']} with subject: {email['subject']}")
43
+
44
+ # No state changes needed here
45
+ return {}
46
+
47
+ def classify_email(state: EmailState):
48
+ """Alfred uses an LLM to determine if the email is spam or legitimate"""
49
+ email = state["email"]
50
+
51
+ # Prepare our prompt for the LLM
52
+ prompt = f"""
53
+ As Alfred the butler, analyze this email and determine if it is spam or legitimate.
54
+
55
+ Email:
56
+ From: {email['sender']}
57
+ Subject: {email['subject']}
58
+ Body: {email['body']}
59
+
60
+ First, determine if this email is spam. If it is spam, explain why.
61
+ If it is legitimate, categorize it (inquiry, complaint, thank you, etc.).
62
+ """
63
+
64
+ # Call the LLM
65
+ messages = [HumanMessage(content=prompt)]
66
+ response = model.invoke(messages)
67
+
68
+ # Simple logic to parse the response (in a real app, you'd want more robust parsing)
69
+ response_text = response.content.lower()
70
+ is_spam = "spam" in response_text and "not spam" not in response_text
71
+
72
+ # Extract a reason if it's spam
73
+ spam_reason = None
74
+ if is_spam and "reason:" in response_text:
75
+ spam_reason = response_text.split("reason:")[1].strip()
76
+
77
+ # Determine category if legitimate
78
+ email_category = None
79
+ if not is_spam:
80
+ categories = ["inquiry", "complaint", "thank you", "request", "information"]
81
+ for category in categories:
82
+ if category in response_text:
83
+ email_category = category
84
+ break
85
+
86
+ # Update messages for tracking
87
+ new_messages = state.get("messages", []) + [
88
+ {"role": "user", "content": prompt},
89
+ {"role": "assistant", "content": response.content}
90
+ ]
91
+
92
+ # Return state updates
93
+ return {
94
+ "is_spam": is_spam,
95
+ "spam_reason": spam_reason,
96
+ "email_category": email_category,
97
+ "messages": new_messages
98
+ }
99
+
100
+ def handle_spam(state: EmailState):
101
+ """Alfred discards spam email with a note"""
102
+ print(f"Alfred has marked the email as spam. Reason: {state['spam_reason']}")
103
+ print("The email has been moved to the spam folder.")
104
+
105
+ # We're done processing this email
106
+ return {}
107
+
108
+ def draft_response(state: EmailState):
109
+ """Alfred drafts a preliminary response for legitimate emails"""
110
+ email = state["email"]
111
+ category = state["email_category"] or "general"
112
+
113
+ # Prepare our prompt for the LLM
114
+ prompt = f"""
115
+ As Alfred the butler, draft a polite preliminary response to this email.
116
+
117
+ Email:
118
+ From: {email['sender']}
119
+ Subject: {email['subject']}
120
+ Body: {email['body']}
121
+
122
+ This email has been categorized as: {category}
123
+
124
+ Draft a brief, professional response that Mr. Hugg can review and personalize before sending.
125
+ """
126
+
127
+ # Call the LLM
128
+ messages = [HumanMessage(content=prompt)]
129
+ response = model.invoke(messages)
130
+
131
+ # Update messages for tracking
132
+ new_messages = state.get("messages", []) + [
133
+ {"role": "user", "content": prompt},
134
+ {"role": "assistant", "content": response.content}
135
+ ]
136
+
137
+ # Return state updates
138
+ return {
139
+ "email_draft": response.content,
140
+ "messages": new_messages
141
+ }
142
+
143
+ def notify_mr_hugg(state: EmailState):
144
+ """Alfred notifies Mr. Hugg about the email and presents the draft response"""
145
+ email = state["email"]
146
+
147
+ print("\n" + "="*50)
148
+ print(f"Sir, you've received an email from {email['sender']}.")
149
+ print(f"Subject: {email['subject']}")
150
+ print(f"Category: {state['email_category']}")
151
+ print("\nI've prepared a draft response for your review:")
152
+ print("-"*50)
153
+ print(state["email_draft"])
154
+ print("="*50 + "\n")
155
+
156
+ # We're done processing this email
157
+ return {}
158
+
159
+
160
+ def route_email(state: EmailState) -> str:
161
+ """Determine the next step based on spam classification"""
162
+ if state["is_spam"]:
163
+ return "spam"
164
+ else:
165
+ return "legitimate"
166
+
167
+ # Create the graph
168
+ email_graph = StateGraph(EmailState)
169
+
170
+ # Add nodes
171
+ email_graph.add_node("read_email", read_email)
172
+ email_graph.add_node("classify_email", classify_email)
173
+ email_graph.add_node("handle_spam", handle_spam)
174
+ email_graph.add_node("draft_response", draft_response)
175
+ email_graph.add_node("notify_mr_hugg", notify_mr_hugg)
176
+
177
+ # Start the edges
178
+ email_graph.add_edge(START, "read_email")
179
+ # Add edges - defining the flow
180
+ email_graph.add_edge("read_email", "classify_email")
181
+
182
+ # Add conditional branching from classify_email
183
+ email_graph.add_conditional_edges(
184
+ "classify_email",
185
+ route_email,
186
+ {
187
+ "spam": "handle_spam",
188
+ "legitimate": "draft_response"
189
+ }
190
+ )
191
+
192
+ # Add the final edges
193
+ email_graph.add_edge("handle_spam", END)
194
+ email_graph.add_edge("draft_response", "notify_mr_hugg")
195
+ email_graph.add_edge("notify_mr_hugg", END)
196
+
197
+ # Compile the graph
198
+ compiled_graph = email_graph.compile()
199
+
200
+
201
+ # Example legitimate email
202
+ legitimate_email = {
203
+ "sender": "john.smith@example.com",
204
+ "subject": "Question about your services",
205
+ "body": "Dear Mr. Hugg, I was referred to you by a colleague and I'm interested in learning more about your consulting services. Could we schedule a call next week? Best regards, John Smith"
206
+ }
207
+
208
+ # Example spam email
209
+ spam_email = {
210
+ "sender": "winner@lottery-intl.com",
211
+ "subject": "YOU HAVE WON $5,000,000!!!",
212
+ "body": "CONGRATULATIONS! You have been selected as the winner of our international lottery! To claim your $5,000,000 prize, please send us your bank details and a processing fee of $100."
213
+ }
214
+
215
+ # Process the legitimate email
216
+ print("\nProcessing legitimate email...")
217
+ legitimate_result = compiled_graph.invoke({
218
+ "email": legitimate_email,
219
+ "is_spam": None,
220
+ "spam_reason": None,
221
+ "email_category": None,
222
+ "email_draft": None,
223
+ "messages": []
224
+ })
225
+
226
+ # Process the spam email
227
+ print("\nProcessing spam email...")
228
+ spam_result = compiled_graph.invoke({
229
+ "email": spam_email,
230
+ "is_spam": None,
231
+ "spam_reason": None,
232
+ "email_category": None,
233
+ "email_draft": None,
234
+ "messages": []
235
+ })
236
+
237
+ print("\nAll emails processed.")
238
+ print("Legitimate email result:", legitimate_result)
239
+ print("Spam email result:", spam_result)
agents/graphs/__init__.py ADDED
File without changes
agents/tools/__init__.py ADDED
File without changes
agents/tools/search.py ADDED
File without changes
app.py CHANGED
@@ -3,21 +3,12 @@ import gradio as gr
3
  import requests
4
  import inspect
5
  import pandas as pd
 
6
 
7
  # (Keep Constants as is)
8
  # --- Constants ---
9
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
10
 
11
- # --- Basic Agent Definition ---
12
- # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
13
- class BasicAgent:
14
- def __init__(self):
15
- print("BasicAgent initialized.")
16
- def __call__(self, question: str) -> str:
17
- print(f"Agent received question (first 50 chars): {question[:50]}...")
18
- fixed_answer = "This is a default answer."
19
- print(f"Agent returning fixed answer: {fixed_answer}")
20
- return fixed_answer
21
 
22
  def run_and_submit_all( profile: gr.OAuthProfile | None):
23
  """
 
3
  import requests
4
  import inspect
5
  import pandas as pd
6
+ import models.basic_agent as BasicAgent
7
 
8
  # (Keep Constants as is)
9
  # --- Constants ---
10
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
11
 
 
 
 
 
 
 
 
 
 
 
12
 
13
  def run_and_submit_all( profile: gr.OAuthProfile | None):
14
  """
models/__init__.py ADDED
File without changes
models/basic_agent.py ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ class BasicAgent:
2
+ def __init__(self):
3
+ print("BasicAgent initialized.")
4
+ def __call__(self, question: str) -> str:
5
+ print(f"Agent received question (first 50 chars): {question[:50]}...")
6
+ fixed_answer = "This is a default answer."
7
+ print(f"Agent returning fixed answer: {fixed_answer}")
8
+ return fixed_answer