bat-6 commited on
Commit
cff9d3b
·
1 Parent(s): 63df0e6

feat: implement chatbot engine for project validation, classification, and management

Browse files
src/recommendation_engine/__pycache__/chatbot_engine.cpython-313.pyc CHANGED
Binary files a/src/recommendation_engine/__pycache__/chatbot_engine.cpython-313.pyc and b/src/recommendation_engine/__pycache__/chatbot_engine.cpython-313.pyc differ
 
src/recommendation_engine/__pycache__/full_project_generator.cpython-313.pyc CHANGED
Binary files a/src/recommendation_engine/__pycache__/full_project_generator.cpython-313.pyc and b/src/recommendation_engine/__pycache__/full_project_generator.cpython-313.pyc differ
 
src/recommendation_engine/__pycache__/llm_router.cpython-313.pyc CHANGED
Binary files a/src/recommendation_engine/__pycache__/llm_router.cpython-313.pyc and b/src/recommendation_engine/__pycache__/llm_router.cpython-313.pyc differ
 
src/recommendation_engine/chatbot_engine.py CHANGED
@@ -506,6 +506,8 @@ def chatbot(user_id: str, user_input: str):
506
  domain = analysis.get("domain")
507
  project_title = analysis.get("project_title")
508
  number = analysis.get("number")
 
 
509
 
510
  if action == "reply_directly":
511
  if project_title and not state.get("project_title"):
@@ -513,11 +515,25 @@ def chatbot(user_id: str, user_input: str):
513
  if domain and not state.get("domain"):
514
  state["domain"] = domain
515
 
 
 
 
 
 
 
 
 
 
 
516
  save_user_memory(user_id, {"history": history, "state": state})
517
 
 
 
 
 
518
  return finalize_response(
519
  user_input,
520
- reply_text or "I didn't quite catch that. Can you clarify?",
521
  history,
522
  state,
523
  user_id
@@ -529,6 +545,8 @@ def chatbot(user_id: str, user_input: str):
529
  if domain_lower in ["other", "others", "general", "any"]:
530
  state["domain"] = "general"
531
  state["waiting_for_domain"] = False
 
 
532
  else:
533
  state["domain"] = domain
534
  state["waiting_for_domain"] = False
@@ -629,11 +647,16 @@ def chatbot(user_id: str, user_input: str):
629
  )
630
  state["features"] = feature_result.get("recommended_features", [])
631
 
 
 
 
632
  result = generate_full_project(
633
  title=state.get("project_title"),
634
  features=state.get("features", []),
635
- description=state.get("description", ""),
636
- abstract=state.get("abstract", "")
 
 
637
  )
638
 
639
  state = update_state(state, result, mode="merge")
@@ -684,5 +707,16 @@ def chatbot(user_id: str, user_input: str):
684
  save_user_memory(user_id, {"history": history, "state": state})
685
  return finalize_response(user_input, "No problem! Let's try something else.", history, state, user_id)
686
 
 
 
 
 
 
 
 
 
 
 
 
687
  else:
688
  return finalize_response(user_input, "I am not sure how to handle that.", history, state, user_id)
 
506
  domain = analysis.get("domain")
507
  project_title = analysis.get("project_title")
508
  number = analysis.get("number")
509
+ abstract = analysis.get("abstract")
510
+ description = analysis.get("description")
511
 
512
  if action == "reply_directly":
513
  if project_title and not state.get("project_title"):
 
515
  if domain and not state.get("domain"):
516
  state["domain"] = domain
517
 
518
+ custom_saved = False
519
+ if abstract:
520
+ state["abstract"] = abstract
521
+ state["custom_abstract"] = True
522
+ custom_saved = True
523
+ if description:
524
+ state["description"] = description
525
+ state["custom_description"] = True
526
+ custom_saved = True
527
+
528
  save_user_memory(user_id, {"history": history, "state": state})
529
 
