Akashmj22122002 commited on
Commit
93ef338
·
verified ·
1 Parent(s): a524489

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +599 -61
app.py CHANGED
@@ -302,7 +302,535 @@
302
  # # gr.ChatInterface(me.chat).launch()
303
 
304
 
305
- # app.py
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
306
  from dotenv import load_dotenv
307
  from openai import OpenAI
308
  import json
@@ -315,14 +843,22 @@ import time
315
 
316
  load_dotenv(override=True)
317
 
318
- # --- CONFIG ---
319
- GEMINI_BASE_URL = "https://generativelanguage.googleapis.com/v1beta/openai/"
320
- google_api_key = os.getenv("GOOGLE_API_KEY")
 
 
 
 
 
 
321
 
322
- # Initialize Gemini client (using OpenAI wrapper you used earlier)
323
- gemini = OpenAI(base_url=GEMINI_BASE_URL, api_key=google_api_key)
324
 
325
- # --- Pushover helper ---
 
 
326
  def push(text):
327
  token = os.getenv("PUSHOVER_TOKEN")
328
  user = os.getenv("PUSHOVER_USER")
@@ -338,14 +874,15 @@ def push(text):
338
  except Exception as e:
339
  print("Pushover error:", e)
340
 
341
- # --- Tools (actual implementations) ---
 
 
342
  def record_user_details(email, name="Name not provided", notes="not provided"):
343
  push(f"Recording contact: {name} <{email}> notes: {notes}")
344
  return {"recorded": "ok", "email": email, "name": name}
345
 
346
  def record_unknown_question(question):
347
  push(f"Unknown question recorded: {question}")
348
- # Optionally write to a local file for audits
349
  os.makedirs("me/logs", exist_ok=True)
350
  with open("me/logs/unknown_questions.txt", "a", encoding="utf-8") as f:
351
  f.write(question.strip() + "\n")
@@ -362,7 +899,7 @@ def search_faq(query):
362
  conn.close()
363
  return {"answer": row[0]} if row else {"answer": "not found"}
364
 
365
- # --- Tool JSON metadata (for function-calling style) ---
366
  record_user_details_json = {
367
  "name": "record_user_details",
368
  "description": "Record an interested user's email and optional name/notes.",
@@ -410,13 +947,15 @@ tools = [
410
  {"type": "function", "function": search_faq_json}
411
  ]
412
 
413
- # --- The assistant class ---
 
 
414
  class Me:
415
  def __init__(self):
416
- self.openai = gemini
417
  self.name = "AKASH M J"
418
 
419
- # Load profile PDF into self.linkedin
420
  self.linkedin = ""
421
  try:
422
  reader = PdfReader(os.path.join("me", "Profile.pdf"))
@@ -435,7 +974,7 @@ class Me:
435
  print("Could not read summary.txt:", e)
436
  self.summary = ""
437
 
438
- # Load knowledge files (RAG-style simple concatenation)
439
  self.knowledge = ""
440
  kb_dir = os.path.join("me", "knowledge")
441
  if os.path.exists(kb_dir):
@@ -449,13 +988,13 @@ class Me:
449
 
450
  def system_prompt(self):
451
  system_prompt = (
452
- f"You are acting as {self.name}. Answer questions about {self.name}'s background "
453
- "and experience using the context provided. Be professional and concise. "
454
- "If you don't know an answer, use the record_unknown_question tool."
455
  )
456
  system_prompt += f"\n\n## Summary:\n{self.summary}\n\n"
457
- system_prompt += f"## LinkedIn profile (extracted):\n{self.linkedin}\n\n"
458
- system_prompt += f"## Knowledge base:\n{self.knowledge}\n\n"
459
  return system_prompt
460
 
461
  def handle_tool_call(self, tool_calls):
@@ -476,95 +1015,94 @@ class Me:
476
  })
477
  return results
478
 
479
- # Simple router/orchestrator: route common queries to the FAQ or to the LLM
480
  def route_question(self, question):
481
  q = question.lower()
482
- # keywords that map to FAQ
483
- faq_keywords = ["project", "tech stack", "stack", "skill", "skills", "study", "education", "experience"]
484
  if any(k in q for k in faq_keywords):
485
  return "search_faq"
486
  return None
487
 
 
488
  def evaluate_answer(self, user_question, ai_answer):
489
- # Simple evaluator: ask the LLM to judge the quality
490
  eval_prompt = f"""
491
- You are an evaluator. Judge whether the assistant reply is clear, correct, and complete for the user question.
492
- Return exactly PASS or FAIL and a one-line reason.
493
 
494
- User question:
495
- {user_question}
496
-
497
- Assistant reply:
498
- {ai_answer}
499
  """
