Antonis Bast commited on
Commit
4d557a4
·
1 Parent(s): f62f692

Fix app.py merge conflict markers

Browse files
Files changed (1) hide show
  1. app.py +0 -389
app.py CHANGED
@@ -1,4 +1,3 @@
1
- <<<<<<< HEAD
2
  from dotenv import load_dotenv
3
  from openai import OpenAI
4
  import json
@@ -385,392 +384,4 @@ if __name__ == "__main__":
385
  demo.load(load_welcome, None, chatbot)
386
 
387
  demo.launch(share=False)
388
- =======
389
- from dotenv import load_dotenv
390
- from openai import OpenAI
391
- import json
392
- import os
393
- import requests
394
- from pypdf import PdfReader
395
- import gradio as gr
396
- from datetime import datetime
397
- import gspread
398
- from oauth2client.service_account import ServiceAccountCredentials
399
- import base64
400
-
401
-
402
- load_dotenv(override=True)
403
-
404
-
405
- def get_sheets_client():
406
- """Initialize Google Sheets client"""
407
- scope = [
408
- 'https://spreadsheets.google.com/feeds',
409
- 'https://www.googleapis.com/auth/drive'
410
- ]
411
-
412
- credentials_file = 'credentials.json'
413
-
414
- if os.path.exists(credentials_file):
415
- creds = ServiceAccountCredentials.from_json_keyfile_name(
416
- credentials_file,
417
- scope
418
- )
419
- else:
420
- creds_json = os.environ.get("GOOGLE_SHEETS_CREDS")
421
-
422
- if not creds_json:
423
- raise ValueError("GOOGLE_SHEETS_CREDS not found in environment variables!")
424
-
425
- try:
426
- creds_dict = json.loads(creds_json)
427
- except json.JSONDecodeError as e:
428
- raise ValueError(f"Invalid JSON in GOOGLE_SHEETS_CREDS: {e}")
429
-
430
- creds = ServiceAccountCredentials.from_json_keyfile_dict(
431
- creds_dict,
432
- scope
433
- )
434
-
435
- return gspread.authorize(creds)
436
-
437
- def record_user_details(email, name="Name not provided", notes="Not provided"):
438
- """Record user contact details to Google Sheets"""
439
- try:
440
- client = get_sheets_client()
441
- sheet_name = os.getenv("GOOGLE_SHEET_NAME", "ChatBot Contacts")
442
- spreadsheet = client.open(sheet_name)
443
- worksheet = spreadsheet.worksheet("Contacts")
444
-
445
- row = [
446
- datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
447
- name,
448
- email,
449
- notes
450
- ]
451
-
452
- worksheet.append_row(row, value_input_option='RAW')
453
- print(f"✓ Contact recorded: {name} ({email})")
454
- return {"status": "success", "message": f"Thank you! I've recorded your details. I'll get back to you at {email} soon."}
455
-
456
- except Exception as e:
457
- print(f"❌ Error recording contact: {e}")
458
- return {"status": "error", "message": "There was an error recording your details. Please try again."}
459
-
460
- def record_unanswered_question(question, context=""):
461
- """Record questions the bot couldn't answer"""
462
- try:
463
- client = get_sheets_client()
464
- sheet_name = os.getenv("GOOGLE_SHEET_NAME", "ChatBot Contacts")
465
- spreadsheet = client.open(sheet_name)
466
- worksheet = spreadsheet.worksheet("Unanswered Questions")
467
-
468
- row = [
469
- datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
470
- question,
471
- context
472
- ]
473
-
474
- worksheet.append_row(row, value_input_option='RAW')
475
- print(f"✓ Unanswered question recorded")
476
- return {"status": "success", "message": "I've recorded your question for follow-up."}
477
-
478
- except Exception as e:
479
- print(f"❌ Error recording question: {e}")
480
- return {"status": "error", "message": "Error recording question."}
481
-
482
-
483
-
484
- record_user_details_json = {
485
- "name": "record_user_details",
486
- "description": "Use this tool to record that a user is interested in being in touch and provided an email address",
487
- "parameters": {
488
- "type": "object",
489
- "properties": {
490
- "email": {
491
- "type": "string",
492
- "description": "The email address of this user"
493
- },
494
- "name": {
495
- "type": "string",
496
- "description": "The user's name, if they provided it"
497
- }
498
- ,
499
- "notes": {
500
- "type": "string",
501
- "description": "Any additional information about the conversation that's worth recording to give context"
502
- }
503
- },
504
- "required": ["email"],
505
- "additionalProperties": False
506
- }
507
- }
508
-
509
- record_unanswered_question_json = {
510
- "name": "record_unanswered_question",
511
- "description": "Always use this tool to record any question that couldn't be answered as you didn't know the answer",
512
- "parameters": {
513
- "type": "object",
514
- "properties": {
515
- "question": {
516
- "type": "string",
517
- "description": "The question that couldn't be answered"
518
- },
519
- },
520
- "required": ["question"],
521
- "additionalProperties": False
522
- }
523
- }
524
-
525
- tools = [{"type": "function", "function": record_user_details_json},
526
- {"type": "function", "function": record_unanswered_question_json}]
527
-
528
-
529
- class Me:
530
-
531
- def __init__(self):
532
- self.openai = OpenAI()
533
- self.name = "Antonis Bastoulis"
534
- reader = PdfReader("me/linkedin.pdf")
535
- self.linkedin = ""
536
- for page in reader.pages:
537
- text = page.extract_text()
538
- if text:
539
- self.linkedin += text
540
- readerCV = PdfReader("me/CV.pdf")
541
- self.CV = ""
542
- for page in readerCV.pages:
543
- text = page.extract_text()
544
- if text:
545
- self.CV += text
546
- with open("me/summary.txt", "r", encoding="utf-8") as f:
547
- self.summary = f.read()
548
-
549
-
550
- def handle_tool_call(self, tool_calls):
551
- results = []
552
- for tool_call in tool_calls:
553
- tool_name = tool_call.function.name
554
- arguments = json.loads(tool_call.function.arguments)
555
- print(f"Tool called: {tool_name}", flush=True)
556
- tool = globals().get(tool_name)
557
- result = tool(**arguments) if tool else {}
558
- results.append({"role": "tool","content": json.dumps(result),"tool_call_id": tool_call.id})
559
- return results
560
-
561
- def system_prompt(self):
562
- system_prompt = f"""You are {self.name}'s professional representative, answering questions about career, skills, and experience.
563
- You are an AI assistant representing {self.name}. Speak in first person as if you are {self.name}, using "I" and "my" when discussing his experience, skills, and background. However, if directly asked if you are AI or Antonis himself, be transparent that you're an AI assistant trained on his professional information.
564
-
565
- ## BOUNDARIES - Never Discuss:
566
- - Personal life (family, relationships, health, politics, religion)
567
- - Compensation details (past salaries, current income)
568
- - Why you left previous jobs
569
- - Negative comments about past employers/colleagues
570
- - Confidential project details or proprietary information
571
- - Personal contact info (home address, personal phone)
572
-
573
- ## How to Handle Restricted Topics:
574
- Redirect professionally: "I keep that private. Let me tell you about {self.name}'s expertise in [relevant area] instead."
575
- For salary questions: "Compensation is best discussed based on role requirements. What position interests you?"
576
-
577
- ## Available Information:
578
- ### Summary:
579
- {self.summary}
580
- ### LinkedIn Profile:
581
- {self.linkedin}
582
- ### CV:
583
- {self.CV}
584
- Stay in character as {self.name}'s professional representative. Be helpful within appropriate boundaries."""
585
- return system_prompt
586
-
587
-
588
-
589
- def chat(self, message, history):
590
- messages = [{"role": "system", "content": self.system_prompt()}] + history + [{"role": "user", "content": message}]
591
- done = False
592
- while not done:
593
- response = self.openai.chat.completions.create(model="gpt-4o-mini", messages=messages, tools=tools)
594
- if response.choices[0].finish_reason=="tool_calls":
595
- message = response.choices[0].message
596
- tool_calls = message.tool_calls
597
- results = self.handle_tool_call(tool_calls)
598
- messages.append(message)
599
- messages.extend(results)
600
- else:
601
- done = True
602
- return response.choices[0].message.content
603
-
604
-
605
- if __name__ == "__main__":
606
- me = Me()
607
-
608
- # Custom theme
609
- custom_theme = gr.themes.Soft(
610
- primary_hue="blue",
611
- secondary_hue="slate",
612
- neutral_hue="slate",
613
- font=[gr.themes.GoogleFont("Inter"), "sans-serif"]
614
- ).set(
615
- body_background_fill="*neutral_50",
616
- button_primary_background_fill="*primary_600",
617
- button_primary_background_fill_hover="*primary_700",
618
- )
619
-
620
- with gr.Blocks(theme=custom_theme, title="Career Chatbot ", css="""
621
- .header-container {
622
- text-align: center;
623
- padding: 2rem;
624
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
625
- color: white;
626
- border-radius: 10px;
627
- margin-bottom: 2rem;
628
- }
629
- .header-title {
630
- font-size: 2.5rem;
631
- font-weight: bold;
632
- margin-bottom: 0.5rem;
633
- }
634
- .header-subtitle {
635
- font-size: 1.2rem;
636
- opacity: 0.9;
637
- }
638
- footer {
639
- text-align: center;
640
- padding: 2rem;
641
- color: #666;
642
- }
643
- """) as demo:
644
-
645
- # Header
646
- gr.HTML("""
647
- <div class="header-container">
648
- <div class="header-title">Antonis Bastoulis</div>
649
- <div class="header-subtitle">
650
- Naval Architect & Marine Engineer | M.Sc. in Artificial Intelligence & Deep Learning
651
- </div>
652
- <div style="font-size: 1rem; margin-top: 1rem; opacity: 0.85;">
653
- AI Assistant • Ask me about my experience, projects, and expertise
654
- </div>
655
- </div>
656
- """)
657
-
658
- # Tabs for different sections
659
- with gr.Tabs():
660
- # Chat Tab
661
- with gr.TabItem("💬 Chat with Me"):
662
- with gr.Row():
663
- with gr.Column(scale=2):
664
- chatbot = gr.Chatbot(
665
- label="Conversation",
666
- height=500,
667
- bubble_full_width=False,
668
- show_copy_button=True,
669
- type="messages"
670
- )
671
-
672
- with gr.Row():
673
- msg = gr.Textbox(
674
- placeholder="Ask me about my experience, skills, projects...",
675
- show_label=False,
676
- scale=4,
677
- container=False
678
- )
679
- submit_btn = gr.Button("Send", variant="primary", scale=1)
680
-
681
- with gr.Row():
682
- clear = gr.Button("🔄 Clear Chat", size="sm")
683
-
684
-
685
- # About/FAQ Tab
686
- with gr.TabItem("ℹ️ About"):
687
- with gr.Row():
688
- with gr.Column():
689
- gr.Markdown("""
690
- ### About This Chatbot
691
-
692
- This AI assistant is trained on my professional profile and can answer questions about:
693
- - 💼 Work experience and career history
694
- - 🛠️ Technical skills and expertise
695
- - 📁 Projects and achievements
696
- - 🎓 Education and certifications
697
- - 🎯 Career goals and interests
698
-
699
- ### How to Use
700
- 1. Ask any question about my professional background
701
- 2. If I can't answer something, it will be recorded for me to review
702
- 3. Want to connect? Simply tell the chatbot your name, email, and a message - I'll record your details and get back to you.
703
-
704
-
705
- ### Privacy & Data Protection
706
- - Conversations are processed securely via OpenAI's API
707
- - Contact information is only used to reach out to you
708
- - Unanswered questions are recorded to improve responses
709
- - No conversation history is permanently stored
710
-
711
- ---
712
- """)
713
-
714
- gr.Markdown("### Connect With Me")
715
- gr.HTML("""
716
- <div style="text-align: center; padding: 2rem;">
717
- <a href="https://www.linkedin.com/in/antonisbast/" target="_blank"
718
- style="margin: 0 1rem; text-decoration: none; font-size: 1.1rem;">
719
- 🔗 LinkedIn
720
- </a>
721
- <a href="https://github.com/antonisbast" target="_blank"
722
- style="margin: 0 1rem; text-decoration: none; font-size: 1.1rem;">
723
- 💻 GitHub
724
- </a>
725
- <a href="mailto:antonisbast@gmail.com"
726
- style="margin: 0 1rem; text-decoration: none; font-size: 1.1rem;">
727
- 📧 Email
728
- </a>
729
- <a href="https://huggingface.co/antonisbast" target="_blank"
730
- style="margin: 0 1rem; text-decoration: none; font-size: 1.1rem;">
731
- 🌐 Hugging Face
732
- </a>
733
- </div>
734
- """)
735
-
736
- gr.Markdown("""
737
- ### Technical Details
738
- - **Powered by:** OpenAI GPT-4o-mini
739
- - **Framework:** Gradio
740
- - **Data Sources:** LinkedIn Profile, CV/Resume
741
- - **Features:** Function calling for contact recording and unanswered questions
742
- """)
743
-
744
- # Footer
745
- gr.HTML("""
746
- <footer>
747
- <p>🤖 Powered by ChatGPT & Gradio | Last Updated: 2025</p>
748
- </footer>
749
- """)
750
-
751
- def respond(message, history):
752
- """Handle chat response"""
753
- if not message.strip():
754
- return history, ""
755
-
756
- history.append({"role": "user", "content": message})
757
- yield history, "" # First yield - shows user message right away
758
- bot_message = me.chat(message, history[:-1])
759
- history.append({"role": "assistant", "content": bot_message})
760
- yield history, "" # Second yield - shows bot response
761
-
762
- def load_welcome():
763
- return [{"role": "assistant", "content": "Hello! I'm Antonis's AI assistant. I can answer questions about his experience in naval architecture, marine engineering, and AI/ML projects. What would you like to know?"}]
764
-
765
- # Chat events
766
- msg.submit(respond, [msg, chatbot], [chatbot, msg])
767
- submit_btn.click(respond, [msg, chatbot], [chatbot, msg])
768
- clear.click(lambda: [], None, chatbot, queue=False)
769
-
770
-
771
- # Load welcome message
772
- demo.load(load_welcome, None, chatbot)
773
-
774
- demo.launch(share=False)
775
- >>>>>>> huggingface/main
776
 
 
 
1
  from dotenv import load_dotenv
2
  from openai import OpenAI
3
  import json
 
384
  demo.load(load_welcome, None, chatbot)
385
 
386
  demo.launch(share=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
387