530
+ final_reply = reply_text or "I didn't quite catch that. Can you clarify?"
531
+ if custom_saved:
532
+ final_reply = "✅ I have saved your custom project details!\n\n" + final_reply
533
+
534
  return finalize_response(
535
  user_input,
536
+ final_reply,
537
  history,
538
  state,
539
  user_id
 
545
  if domain_lower in ["other", "others", "general", "any"]:
546
  state["domain"] = "general"
547
  state["waiting_for_domain"] = False
548
+ elif domain_lower in ["domain", "domains", "list", "options", "help"]:
549
+ state["domain"] = None
550
  else:
551
  state["domain"] = domain
552
  state["waiting_for_domain"] = False
 
647
  )
648
  state["features"] = feature_result.get("recommended_features", [])
649
 
650
+ custom_desc = state.get("custom_description", False)
651
+ custom_abs = state.get("custom_abstract", False)
652
+
653
  result = generate_full_project(
654
  title=state.get("project_title"),
655
  features=state.get("features", []),
656
+ description=state.get("description", "") if custom_desc else "",
657
+ abstract=state.get("abstract", "") if custom_abs else "",
658
+ custom_description=custom_desc,
659
+ custom_abstract=custom_abs
660
  )
661
 
662
  state = update_state(state, result, mode="merge")
 
707
  save_user_memory(user_id, {"history": history, "state": state})
708
  return finalize_response(user_input, "No problem! Let's try something else.", history, state, user_id)
709
 
710
+ elif action == "clear_session":
711
+ state = default_state()
712
+ save_user_memory(user_id, {"history": history, "state": state})
713
+ return finalize_response(
714
+ user_input,
715
+ "✅ Session cleared! We are starting fresh. How can I help you today?",
716
+ history,
717
+ state,
718
+ user_id
719
+ )
720
+
721
  else:
722
  return finalize_response(user_input, "I am not sure how to handle that.", history, state, user_id)
src/recommendation_engine/full_project_generator.py CHANGED
@@ -72,7 +72,9 @@ def generate_full_project(
72
  title,
73
  features,
74
  description="",
75
- abstract=""
 
 
76
  ):
77
 
78
  context = {
@@ -100,10 +102,10 @@ def generate_full_project(
100
  extract_section(raw, "CATEGORY"),
101
 
102
  "abstract":
103
- extract_section(raw, "ABSTRACT"),
104
 
105
  "description":
106
- extract_section(raw, "DESCRIPTION"),
107
 
108
 
109
 
 
72
  title,
73
  features,
74
  description="",
75
+ abstract="",
76
+ custom_description=False,
77
+ custom_abstract=False
78
  ):
79
 
80
  context = {
 
102
  extract_section(raw, "CATEGORY"),
103
 
104
  "abstract":
105
+ abstract if custom_abstract else extract_section(raw, "ABSTRACT"),
106
 
107
  "description":
108
+ description if custom_description else extract_section(raw, "DESCRIPTION"),
109
 
110
 
111
 
src/recommendation_engine/llm_router.py CHANGED
@@ -16,7 +16,7 @@ def word_to_number(text: str):
16
  text = text.lower()
17
 
18
  for word, num in mapping.items():
19
- if word in text:
20
  return num
21
 
22
  nums = re.findall(r'\d+', text)
@@ -63,12 +63,16 @@ Evaluate the input and choose exactly ONE action from the following list:
63
  4. "trigger_full_project_generation": Choose this if the user wants to generate the complete project specification.
64
  - Example: "generate full project", "fill all fields", or "2" (if selecting option 2 to generate the full project).
65
 
 
 
 
66
  5. "confirmation_yes": Choose this if the user is saying yes/confirming a prompt.
67
  6. "confirmation_no": Choose this if the user is saying no/declining.
68
 
69
  CRITICAL INSTRUCTIONS:
70
  - You are the orchestrator. If a request is invalid, missing context, or vague, use "reply_directly" to ask the user to clarify. Do NOT trigger a generation task if the input is bad.
71
  - NEVER choose "trigger_feature_generation" if the project title is just a single word, a brand name, or lacks technical description (e.g. "Fixy", "My Project", "Test"). You MUST choose "reply_directly" and ask the user to provide a descriptive technical title that explains what the system actually does.
 
72
  - Return ONLY a valid JSON object. Do NOT include markdown blocks.
73
 
74
  JSON Format:
@@ -76,7 +80,9 @@ JSON Format:
76
  "action": "exact_action_name",
77
  "reply_text": "text to reply to the user (ONLY if action is reply_directly, otherwise null)",
78
  "extracted_domain": "extract_domain_if_mentioned_else_null",
79
- "extracted_title": "extract_project_title_if_mentioned_else_null"
 
 
80
  }}