500
  try:
501
  ev = self.openai.chat.completions.create(
502
- model="gemini-2.0-flash",
503
- messages=[{"role":"system","content":"You are an evaluator."},
504
- {"role":"user","content":eval_prompt}]
 
 
505
  )
506
  text = ev.choices[0].message.content.strip()
507
- # very simple parse
508
  if text.upper().startswith("PASS"):
509
- return {"result":"PASS", "note": text}
510
  else:
511
- return {"result":"FAIL", "note": text}
512
  except Exception as e:
513
- print("Evaluator failed:", e)
514
- return {"result":"UNKNOWN", "note": str(e)}
515
 
 
516
  def chat(self, message, history):
517
- # build messages with system prompt + history + user
518
  messages = [{"role":"system","content":self.system_prompt()}] + history + [{"role":"user","content":message}]
519
 
520
- # 1) Router: check if the question should use the FAQ tool
521
  tool_to_use = self.route_question(message)
522
  if tool_to_use == "search_faq":
523
- # call tool directly and return evaluated answer
524
  tool_result = search_faq(message)
525
- raw_answer = tool_result.get("answer", "I don't have that in my FAQ.")
526
- eval_res = self.evaluate_answer(message, raw_answer)
527
- if eval_res["result"] == "PASS":
528
  return raw_answer
529
- else:
530
- # fall back to LLM if FAIL
531
- pass
532
 
533
- # 2) Normal LLM flow with tools support (function-calling style)
534
  done = False
535
  while not done:
536
  response = self.openai.chat.completions.create(
537
- model="gemini-2.0-flash",
538
  messages=messages,
539
  tools=tools
540
  )
541
 
542
  finish = response.choices[0].finish_reason
543
  if finish == "tool_calls":
544
- # the LLM asked to call a tool
545
- message_obj = response.choices[0].message
546
- tool_calls = getattr(message_obj, "tool_calls", [])
547
  results = self.handle_tool_call(tool_calls)
548
- messages.append(message_obj)
549
  messages.extend(results)
550
- # loop again so the LLM can consume tool outputs
551
  else:
552
  done = True
553
 
554
  ai_answer = response.choices[0].message.content
555
- # 3) Evaluate the answer; if FAIL, ask LLM to improve
 
556
  eval_res = self.evaluate_answer(message, ai_answer)
557
  if eval_res["result"] == "FAIL":
558
- # ask the model to improve using the critique
559
- improve_prompt = f"User question:\n{message}\n\nAssistant previous reply:\n{ai_answer}\n\nEvaluator note:\n{eval_res['note']}\n\nPlease produce an improved concise answer."
560
- messages.append({"role":"user","content":improve_prompt})
561
- improved_resp = self.openai.chat.completions.create(model="gemini-2.0-flash", messages=messages)
 
 
 
 
 
 
562
  ai_answer = improved_resp.choices[0].message.content
563
 
564
  return ai_answer
565
 
566
- # --- Launch ---
 
 
567
  if __name__ == "__main__":
568
  me = Me()
569
  # gr.ChatInterface(me.chat, type="messages").launch()
570
- gr.ChatInterface(me.chat).launch()
 
302
  # # gr.ChatInterface(me.chat).launch()
303
 
304
 
