Ani14 commited on
Commit
ddeba25
·
verified ·
1 Parent(s): 4f164e3

Update agent.py

Browse files
Files changed (1) hide show
  1. agent.py +47 -34
agent.py CHANGED
@@ -12,13 +12,15 @@ from openai import OpenAI
12
  from models import AgentState, Message, ExtractedIntelligence
13
 
14
  # --- Configuration ---
15
- CALLBACK_URL = "https://hackathon.guvi.in/api/updateHoneyPotFinalResult"
16
  HONEYPOT_API_KEY = os.environ.get("HONEYPOT_API_KEY", "sk_test_123456789")
17
 
18
- # OpenRouter configuration
 
 
 
 
19
  OPENROUTER_API_KEY = os.environ.get("OPENROUTER_API_KEY")
20
- # Using a highly capable model for complex reasoning
21
- OPENROUTER_MODEL = os.environ.get("OPENROUTER_MODEL", "openrouter/free")
22
 
23
  client = OpenAI(
24
  base_url="https://openrouter.ai/api/v1",
@@ -26,21 +28,29 @@ client = OpenAI(
26
  )
27
 
28
  def call_openrouter(messages: List[Dict[str, str]], max_tokens: int = 512) -> str:
29
- """Call the OpenRouter API to generate a text response."""
 
 
 
 
30
  if not OPENROUTER_API_KEY:
31
  raise ValueError("OPENROUTER_API_KEY is not set.")
32
-
33
- try:
34
- response = client.chat.completions.create(
35
- model=OPENROUTER_MODEL,
36
- messages=messages,
37
- max_tokens=max_tokens,
38
- extra_body={"reasoning": {"enabled": True}}
39
- )
40
- return response.choices[0].message.content
41
- except Exception as e:
42
- print(f"OpenRouter API error: {e}")
43
- raise
 
 
 
 
44
 
45
  # --- LangGraph Nodes (Functions) ---
46
 
@@ -50,7 +60,7 @@ def detect_scam(state: AgentState) -> AgentState:
50
  text = latest_message.text
51
  is_scam = False
52
  reason = "No scam indicators found"
53
-
54
  # Detailed prompt focusing on specific Indian scam vectors and subtle indicators
55
  prompt = (
56
  "You are a Senior Fraud Analyst specializing in Indian Cybercrime patterns. "
@@ -68,12 +78,13 @@ def detect_scam(state: AgentState) -> AgentState:
68
  "Respond ONLY in the format 'true|<reason>' if it is a scam or 'false|<reason>' if not. "
69
  f"Message: {text}"
70
  )
71
-
72
  try:
73
  response = call_openrouter([{"role": "user", "content": prompt}], max_tokens=150)
74
  resp = (response or "").strip()
75
  if not resp:
76
  raise ValueError("OpenRouter returned empty response")
 
77
  first_line = resp.splitlines()[0].strip()
78
 
79
  # Expected format: true|<reason> or false|<reason>
@@ -99,8 +110,9 @@ def detect_scam(state: AgentState) -> AgentState:
99
 
100
  if not reason:
101
  reason = "OpenRouter classification did not provide a reason"
 
102
  except Exception as e:
103
- print(f"OpenRouter classification error: {e}. Falling back to heuristic.")
104
  lower_text = text.lower()
105
  scam_keywords = [
106
  "bank", "account", "blocked", "verify", "otp", "password", "upi", "urgent", "link", "update",
@@ -112,7 +124,7 @@ def detect_scam(state: AgentState) -> AgentState:
112
  is_scam = True
113
  reason = f"Keyword '{kw}' found in message (fallback)"
114
  break
115
-
116
  state["scamDetected"] = is_scam
117
  if "agentNotes" not in state:
118
  state["agentNotes"] = ""
@@ -127,7 +139,7 @@ def agent_persona_response(state: AgentState) -> AgentState:
127
  return state
128
 
129
  latest_text = state["conversationHistory"][-1].text
130
-
131
  # Detailed prompt for persona engagement
132
  prompt = (
133
  "You are an AI Honeypot Agent. Your goal is to keep a scammer engaged to extract intelligence (UPI IDs, Bank Accounts, Links, Phone Numbers). "
@@ -140,13 +152,16 @@ def agent_persona_response(state: AgentState) -> AgentState:
140
  f"Scammer's latest message: {latest_text}\n\n"
141
  "Respond in under 40 words. Be polite and encouraging."
142
  )
143
-
144
  try:
145
  response_text = call_openrouter([{"role": "user", "content": prompt}], max_tokens=150)
146
  response_text = response_text.strip().split('\n')[0]
147
- except Exception as e:
148
- print(f"OpenRouter persona generation error: {e}. Falling back to heuristic.")
149
- response_text = "Sir, I am trying to do as you said but it is not working. Can you please guide me again? I don't want my account to be blocked."
 
 
 
150
 
151
  agent_message = Message(
152
  sender="user",
@@ -349,8 +364,8 @@ def decide_engagement_end(state: AgentState) -> AgentState:
349
  continue_engagement = True
350
 
351
  # End if we have gathered significant actionable intelligence
352
- if (len(intelligence.bankAccounts) > 0 or
353
- len(intelligence.upiIds) > 0 or
354
  len(intelligence.phishingLinks) > 0):
355
  continue_engagement = False
356
 
@@ -368,13 +383,12 @@ def decide_engagement_end(state: AgentState) -> AgentState:
368
  state["should_continue_engagement"] = continue_engagement
369
  return state
370
 
371
-
372
  def final_callback(state: AgentState) -> AgentState:
373
  """Node 5: Sends the mandatory final result callback."""
374
  # If it's a scam and we haven't sent the callback yet, send it
375
  if not state["scamDetected"] or state.get("callbackSent", False):
376
  return state
377
-
378
  intelligence = state.get("extractedIntelligence", ExtractedIntelligence())
379
  payload = {
380
  "sessionId": state.get("sessionId"),
@@ -383,12 +397,12 @@ def final_callback(state: AgentState) -> AgentState:
383
  "extractedIntelligence": intelligence.model_dump(),
384
  "agentNotes": state.get("agentNotes", "")
385
  }
386
-
387
  headers = {
388
  "Content-Type": "application/json",
389
  "x-api-key": HONEYPOT_API_KEY
390
  }
391
-
392
  try:
393
  response = requests.post(CALLBACK_URL, json=payload, headers=headers, timeout=10)
394
  response.raise_for_status()
@@ -397,11 +411,10 @@ def final_callback(state: AgentState) -> AgentState:
397
  state["agentNotes"] = ""
398
  state["agentNotes"] += "Final callback sent successfully. "
399
  except Exception as e:
400
- print(f"Final callback failed: {e}")
401
  if "agentNotes" not in state:
402
  state["agentNotes"] = ""
403
  state["agentNotes"] += f"Final callback failed: {e}. "
404
-
405
  return state
406
 
407
  def create_honeypot_graph(checkpoint_saver: BaseCheckpointSaver):
 
12
  from models import AgentState, Message, ExtractedIntelligence
13
 
14
  # --- Configuration ---
15
+ CALLBACK_URL = "https://hackathon.guvi.in/api/updateHoneyPotFinalResult"
16
  HONEYPOT_API_KEY = os.environ.get("HONEYPOT_API_KEY", "sk_test_123456789")
17
 
18
+ # Choose a specific, supported model instead of the router to avoid empty messages
19
+ # Models like anthropic/claude-3-haiku or meta-llama/llama-3.1-8b-instruct are robust for classification
20
+ OPENROUTER_MODEL = os.environ.get("OPENROUTER_MODEL", "anthropic/claude-3-haiku")
21
+
22
+ # API key for OpenRouter
23
  OPENROUTER_API_KEY = os.environ.get("OPENROUTER_API_KEY")
 
 
24
 
25
  client = OpenAI(
26
  base_url="https://openrouter.ai/api/v1",
 
28
  )
29
 
30
  def call_openrouter(messages: List[Dict[str, str]], max_tokens: int = 512) -> str:
31
+ """
32
+ Call the OpenRouter API to generate a text response.
33
+ This version avoids reasoning tokens (which can leave message.content empty)
34
+ and checks for empty content, raising an error to trigger fallback logic.
35
+ """
36
  if not OPENROUTER_API_KEY:
37
  raise ValueError("OPENROUTER_API_KEY is not set.")
38
+
39
+ response = client.chat.completions.create(
40
+ model=OPENROUTER_MODEL,
41
+ messages=messages,
42
+ max_tokens=max_tokens
43
+ # No extra_body to avoid reasoning tokens; reasoning can cause empty content:contentReference[oaicite:2]{index=2}
44
+ )
45
+
46
+ # Guard against empty responses: if no content, raise to trigger fallback
47
+ message_obj = response.choices[0].message
48
+ content = getattr(message_obj, "content", "")
49
+ if not content or not content.strip():
50
+ # Optionally, you could parse reasoning_details here, but that complicates your flow
51
+ raise ValueError("Empty model response from OpenRouter; check model or parameters.")
52
+
53
+ return content
54
 
55
  # --- LangGraph Nodes (Functions) ---
56
 
 
60
  text = latest_message.text
61
  is_scam = False
62
  reason = "No scam indicators found"
63
+
64
  # Detailed prompt focusing on specific Indian scam vectors and subtle indicators
65
  prompt = (
66
  "You are a Senior Fraud Analyst specializing in Indian Cybercrime patterns. "
 
78
  "Respond ONLY in the format 'true|<reason>' if it is a scam or 'false|<reason>' if not. "
79
  f"Message: {text}"
80
  )
81
+
82
  try:
83
  response = call_openrouter([{"role": "user", "content": prompt}], max_tokens=150)
84
  resp = (response or "").strip()
85
  if not resp:
86
  raise ValueError("OpenRouter returned empty response")
87
+
88
  first_line = resp.splitlines()[0].strip()
89
 
90
  # Expected format: true|<reason> or false|<reason>
 
110
 
111
  if not reason:
112
  reason = "OpenRouter classification did not provide a reason"
113
+
114
  except Exception as e:
115
+ # Fallback heuristic if the model fails or returns empty content
116
  lower_text = text.lower()
117
  scam_keywords = [
118
  "bank", "account", "blocked", "verify", "otp", "password", "upi", "urgent", "link", "update",
 
124
  is_scam = True
125
  reason = f"Keyword '{kw}' found in message (fallback)"
126
  break
127
+
128
  state["scamDetected"] = is_scam
129
  if "agentNotes" not in state:
130
  state["agentNotes"] = ""
 
139
  return state
140
 
141
  latest_text = state["conversationHistory"][-1].text
142
+
143
  # Detailed prompt for persona engagement
144
  prompt = (
145
  "You are an AI Honeypot Agent. Your goal is to keep a scammer engaged to extract intelligence (UPI IDs, Bank Accounts, Links, Phone Numbers). "
 
152
  f"Scammer's latest message: {latest_text}\n\n"
153
  "Respond in under 40 words. Be polite and encouraging."
154
  )
155
+
156
  try:
157
  response_text = call_openrouter([{"role": "user", "content": prompt}], max_tokens=150)
158
  response_text = response_text.strip().split('\n')[0]
159
+ except Exception:
160
+ # Fallback phrase if the model fails or returns empty content
161
+ response_text = (
162
+ "Sir, I am trying to do as you said but it is not working. "
163
+ "Can you please guide me again? I don't want my account to be blocked."
164
+ )
165
 
166
  agent_message = Message(
167
  sender="user",
 
364
  continue_engagement = True
365
 
366
  # End if we have gathered significant actionable intelligence
367
+ if (len(intelligence.bankAccounts) > 0 or
368
+ len(intelligence.upiIds) > 0 or
369
  len(intelligence.phishingLinks) > 0):
370
  continue_engagement = False
371
 
 
383
  state["should_continue_engagement"] = continue_engagement
384
  return state
385
 
 
386
  def final_callback(state: AgentState) -> AgentState:
387
  """Node 5: Sends the mandatory final result callback."""
388
  # If it's a scam and we haven't sent the callback yet, send it
389
  if not state["scamDetected"] or state.get("callbackSent", False):
390
  return state
391
+
392
  intelligence = state.get("extractedIntelligence", ExtractedIntelligence())
393
  payload = {
394
  "sessionId": state.get("sessionId"),
 
397
  "extractedIntelligence": intelligence.model_dump(),
398
  "agentNotes": state.get("agentNotes", "")
399
  }
400
+
401
  headers = {
402
  "Content-Type": "application/json",
403
  "x-api-key": HONEYPOT_API_KEY
404
  }
405
+
406
  try:
407
  response = requests.post(CALLBACK_URL, json=payload, headers=headers, timeout=10)
408
  response.raise_for_status()
 
411
  state["agentNotes"] = ""
412
  state["agentNotes"] += "Final callback sent successfully. "
413
  except Exception as e:
 
414
  if "agentNotes" not in state:
415
  state["agentNotes"] = ""
416
  state["agentNotes"] += f"Final callback failed: {e}. "
417
+
418
  return state
419
 
420
  def create_honeypot_graph(checkpoint_saver: BaseCheckpointSaver):