81
  """
82
  try:
@@ -99,12 +105,16 @@ JSON Format:
99
  reply_text = parsed.get("reply_text")
100
  domain = parsed.get("extracted_domain") or extract_domain(text)
101
  project_title = parsed.get("extracted_title")
 
 
102
 
103
  return {
104
  "action": action,
105
  "reply_text": reply_text,
106
  "domain": domain,
107
  "project_title": project_title,
 
 
108
  "number": number
109
  }
110
  except Exception as e:
@@ -115,11 +125,15 @@ JSON Format:
115
  action = "reply_directly"
116
  reply_text = None
117
  project_title = None
 
 
118
 
119
  return {
120
  "action": action,
121
  "reply_text": reply_text,
122
  "domain": domain,
123
  "project_title": project_title,
 
 
124
  "number": number
125
  }
 
16
  text = text.lower()
17
 
18
  for word, num in mapping.items():
19
+ if re.search(rf"\b{word}\b", text):
20
  return num
21
 
22
  nums = re.findall(r'\d+', text)
 
63
  4. "trigger_full_project_generation": Choose this if the user wants to generate the complete project specification.
64
  - Example: "generate full project", "fill all fields", or "2" (if selecting option 2 to generate the full project).
65
 
66
+ 5. "clear_session": Choose this if the user explicitly asks to start over, clear the chat, start a new project, reset the memory, or forget the current project.
67
+ - Example: "restart", "start over", "clear memory", "let's do a different project".
68
+
69
  5. "confirmation_yes": Choose this if the user is saying yes/confirming a prompt.
70
  6. "confirmation_no": Choose this if the user is saying no/declining.
71
 
72
  CRITICAL INSTRUCTIONS:
73
  - You are the orchestrator. If a request is invalid, missing context, or vague, use "reply_directly" to ask the user to clarify. Do NOT trigger a generation task if the input is bad.
74
  - NEVER choose "trigger_feature_generation" if the project title is just a single word, a brand name, or lacks technical description (e.g. "Fixy", "My Project", "Test"). You MUST choose "reply_directly" and ask the user to provide a descriptive technical title that explains what the system actually does.
75
+ - If the user provides a summary or context about their project, extract it into "extracted_abstract". If they provide a detailed breakdown or scope, extract it into "extracted_description".
76
  - Return ONLY a valid JSON object. Do NOT include markdown blocks.
77
 
78
  JSON Format:
 
80
  "action": "exact_action_name",
81
  "reply_text": "text to reply to the user (ONLY if action is reply_directly, otherwise null)",
82
  "extracted_domain": "extract_domain_if_mentioned_else_null",
83
+ "extracted_title": "extract_project_title_if_mentioned_else_null",
84
+ "extracted_abstract": "extract_abstract_if_user_provides_one_else_null",
85
+ "extracted_description": "extract_description_if_user_provides_one_else_null"
86
  }}
87
  """
88
  try:
 
105
  reply_text = parsed.get("reply_text")
106
  domain = parsed.get("extracted_domain") or extract_domain(text)
107
  project_title = parsed.get("extracted_title")
108
+ abstract = parsed.get("extracted_abstract")
109
+ description = parsed.get("extracted_description")
110
 
111
  return {
112
  "action": action,
113
  "reply_text": reply_text,
114
  "domain": domain,
115
  "project_title": project_title,
116
+ "abstract": abstract,
117
+ "description": description,
118
  "number": number
119
  }
120
  except Exception as e:
 
125
  action = "reply_directly"
126
  reply_text = None
127
  project_title = None
128
+ abstract = None
129
+ description = None
130
 
131
  return {
132
  "action": action,
133
  "reply_text": reply_text,
134
  "domain": domain,
135
  "project_title": project_title,
136
+ "abstract": abstract,
137
+ "description": description,
138
  "number": number
139
  }