305
+
306
+
307
+ # working perfectly one
308
+ # # app.py
309
+ # from dotenv import load_dotenv
310
+ # from openai import OpenAI
311
+ # import json
312
+ # import os
313
+ # import requests
314
+ # from pypdf import PdfReader
315
+ # import gradio as gr
316
+ # import sqlite3
317
+ # import time
318
+
319
+ # load_dotenv(override=True)
320
+
321
+ # # --- CONFIG ---
322
+ # GEMINI_BASE_URL = "https://generativelanguage.googleapis.com/v1beta/openai/"
323
+ # google_api_key = os.getenv("GOOGLE_API_KEY")
324
+
325
+ # # Initialize Gemini client (using OpenAI wrapper you used earlier)
326
+ # gemini = OpenAI(base_url=GEMINI_BASE_URL, api_key=google_api_key)
327
+
328
+ # # --- Pushover helper ---
329
+ # def push(text):
330
+ # token = os.getenv("PUSHOVER_TOKEN")
331
+ # user = os.getenv("PUSHOVER_USER")
332
+ # if not token or not user:
333
+ # print("Pushover credentials not set. Skipping push.")
334
+ # return
335
+ # try:
336
+ # requests.post(
337
+ # "https://api.pushover.net/1/messages.json",
338
+ # data={"token": token, "user": user, "message": text},
339
+ # timeout=5
340
+ # )
341
+ # except Exception as e:
342
+ # print("Pushover error:", e)
343
+
344
+ # # --- Tools (actual implementations) ---
345
+ # def record_user_details(email, name="Name not provided", notes="not provided"):
346
+ # push(f"Recording contact: {name} <{email}> notes: {notes}")
347
+ # return {"recorded": "ok", "email": email, "name": name}
348
+
349
+ # def record_unknown_question(question):
350
+ # push(f"Unknown question recorded: {question}")
351
+ # # Optionally write to a local file for audits
352
+ # os.makedirs("me/logs", exist_ok=True)
353
+ # with open("me/logs/unknown_questions.txt", "a", encoding="utf-8") as f:
354
+ # f.write(question.strip() + "\n")
355
+ # return {"recorded": "ok", "question": question}
356
+
357
+ # def search_faq(query):
358
+ # db_path = os.path.join("me", "qa.db")
359
+ # if not os.path.exists(db_path):
360
+ # return {"answer": "FAQ database not found."}
361
+ # conn = sqlite3.connect(db_path)
362
+ # cur = conn.cursor()
363
+ # cur.execute("SELECT answer FROM faq WHERE question LIKE ? LIMIT 1", (f"%{query}%",))
364
+ # row = cur.fetchone()
365
+ # conn.close()
366
+ # return {"answer": row[0]} if row else {"answer": "not found"}
367
+
368
+ # # --- Tool JSON metadata (for function-calling style) ---
369
+ # record_user_details_json = {
370
+ # "name": "record_user_details",
371
+ # "description": "Record an interested user's email and optional name/notes.",
372
+ # "parameters": {
373
+ # "type": "object",
374
+ # "properties": {
375
+ # "email": {"type": "string"},
376
+ # "name": {"type": "string"},
377
+ # "notes": {"type": "string"}
378
+ # },
379
+ # "required": ["email"],
380
+ # "additionalProperties": False
381
+ # }
382
+ # }
383
+
384
+ # record_unknown_question_json = {
385
+ # "name": "record_unknown_question",
386
+ # "description": "Record any question the assistant could not answer.",
387
+ # "parameters": {
388
+ # "type": "object",
389
+ # "properties": {
390
+ # "question": {"type": "string"}
391
+ # },
392
+ # "required": ["question"],
393
+ # "additionalProperties": False
394
+ # }
395
+ # }
396
+
397
+ # search_faq_json = {
398
+ # "name": "search_faq",
399
+ # "description": "Search the FAQ database for a question.",
400
+ # "parameters": {
401
+ # "type": "object",
402
+ # "properties": {
403
+ # "query": {"type": "string"}
404
+ # },
405
+ # "required": ["query"],
406
+ # "additionalProperties": False
407
+ # }
408
+ # }
409
+
410
+ # tools = [
411
+ # {"type": "function", "function": record_user_details_json},
412
+ # {"type": "function", "function": record_unknown_question_json},
413
+ # {"type": "function", "function": search_faq_json}
414
+ # ]
415
+
416
+ # # --- The assistant class ---
417
+ # class Me:
418
+ # def __init__(self):
419
+ # self.openai = gemini
420
+ # self.name = "AKASH M J"
421
+
422
+ # # Load profile PDF into self.linkedin
423
+ # self.linkedin = ""
424
+ # try:
425
+ # reader = PdfReader(os.path.join("me", "Profile.pdf"))
426
+ # for page in reader.pages:
427
+ # text = page.extract_text()
428
+ # if text:
429
+ # self.linkedin += text + "\n"
430
+ # except Exception as e:
431
+ # print("Could not read Profile.pdf:", e)
432
+
433
+ # # Load summary
434
+ # try:
435
+ # with open(os.path.join("me", "summary.txt"), "r", encoding="utf-8") as f:
436
+ # self.summary = f.read()
437
+ # except Exception as e:
438
+ # print("Could not read summary.txt:", e)
439
+ # self.summary = ""
440
+
441
+ # # Load knowledge files (RAG-style simple concatenation)
442
+ # self.knowledge = ""
443
+ # kb_dir = os.path.join("me", "knowledge")
444
+ # if os.path.exists(kb_dir):
445
+ # for fn in sorted(os.listdir(kb_dir)):
446
+ # path = os.path.join(kb_dir, fn)
447
+ # try:
448
+ # with open(path, "r", encoding="utf-8") as f:
449
+ # self.knowledge += f"# {fn}\n" + f.read() + "\n\n"
450
+ # except Exception as e:
451
+ # print("Error reading", path, e)
452
+
453
+ # def system_prompt(self):
454
+ # system_prompt = (
455
+ # f"You are acting as {self.name}. Answer questions about {self.name}'s background "
456
+ # "and experience using the context provided. Be professional and concise. "
457
+ # "If you don't know an answer, use the record_unknown_question tool."
458
+ # )
459
+ # system_prompt += f"\n\n## Summary:\n{self.summary}\n\n"
460
+ # system_prompt += f"## LinkedIn profile (extracted):\n{self.linkedin}\n\n"
461
+ # system_prompt += f"## Knowledge base:\n{self.knowledge}\n\n"
462
+ # return system_prompt
463
+
464
+ # def handle_tool_call(self, tool_calls):
465
+ # results = []
466
+ # for tool_call in tool_calls:
467
+ # tool_name = tool_call.function.name
468
+ # try:
469
+ # arguments = json.loads(tool_call.function.arguments)
470
+ # except Exception:
471
+ # arguments = {}
472
+ # print("Tool called:", tool_name, arguments, flush=True)
473
+ # tool = globals().get(tool_name)
474
+ # result = tool(**arguments) if tool else {}
475
+ # results.append({
476
+ # "role": "tool",
477
+ # "content": json.dumps(result),
478
+ # "tool_call_id": tool_call.id
479
+ # })
480
+ # return results
481
+
482
+ # # Simple router/orchestrator: route common queries to the FAQ or to the LLM
483
+ # def route_question(self, question):
484
+ # q = question.lower()
485
+ # # keywords that map to FAQ
486
+ # faq_keywords = ["project", "tech stack", "stack", "skill", "skills", "study", "education", "experience"]
487
+ # if any(k in q for k in faq_keywords):
488
+ # return "search_faq"
489
+ # return None
490
+
491
+ # def evaluate_answer(self, user_question, ai_answer):
492
+ # # Simple evaluator: ask the LLM to judge the quality
493
+ # eval_prompt = f"""
494
+ # You are an evaluator. Judge whether the assistant reply is clear, correct, and complete for the user question.
495
+ # Return exactly PASS or FAIL and a one-line reason.
496
+
497
+ # User question:
498
+ # {user_question}
499
+
500
+ # Assistant reply:
501
+ # {ai_answer}
502
+ # """
503
+ # try:
504
+ # ev = self.openai.chat.completions.create(
505
+ # model="gemini-2.0-flash",
506
+ # messages=[{"role":"system","content":"You are an evaluator."},
507
+ # {"role":"user","content":eval_prompt}]
508
+ # )
509
+ # text = ev.choices[0].message.content.strip()
510
+ # # very simple parse
511
+ # if text.upper().startswith("PASS"):
512
+ # return {"result":"PASS", "note": text}
513
+ # else:
514
+ # return {"result":"FAIL", "note": text}
515
+ # except Exception as e:
516
+ # print("Evaluator failed:", e)
517
+ # return {"result":"UNKNOWN", "note": str(e)}
518
+
519
+ # def chat(self, message, history):
520
+ # # build messages with system prompt + history + user
521
+ # messages = [{"role":"system","content":self.system_prompt()}] + history + [{"role":"user","content":message}]
522
+
523
+ # # 1) Router: check if the question should use the FAQ tool
524
+ # tool_to_use = self.route_question(message)
525
+ # if tool_to_use == "search_faq":
526
+ # # call tool directly and return evaluated answer
527
+ # tool_result = search_faq(message)
528
+ # raw_answer = tool_result.get("answer", "I don't have that in my FAQ.")
529
+ # eval_res = self.evaluate_answer(message, raw_answer)
530
+ # if eval_res["result"] == "PASS":
531
+ # return raw_answer
532
+ # else:
533
+ # # fall back to LLM if FAIL
534
+ # pass
535
+
536
+ # # 2) Normal LLM flow with tools support (function-calling style)
537
+ # done = False
538
+ # while not done:
539
+ # response = self.openai.chat.completions.create(
540
+ # model="gemini-2.0-flash",
541
+ # messages=messages,
542
+ # tools=tools
543
+ # )
544
+
545
+ # finish = response.choices[0].finish_reason
546
+ # if finish == "tool_calls":
547
+ # # the LLM asked to call a tool
548
+ # message_obj = response.choices[0].message
549
+ # tool_calls = getattr(message_obj, "tool_calls", [])
550
+ # results = self.handle_tool_call(tool_calls)
551
+ # messages.append(message_obj)
552
+ # messages.extend(results)
553
+ # # loop again so the LLM can consume tool outputs
554
+ # else:
555
+ # done = True
556
+
557
+ # ai_answer = response.choices[0].message.content
558
+ # # 3) Evaluate the answer; if FAIL, ask LLM to improve
559
+ # eval_res = self.evaluate_answer(message, ai_answer)
560
+ # if eval_res["result"] == "FAIL":
561
+ # # ask the model to improve using the critique
562
+ # improve_prompt = f"User question:\n{message}\n\nAssistant previous reply:\n{ai_answer}\n\nEvaluator note:\n{eval_res['note']}\n\nPlease produce an improved concise answer."
563
+ # messages.append({"role":"user","content":improve_prompt})
564
+ # improved_resp = self.openai.chat.completions.create(model="gemini-2.0-flash", messages=messages)
565
+ # ai_answer = improved_resp.choices[0].message.content
566
+
567
+ # return ai_answer
568
+
569
+ # # --- Launch ---
570
+ # if __name__ == "__main__":
571
+ # me = Me()
572
+ # gr.ChatInterface(me.chat, type="messages").launch()
573
+ # # gr.ChatInterface(me.chat).launch()
574
+
575
+
576
+
577
+
578
+
579
+
580
+ # # openAI router using Gemini
581
+ # # app.py
582
+ # from dotenv import load_dotenv
583
+ # from openai import OpenAI
584
+ # import json
585
+ # import os
586
+ # import requests
587
+ # from pypdf import PdfReader
588
+ # import gradio as gr
589
+ # import sqlite3
590
+ # import time
591
+
592
+ # load_dotenv(override=True)
593
+
594
+ # # --- CONFIG (OpenRouter instead of Google Gemini) ---
595
+ # OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1"
596
+ # openrouter_api_key = os.getenv("OPENROUTER_API_KEY")
597
+
598
+ # # Initialize OpenRouter client
599
+ # gemini = OpenAI(
600
+ # base_url=OPENROUTER_BASE_URL,
601
+ # api_key=openrouter_api_key,
602
+ # default_headers={
603
+ # "HTTP-Referer": "http://localhost", # required by OpenRouter
604
+ # "X-Title": "My-Gemini-App"
605
+ # }
606
+ # )
607
+
608
+ # # --- Pushover helper ---
609
+ # def push(text):
610
+ # token = os.getenv("PUSHOVER_TOKEN")
611
+ # user = os.getenv("PUSHOVER_USER")
612
+ # if not token or not user:
613
+ # print("Pushover credentials not set. Skipping push.")
614
+ # return
615
+ # try:
616
+ # requests.post(
617
+ # "https://api.pushover.net/1/messages.json",
618
+ # data={"token": token, "user": user, "message": text},
619
+ # timeout=5
620
+ # )
621
+ # except Exception as e:
622
+ # print("Pushover error:", e)
623
+
624
+ # # --- Tools ---
625
+ # def record_user_details(email, name="Name not provided", notes="not provided"):
626
+ # push(f"Recording contact: {name} <{email}> notes: {notes}")
627
+ # return {"recorded": "ok", "email": email, "name": name}
628
+
629
+ # def record_unknown_question(question):
630
+ # push(f"Unknown question recorded: {question}")
631
+ # os.makedirs("me/logs", exist_ok=True)
632
+ # with open("me/logs/unknown_questions.txt", "a", encoding="utf-8") as f:
633
+ # f.write(question.strip() + "\n")
634
+ # return {"recorded": "ok", "question": question}
635
+
636
+ # def search_faq(query):
637
+ # db_path = os.path.join("me", "qa.db")
638
+ # if not os.path.exists(db_path):
639
+ # return {"answer": "FAQ database not found."}
640
+ # conn = sqlite3.connect(db_path)
641
+ # cur = conn.cursor()
642
+ # cur.execute("SELECT answer FROM faq WHERE question LIKE ? LIMIT 1", (f"%{query}%",))
643
+ # row = cur.fetchone()
644
+ # conn.close()
645
+ # return {"answer": row[0]} if row else {"answer": "not found"}
646
+
647
+ # # --- Tool JSON metadata ---
648
+ # record_user_details_json = {
649
+ # "name": "record_user_details",
650
+ # "description": "Record an interested user's email and optional name/notes.",
651
+ # "parameters": {
652
+ # "type": "object",
653
+ # "properties": {
654
+ # "email": {"type": "string"},
655
+ # "name": {"type": "string"},
656
+ # "notes": {"type": "string"}
657
+ # },
658
+ # "required": ["email"],
659
+ # "additionalProperties": False
660
+ # }
661
+ # }
662
+
663
+ # record_unknown_question_json = {
664
+ # "name": "record_unknown_question",
665
+ # "description": "Record any question the assistant could not answer.",
666
+ # "parameters": {
667
+ # "type": "object",
668
+ # "properties": {
669
+ # "question": {"type": "string"}
670
+ # },
671
+ # "required": ["question"],
672
+ # "additionalProperties": False
673
+ # }
674
+ # }
675
+
676
+ # search_faq_json = {
677
+ # "name": "search_faq",
678
+ # "description": "Search the FAQ database for a question.",
679
+ # "parameters": {
680
+ # "type": "object",
681
+ # "properties": {
682
+ # "query": {"type": "string"}
683
+ # },
684
+ # "required": ["query"],
685
+ # "additionalProperties": False
686
+ # }
687
+ # }
688
+
689
+ # tools = [
690
+ # {"type": "function", "function": record_user_details_json},
691
+ # {"type": "function", "function": record_unknown_question_json},
692
+ # {"type": "function", "function": search_faq_json}
693
+ # ]
694
+
695
+ # # --- The assistant class ---
696
+ # class Me:
697
+ # def __init__(self):
698
+ # self.openai = gemini
699
+ # self.name = "AKASH M J"
700
+
701
+ # self.linkedin = ""
702
+ # try:
703
+ # reader = PdfReader(os.path.join("me", "Profile.pdf"))
704
+ # for page in reader.pages:
705
+ # text = page.extract_text()
706
+ # if text:
707
+ # self.linkedin += text + "\n"
708
+ # except Exception as e:
709
+ # print("Could not read Profile.pdf:", e)
710
+
711
+ # try:
712
+ # with open(os.path.join("me", "summary.txt"), "r", encoding="utf-8") as f:
713
+ # self.summary = f.read()
714
+ # except:
715
+ # self.summary = ""
716
+
717
+ # self.knowledge = ""
718
+ # kb_dir = os.path.join("me", "knowledge")
719
+ # if os.path.exists(kb_dir):
720
+ # for fn in sorted(os.listdir(kb_dir)):
721
+ # try:
722
+ # with open(os.path.join(kb_dir, fn), "r", encoding="utf-8") as f:
723
+ # self.knowledge += f"# {fn}\n" + f.read() + "\n\n"
724
+ # except:
725
+ # pass
726
+
727
+ # def system_prompt(self):
728
+ # system_prompt = (
729
+ # f"You are acting as {self.name}. Answer questions about {self.name}'s background."
730
+ # )
731
+ # system_prompt += f"\n\n## Summary:\n{self.summary}\n\n"
732
+ # system_prompt += f"## LinkedIn profile:\n{self.linkedin}\n\n"
733
+ # system_prompt += f"## Knowledge base:\n{self.knowledge}\n\n"
734
+ # return system_prompt
735
+
736
+ # def handle_tool_call(self, tool_calls):
737
+ # results = []
738
+ # for tool_call in tool_calls:
739
+ # tool_name = tool_call.function.name
740
+ # arguments = json.loads(tool_call.function.arguments)
741
+ # tool = globals().get(tool_name)
742
+ # result = tool(**arguments) if tool else {}
743
+ # results.append({
744
+ # "role": "tool",
745
+ # "content": json.dumps(result),
746
+ # "tool_call_id": tool_call.id
747
+ # })
748
+ # return results
749
+
750
+ # def route_question(self, q):
751
+ # q = q.lower()
752
+ # faq_keywords = ["project", "skills", "experience", "study", "education"]
753
+ # if any(k in q for k in faq_keywords):
754
+ # return "search_faq"
755
+ # return None
756
+
757
+ # def evaluate_answer(self, user_question, ai_answer):
758
+ # eval_prompt = f"""
759
+ # Evaluate if the answer is good. Respond with PASS or FAIL.
760
+
761
+ # User question:
762
+ # {user_question}
763
+
764
+ # Assistant reply:
765
+ # {ai_answer}
766
+ # """
767
+ # try:
768
+ # ev = self.openai.chat.completions.create(
769
+ # model="google/gemini-2.0-flash-exp:free",
770
+ # messages=[
771
+ # {"role": "system", "content": "You are an evaluator."},
772
+ # {"role": "user", "content": eval_prompt}
773
+ # ]
774
+ # )
775
+ # text = ev.choices[0].message.content.strip()
776
+ # if text.upper().startswith("PASS"):
777
+ # return {"result": "PASS", "note": text}
778
+ # return {"result": "FAIL", "note": text}
779
+ # except Exception as e:
780
+ # return {"result": "UNKNOWN", "note": str(e)}
781
+
782
+ # def chat(self, message, history):
783
+ # messages = [{"role": "system", "content": self.system_prompt()}] + history + [{"role": "user", "content": message}]
784
+
785
+ # tool_to_use = self.route_question(message)
786
+ # if tool_to_use == "search_faq":
787
+ # tool_result = search_faq(message)
788
+ # ans = tool_result.get("answer", "not found")
789
+ # if self.evaluate_answer(message, ans)["result"] == "PASS":
790
+ # return ans
791
+
792
+ # done = False
793
+ # while not done:
794
+ # response = self.openai.chat.completions.create(
795
+ # model="google/gemini-2.0-flash-exp:free",
796
+ # messages=messages,
797
+ # tools=tools
798
+ # )
799
+ # finish = response.choices[0].finish_reason
800
+
801
+ # if finish == "tool_calls":
802
+ # tool_calls = response.choices[0].message.tool_calls
803
+ # results = self.handle_tool_call(tool_calls)
804
+ # messages.append(response.choices[0].message)
805
+ # messages.extend(results)
806
+ # else:
807
+ # done = True
808
+
809
+ # ai_answer = response.choices[0].message.content
810
+ # eval_res = self.evaluate_answer(message, ai_answer)
811
+ # if eval_res["result"] == "FAIL":
812
+ # improve_prompt = f"Improve this answer:\n{ai_answer}\n\nCritique:\n{eval_res['note']}"
813
+ # messages.append({"role": "user", "content": improve_prompt})
814
+ # improved = self.openai.chat.completions.create(
815
+ # model="google/gemini-2.0-flash-exp:free",
816
+ # messages=messages
817
+ # )
818
+ # ai_answer = improved.choices[0].message.content
819
+
820
+ # return ai_answer
821
+
822
+
823
+ # # --- Launch ---
824
+ # if __name__ == "__main__":
825
+ # me = Me()
826
+ # gr.ChatInterface(me.chat, type="messages").launch()
827
+
828
+
829
+
830
+
831
+ # openAI router using openai/gpt-oss-120b:free
832
+
833
+
834
  from dotenv import load_dotenv
835
  from openai import OpenAI
836
  import json
 
843
 
844
  load_dotenv(override=True)
845
 
846
+ # -------------------------------------------------------------------
847
+ # OPENROUTER CONFIG
848
+ # -------------------------------------------------------------------
849
+ OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY")
850
+
851
+ openrouter = OpenAI(
852
+ base_url="https://openrouter.ai/api/v1",
853
+ api_key=OPENROUTER_API_KEY
854
+ )
855
 
856
+ # Your chosen free model on OpenRouter
857
+ MODEL_NAME = "openai/gpt-oss-120b:free"
858
 
859
+ # -------------------------------------------------------------------
860
+ # Pushover helper
861
+ # -------------------------------------------------------------------
862
  def push(text):
863
  token = os.getenv("PUSHOVER_TOKEN")
864
  user = os.getenv("PUSHOVER_USER")
 
874
  except Exception as e:
875
  print("Pushover error:", e)
876
 
877
+ # -------------------------------------------------------------------
878
+ # TOOLS
879
+ # -------------------------------------------------------------------
880
  def record_user_details(email, name="Name not provided", notes="not provided"):
881
  push(f"Recording contact: {name} <{email}> notes: {notes}")
882
  return {"recorded": "ok", "email": email, "name": name}
883
 
884
  def record_unknown_question(question):
885
  push(f"Unknown question recorded: {question}")
 
886
  os.makedirs("me/logs", exist_ok=True)
887
  with open("me/logs/unknown_questions.txt", "a", encoding="utf-8") as f:
888
  f.write(question.strip() + "\n")
 
899
  conn.close()
900
  return {"answer": row[0]} if row else {"answer": "not found"}
901
 
902
+ # Tool JSON
903
  record_user_details_json = {
904
  "name": "record_user_details",
905
  "description": "Record an interested user's email and optional name/notes.",
 
947
  {"type": "function", "function": search_faq_json}
948
  ]
949
 
950
+ # -------------------------------------------------------------------
951
+ # MAIN ASSISTANT CLASS
952
+ # -------------------------------------------------------------------
953
  class Me:
954
  def __init__(self):
955
+ self.openai = openrouter # <--- using OpenRouter
956
  self.name = "AKASH M J"
957
 
958
+ # Load PDF profile
959
  self.linkedin = ""
960
  try:
961
  reader = PdfReader(os.path.join("me", "Profile.pdf"))
 
974
  print("Could not read summary.txt:", e)
975
  self.summary = ""
976
 
977
+ # Load knowledge files
978
  self.knowledge = ""
979
  kb_dir = os.path.join("me", "knowledge")
980
  if os.path.exists(kb_dir):
 
988
 
989
  def system_prompt(self):
990
  system_prompt = (
991
+ f"You are acting as {self.name}. Answer questions about {self.name}'s "
992
+ "background and experience using the context provided. Be professional. "
993
+ "If unsure, use record_unknown_question."
994
  )
995
  system_prompt += f"\n\n## Summary:\n{self.summary}\n\n"
996
+ system_prompt += f"## LinkedIn:\n{self.linkedin}\n\n"
997
+ system_prompt += f"## Knowledge:\n{self.knowledge}\n\n"
998
  return system_prompt
999
 
1000
  def handle_tool_call(self, tool_calls):
 
1015
  })
1016
  return results
1017
 
1018
+ # Router for FAQ
1019
  def route_question(self, question):
1020
  q = question.lower()
1021
+ faq_keywords = ["project", "tech stack", "skill", "education", "experience"]
 
1022
  if any(k in q for k in faq_keywords):
1023
  return "search_faq"
1024
  return None
1025
 
1026
+ # Evaluator
1027
  def evaluate_answer(self, user_question, ai_answer):
 
1028
  eval_prompt = f"""
1029
+ Evaluate the answer clarity and correctness.
1030
+ Return PASS or FAIL and one-line reason.
1031
 
1032
+ Question: {user_question}
1033
+ Answer: {ai_answer}
 
 
 
1034
  """
1035
  try:
1036
  ev = self.openai.chat.completions.create(
1037
+ model=MODEL_NAME,
1038
+ messages=[
1039
+ {"role": "system", "content": "You are an evaluator."},
1040
+ {"role": "user", "content": eval_prompt}
1041
+ ]
1042
  )
1043
  text = ev.choices[0].message.content.strip()
 
1044
  if text.upper().startswith("PASS"):
1045
+ return {"result": "PASS", "note": text}
1046
  else:
1047
+ return {"result": "FAIL", "note": text}
1048
  except Exception as e:
1049
+ return {"result": "UNKNOWN", "note": str(e)}
 
1050
 
1051
+ # Chat
1052
  def chat(self, message, history):
 
1053
  messages = [{"role":"system","content":self.system_prompt()}] + history + [{"role":"user","content":message}]
1054
 
1055
+ # Router: FAQ
1056
  tool_to_use = self.route_question(message)
1057
  if tool_to_use == "search_faq":
 
1058
  tool_result = search_faq(message)
1059
+ raw_answer = tool_result.get("answer", "Not found.")
1060
+ ev = self.evaluate_answer(message, raw_answer)
1061
+ if ev["result"] == "PASS":
1062
  return raw_answer
 
 
 
1063
 
1064
+ # LLM with tools
1065
  done = False
1066
  while not done:
1067
  response = self.openai.chat.completions.create(
1068
+ model=MODEL_NAME,
1069
  messages=messages,
1070
  tools=tools
1071
  )
1072
 
1073
  finish = response.choices[0].finish_reason
1074
  if finish == "tool_calls":
1075
+ msg = response.choices[0].message
1076
+ tool_calls = getattr(msg, "tool_calls", [])
 
1077
  results = self.handle_tool_call(tool_calls)
1078
+ messages.append(msg)
1079
  messages.extend(results)
 
1080
  else:
1081
  done = True
1082
 
1083
  ai_answer = response.choices[0].message.content
1084
+
1085
+ # Evaluate
1086
  eval_res = self.evaluate_answer(message, ai_answer)
1087
  if eval_res["result"] == "FAIL":
1088
+ improve_prompt = (
1089
+ f"User question:\n{message}\n\n"
1090
+ f"Previous answer:\n{ai_answer}\n\n"
1091
+ f"Evaluator note:\n{eval_res['note']}\n\n"
1092
+ "Please provide an improved answer."
1093
+ )
1094
+ messages.append({"role": "user", "content": improve_prompt})
1095
+ improved_resp = self.openai.chat.completions.create(
1096
+ model=MODEL_NAME, messages=messages
1097
+ )
1098
  ai_answer = improved_resp.choices[0].message.content
1099
 
1100
  return ai_answer
1101
 
1102
+ # -------------------------------------------------------------------
1103
+ # GRADIO LAUNCH
1104
+ # -------------------------------------------------------------------
1105
  if __name__ == "__main__":
1106
  me = Me()
1107
  # gr.ChatInterface(me.chat, type="messages").launch()
1108
+ gr.ChatInterface(me.chat).launch()