diff --git a/README.md b/README.md index 82b7927f..a36d61e2 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,8 @@ Powered by [Nebius AI Studio](https://dub.sh/nebius) - your one-stop platform fo - [LangChain-LangGraph Starter](starter_ai_agents/langchain_langgraph_starter) - LangChain + LangGraph starter. - [AWS Strands Agent Starter](starter_ai_agents/aws_strands_starter) - Weather report Agent. - [Camel AI Starter](starter_ai_agents/camel_ai_starter) - Performance benchmarking tool that compares the performance of various AI models. +- [Career_Guidence](simple_ai_agents/Career_Guidence) - Gives the guidence and roadmap towards career based on the user selected options,it also supports Traditional Rag. +- [Recruitify](simple_ai_agents/Recruitify) - A web app that analyse the resume based on the JobDescription,Interview Question,Enhance Resume,Practise interview based on the question generated. ## πŸͺΆ Simple Agents @@ -47,6 +49,7 @@ Powered by [Nebius AI Studio](https://dub.sh/nebius) - your one-stop platform fo - [Nebius Chat](simple_ai_agents/nebius_chat) - Nebius AI Studio Chat interface. - [Talk to Your DB](simple_ai_agents/talk_to_db) - Talk to your Database with GibsonAI & Langchain + ## πŸ—‚οΈ MCP Agents **Examples using Model Context Protocol:** @@ -96,6 +99,8 @@ Powered by [Nebius AI Studio](https://dub.sh/nebius) - your one-stop platform fo - [Price Monitoring Agent](advance_ai_agents/price_monitoring_agent) - Price monitoring and alerting Agent powered by CrewAi, Twilio & Nebius. - [Startup Idea Validator Agent](advance_ai_agents/startup_idea_validator_agent) - Agentic Workflow to validate and analyze startup ideas. - [Meeting Assistant Agent](advance_ai_agents/meeting_assistant_agent) - Agentic Workflow that send meeting notes and creates task based on conversation. +- [AlgoMentor](advance_ai_agents/AlgoMentor) - Agentic Workflow that give the BruteForce,SubOptimal & Optimal Approach and code of the Problem , and also supports the notes making . +-[ScholaeLens](advance_ai_agents/ScholarLens) - Agentic Workflow for Find Research Paper,Summarizer,Agentic Rag With Pinecone and Agnno, and Companion of papers. ## πŸ“Ί Playlist of Demo Videos & Tutorials diff --git a/advance_ai_agents/AlgoMentor/.env.example b/advance_ai_agents/AlgoMentor/.env.example new file mode 100644 index 00000000..4824601e --- /dev/null +++ b/advance_ai_agents/AlgoMentor/.env.example @@ -0,0 +1,7 @@ +# API Keys +GROQ_API_KEY=your_groq_api_key_here +GOOGLE_API_KEY=your_google_api_key_here + +# Application Settings +DEBUG=False +LOG_LEVEL=INFO \ No newline at end of file diff --git a/advance_ai_agents/AlgoMentor/.gitignore b/advance_ai_agents/AlgoMentor/.gitignore new file mode 100644 index 00000000..c5f88213 --- /dev/null +++ b/advance_ai_agents/AlgoMentor/.gitignore @@ -0,0 +1,80 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +*.manifest +*.spec +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# VS Code +.vscode/ + +# Streamlit +.streamlit/ +secrets.toml + + +# Temporary files +*.tmp +*.temp +*.swp +*.swo + +# Logs +*.log +logs/ + +# Database +*.db +*.sqlite +*.sqlite3 + +# Cache +.cache/ +*.cache diff --git a/advance_ai_agents/AlgoMentor/README.md b/advance_ai_agents/AlgoMentor/README.md new file mode 100644 index 00000000..c34670a9 --- /dev/null +++ b/advance_ai_agents/AlgoMentor/README.md @@ -0,0 +1,89 @@ +# DSA Assistant πŸš€ + +Your AI-powered coding mentor for problem-solving & algorithm optimization + +## ✨ Features + +- **🎯 Basic Approach**: Generate brute-force solutions with clear explanations +- **⚑ Sub-Optimal Solutions**: Improve efficiency step by step +- **πŸ† Optimal Solutions**: Find the most efficient algorithms +- **πŸ” Code Verification**: Test and validate solutions automatically +- **πŸ“š Notes Generation**: Transform code into comprehensive study notes +- **🎨 Interactive UI**: Beautiful Streamlit interface with progress tracking + +## πŸš€ Quick Start + +### Installation + +```bash +# Clone the repository +git clone https://github.com/ankush0511/AlgoMentor.git +cd AlgoMentor + +# Install dependencies +pip install -r requirements.txt + +# Set up environment variables +cp .env.example .env +# Add your API keys to .env file +``` + +### Environment Setup + +Create a `.env` file with: +``` +GROQ_API_KEY=your_groq_api_key +GOOGLE_API_KEY=your_google_api_key +``` + +### Run the Application + +```bash +streamlit run main.py +``` + +## πŸ“ Project Structure + +``` +DSAAssistant/ +β”œβ”€β”€ πŸ“ src/ # Source code +β”‚ β”œβ”€β”€ πŸ“ agents/ # AI agents for different optimization levels +β”‚ β”œβ”€β”€ πŸ“ models/ # Pydantic data models +β”‚ β”œβ”€β”€ πŸ“ utils/ # Utility functions +β”‚ β”œβ”€β”€ πŸ“ ui/ # User interface components +β”‚ └── πŸ“ core/ # Core application logic +β”œβ”€β”€ πŸ“ config/ # Configuration files +β”œβ”€β”€ πŸ“ tests/ # Test files +β”œβ”€β”€ πŸ“ docs/ # Documentation +β”œβ”€β”€ πŸ“ assets/ # Static assets +β”œβ”€β”€ πŸ“„ main.py # Application entry point +β”œβ”€β”€ πŸ“„ requirements.txt # Python dependencies +β”œβ”€β”€ πŸ“„ .env.example # Environment variables template +└── πŸ“„ README.md # Project documentation +``` + +## πŸ› οΈ Usage + +1. **Enter Problem**: Paste your DSA problem or use example problems +2. **Basic Approach**: Get brute-force solution with explanation +3. **Sub-Optimal**: Improve the solution step by step +4. **Optimal**: Achieve the most efficient algorithm +5. **Notes**: Generate comprehensive study notes from your code + +## 🀝 Contributing + +1. Fork the repository +2. Create a feature branch +3. Make your changes +4. Add tests if applicable +5. Submit a pull request + +## πŸ“„ License + +This project is licensed under the MIT License. + +## πŸ™ Acknowledgments + +- Built with [Streamlit](https://streamlit.io/) +- Powered by [Groq](https://groq.com/) and [Google AI](https://ai.google/) +- Uses [Agno](https://github.com/agno-ai/agno) for AI agent orchestration diff --git a/advance_ai_agents/AlgoMentor/assets/logo.svg b/advance_ai_agents/AlgoMentor/assets/logo.svg new file mode 100644 index 00000000..ef38ba41 --- /dev/null +++ b/advance_ai_agents/AlgoMentor/assets/logo.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/advance_ai_agents/AlgoMentor/config/__init__.py b/advance_ai_agents/AlgoMentor/config/__init__.py new file mode 100644 index 00000000..f05f7f90 --- /dev/null +++ b/advance_ai_agents/AlgoMentor/config/__init__.py @@ -0,0 +1 @@ +"""Configuration settings""" \ No newline at end of file diff --git a/advance_ai_agents/AlgoMentor/config/settings.py b/advance_ai_agents/AlgoMentor/config/settings.py new file mode 100644 index 00000000..3da9818b --- /dev/null +++ b/advance_ai_agents/AlgoMentor/config/settings.py @@ -0,0 +1,24 @@ +import streamlit as st +import os +from dotenv import load_dotenv + +load_dotenv() + +# API Configuration +# GROQ_API_KEY = os.getenv("GROQ_API_KEY") +# GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY") +groq_api_key=st.secrets['GROQ_API_KEY'] +google_api_key=st.secrets['GOOGLE_API_KEY'] + +# Model Configuration +GROQ_MODEL = "llama-3.3-70b-versatile" +GEMINI_MODEL = "gemini-2.0-flash" + +# UI Configuration +PAGE_TITLE = "DSA Assistant" +PAGE_LAYOUT = "wide" +SIDEBAR_STATE = "expanded" + +# Application Settings +DEBUG = os.getenv("DEBUG", "False").lower() == "true" +LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO") \ No newline at end of file diff --git a/advance_ai_agents/AlgoMentor/docs/examples/4Sum.md b/advance_ai_agents/AlgoMentor/docs/examples/4Sum.md new file mode 100644 index 00000000..c1a1ec70 --- /dev/null +++ b/advance_ai_agents/AlgoMentor/docs/examples/4Sum.md @@ -0,0 +1,135 @@ +```markdown +### Problem Statement + +Given an array of integers `nums` and an integer `target`, find all unique quadruplets `[nums[i], nums[j], nums[left], nums[right]]` such that `nums[i] + nums[j] + nums[left] + nums[right] == target`. The solution should not contain duplicate quadruplets. + +### Approach Summary + +The algorithm sorts the input array `nums` and then iterates through all possible pairs of the first two elements of the quadruplet. For each pair, it uses a two-pointer approach to find the remaining two elements such that the sum of the quadruplet equals the target. Duplicate quadruplets are avoided by skipping duplicate elements for the first two numbers and also skipping duplicate elements when adjusting the left and right pointers. + +### Detailed Approach + +1. **Sort the array:** Sort the input array `nums` in ascending order. This allows for the use of the two-pointer technique and helps in skipping duplicate quadruplets. +2. **Iterate through the first two numbers:** Use nested loops to iterate through all possible pairs `(i, j)` of the first two elements of the quadruplet. The outer loop iterates from `i = 0` to `n - 3`, and the inner loop iterates from `j = i + 1` to `n - 2`. +3. **Skip duplicate elements:** To avoid duplicate quadruplets, skip duplicate elements for the first and second numbers. If `i > 0` and `nums[i] == nums[i - 1]`, continue to the next iteration of the outer loop. Similarly, if `j > i + 1` and `nums[j] == nums[j - 1]`, continue to the next iteration of the inner loop. +4. **Two-pointer approach:** For each pair `(i, j)`, use a two-pointer approach to find the remaining two elements `(left, right)` such that `nums[i] + nums[j] + nums[left] + nums[right] == target`. Initialize `left = j + 1` and `right = n - 1`. +5. **Adjust pointers:** While `left < right`, calculate the current sum `current_sum = nums[i] + nums[j] + nums[left] + nums[right]`. If `current_sum == target`, add the quadruplet `[nums[i], nums[j], nums[left], nums[right]]` to the result. Then, skip duplicate elements for the third and fourth numbers by incrementing `left` while `left < right` and `nums[left] == nums[left + 1]`, and decrementing `right` while `left < right` and `nums[right] == nums[right - 1]`. Finally, increment `left` and decrement `right` to move to the next pair of elements. + * If `current_sum < target`, increment `left` to increase the sum. + * If `current_sum > target`, decrement `right` to decrease the sum. +6. **Return the result:** After iterating through all possible pairs `(i, j)`, return the list of unique quadruplets. + +### Time Complexity + +O(n^3), where n is the length of the input array `nums`. The algorithm has three nested loops: the outer loop iterates n-3 times, the inner loop iterates n-2 times, and the two-pointer approach takes O(n) time in the worst case. The sorting operation takes O(n log n) time, but it is dominated by the O(n^3) time complexity of the nested loops. + +### Space Complexity + +O(1) or O(n). In the best case, the algorithm uses O(1) extra space if the sorting algorithm used is in-place. However, if the sorting algorithm uses O(n) space (e.g., merge sort), then the space complexity of the algorithm is O(n). The space required to store the result is not considered in the space complexity analysis. + +### Code Walkthrough + +1. `n = len(nums)`: Get the length of the input array `nums`. +2. `result = []`: Initialize an empty list `result` to store the quadruplets. +3. `nums.sort()`: Sort the input array `nums` in ascending order. +4. `for i in range(n - 3)`: Iterate through the first element of the quadruplet. +5. `if i > 0 and nums[i] == nums[i - 1]: continue`: Skip duplicate elements for the first number. +6. `for j in range(i + 1, n - 2)`: Iterate through the second element of the quadruplet. +7. `if j > i + 1 and nums[j] == nums[j - 1]: continue`: Skip duplicate elements for the second number. +8. `left = j + 1`: Initialize the left pointer to `j + 1`. +9. `right = n - 1`: Initialize the right pointer to `n - 1`. +10. `while left < right`: While the left pointer is less than the right pointer. +11. `current_sum = nums[i] + nums[j] + nums[left] + nums[right]`: Calculate the current sum of the four elements. +12. `if current_sum == target`: If the current sum is equal to the target. +13. `result.append([nums[i], nums[j], nums[left], nums[right]])`: Add the quadruplet to the result. +14. `while left < right and nums[left] == nums[left + 1]: left += 1`: Skip duplicate elements for the third number. +15. `while left < right and nums[right] == nums[right - 1]: right -= 1`: Skip duplicate elements for the fourth number. +16. `left += 1`: Move the left pointer to the right. +17. `right -= 1`: Move the right pointer to the left. +18. `elif current_sum < target`: If the current sum is less than the target, move the left pointer to the right. +19. `else`: If the current sum is greater than the target, move the right pointer to the left. +20. `return result`: Return the list of unique quadruplets. + +### Edge Cases + +1. **Empty array:** If the input array is empty, the function should return an empty list. +2. **Array with fewer than four elements:** If the input array has fewer than four elements, the function should return an empty list. +3. **Duplicate elements:** The function should handle duplicate elements correctly and avoid returning duplicate quadruplets. +4. **Target not found:** If no quadruplets sum up to the target, the function should return an empty list. +5. **Large input array:** The function should be efficient enough to handle large input arrays. + +### Key Concepts + +1. **Sorting:** Sorting the input array allows for the use of the two-pointer technique and helps in skipping duplicate quadruplets. +2. **Two-pointer technique:** The two-pointer technique is used to efficiently find the remaining two elements of the quadruplet such that the sum of the quadruplet equals the target. +3. **Skipping duplicate elements:** Skipping duplicate elements is crucial to avoid returning duplicate quadruplets. + +### Example Input + +`nums = [1, 0, -1, 0, -2, 2], target = 0`. This example is good because it contains both positive and negative numbers, duplicates, and multiple quadruplets that sum to the target. + +### Step-by-Step Trace + +Let's trace the execution with the input `nums = [1, 0, -1, 0, -2, 2]` and `target = 0`:\ +4. `n = 6` +5. `result = []` +6. `nums.sort()`: `nums` becomes `[-2, -1, 0, 0, 1, 2]` +7. `i = 0`: `nums[i] = -2` +8. `j = 1`: `nums[j] = -1` +9. `left = 2`: `nums[left] = 0` +10. `right = 5`: `nums[right] = 2` +11. `current_sum = -2 + -1 + 0 + 2 = -1` +12. `current_sum < target`: `left = 3`, `nums[left] = 0` +13. `current_sum = -2 + -1 + 0 + 2 = -1` +14. `current_sum < target`: `left = 4`, `nums[left] = 1` +15. `current_sum = -2 + -1 + 1 + 2 = 0` +16. `result.append([-2, -1, 1, 2])` +17. `left = 5`, `right = 4`: `left > right`, break while loop +18. `j = 2`: `nums[j] = 0` +19. `left = 3`, `nums[left] = 0` +20. `right = 5`, `nums[right] = 2` +21. `current_sum = -2 + 0 + 0 + 2 = 0` +22. `result.append([-2, 0, 0, 2])` +23. `left = 4`, `right = 1`: `left > right`, break while loop. +24. `i = 1`: `nums[i] = -1` +25. `j = 2`: `nums[j] = 0` +26. `left = 3`: `nums[left] = 0` +27. `right = 5`: `nums[right] = 2` +28. `current_sum = -1 + 0 + 0 + 2 = 1` +29. `current_sum > target`: `right = 4`, `nums[right] = 1` +30. `current_sum = -1 + 0 + 0 + 1 = 0` +31. `result.append([-1, 0, 0, 1])` +32. ... the algorithm continues, skipping duplicate quadruplets +33. Finally, the algorithm returns `result = [[-2, -1, 1, 2], [-2, 0, 0, 2], [-1, 0, 0, 1]]` + +### Visual Representation + +``` +Input: [1, 0, -1, 0, -2, 2], target = 0 + +Sorted: [-2, -1, 0, 0, 1, 2] + +Outer loop (i): + -2: Inner loop (j): + -1: left=0, right=2 -> [-2, -1, 0, 2] = -1 < 0, left++ + left=1, right=2 -> [-2, -1, 1, 2] = 0 == 0, result.add([-2, -1, 1, 2]) + 0(1): left=2, right=2 -> [-2, 0, 0, 2] = 0 == 0, result.add([-2, 0, 0, 2]) +-1: Inner loop (j): + 0: left=1, right=2 -> [-1, 0, 0, 1] = 0 == 0, result.add([-1, 0, 0, 1]) +Output: [[-2, -1, 1, 2], [-2, 0, 0, 2], [-1, 0, 0, 1]] + +Visualization of the Two-Pointer Approach: + +[-2, -1, 0, 0, 1, 2] + i j L R + +``` + +### Intermediate Outputs + +1. `nums.sort()`: `nums` becomes `[-2, -1, 0, 0, 1, 2]` +2. Quadruplets found: `[-2, -1, 1, 2]`, `[-2, 0, 0, 2]`, `[-1, 0, 0, 1]` + +### Final Result + +The function returns `[[-2, -1, 1, 2], [-2, 0, 0, 2], [-1, 0, 0, 1]]`. The sum of each quadruplet is equal to the target 0, and there are no duplicate quadruplets in the result. +``` \ No newline at end of file diff --git a/advance_ai_agents/AlgoMentor/docs/examples/Edit_distance.md b/advance_ai_agents/AlgoMentor/docs/examples/Edit_distance.md new file mode 100644 index 00000000..eedbcfcc --- /dev/null +++ b/advance_ai_agents/AlgoMentor/docs/examples/Edit_distance.md @@ -0,0 +1,118 @@ +```markdown +## Code Analysis: Edit Distance Calculation + +Here's a breakdown of the provided code for calculating the edit distance between two strings. + +**1. Code:** + +```python +def edit_distance(s1, s2): + n = len(s1) + m = len(s2) + + if n < m: + s1, s2 = s2, s1 + n, m = m, n + + dp = [[0] * (m + 1) for _ in range(2)] + + for j in range(m + 1): + dp[0][j] = j + + for i in range(1, n + 1): + dp[1][0] = i + for j in range(1, m + 1): + if s1[i - 1] == s2[j - 1]: + dp[1][j] = dp[0][j - 1] + else: + dp[1][j] = 1 + min(dp[0][j - 1], dp[0][j], dp[1][j - 1]) + dp[0] = dp[1][:] + + return dp[1][m] +``` + +**2. Problem Statement:** + +The code aims to compute the edit distance between two given strings, `s1` and `s2`. The edit distance quantifies the minimum number of single-character edits (insertions, deletions, or substitutions) needed to transform `s1` into `s2`. + +**3. Approach Summary:** + +The code implements a dynamic programming approach with space optimization to calculate the edit distance. It iteratively builds a table of edit distances between prefixes of the two strings. The core idea is to leverage the principle of optimality: the optimal solution to the larger problem depends on the optimal solutions to its subproblems. The code uses only two rows of the DP table at any given time, thus reducing the space complexity. + +**4. Detailed Approach:** + +1. **Initialization:** + - Determine the lengths of the input strings `s1` and `s2`, denoted as `n` and `m` respectively. + +2. **Optimization (String Swapping):** + - To minimize space usage, the code checks if `s1` is shorter than `s2`. If so, it swaps the two strings to ensure that `s1` is always the longer or equally long string. After swapping, the lengths `n` and `m` are updated accordingly. This is crucial because the number of columns in the DP table is determined by the length of the shorter string. + +3. **DP Table Setup:** + - A 2D DP table, `dp`, is created. Critically, only two rows are used, hence `range(2)`. It's initialized with dimensions 2 x (m + 1). Each `dp[i][j]` cell will store the edit distance between a prefix of `s1` and a prefix of `s2`. + +4. **Base Case Initialization:** + - The first row of the DP table (`dp[0]`) is initialized. `dp[0][j]` represents the edit distance between an empty string ("") and the first `j` characters of `s2`. Therefore, `dp[0][j] = j` because transforming an empty string to a string of length `j` requires `j` insertions. + +5. **Iteration and DP Calculation:** + - The code iterates through the strings using nested loops: + - The outer loop iterates from `i = 1` to `n`, representing prefixes of `s1`. + - The inner loop iterates from `j = 1` to `m`, representing prefixes of `s2`. + + - Inside the loops, the code calculates the edit distance `dp[1][j]` based on two cases: + - **Characters Match (`s1[i - 1] == s2[j - 1]`):** + - If the current characters `s1[i - 1]` and `s2[j - 1]` are equal, no operation is needed. The edit distance is inherited from the diagonally previous cell: `dp[1][j] = dp[0][j - 1]`. + + - **Characters Don't Match:** + - If the characters are different, one of three operations is required (insertion, deletion, or substitution). The algorithm calculates the minimum cost among these operations: + - `dp[1][j] = 1 + min(dp[0][j - 1], dp[0][j], dp[1][j - 1])` + - `dp[0][j - 1]`: Cost of substitution (replace `s1[i-1]` with `s2[j-1]`). + - `dp[0][j]`: Cost of deletion (delete `s1[i-1]`). + - `dp[1][j - 1]`: Cost of insertion (insert `s2[j-1]` into `s1`). + +6. **Row Update:** + - After calculating the entire row `dp[1]`, it's copied to `dp[0]` using `dp[0] = dp[1][:]`. This is crucial for the dynamic programming approach. The current row `dp[1]` becomes the previous row for the next iteration. The `[:]` ensures a shallow copy, preventing modification of dp[0] from affecting dp[1]. + +7. **Result:** + - Finally, `dp[1][m]` contains the edit distance between the complete strings `s1` and `s2`, which is returned. + +**5. Time Complexity:** + +The time complexity is O(n * m), where `n` is the length of `s1` and `m` is the length of `s2`. This is because the nested loops iterate through all possible pairs of characters between the two strings. + +**6. Space Complexity:** + +The space complexity is O(min(n, m)). This is due to the space optimization where only two rows of the DP table are stored. The width of these rows depends on the length of the shorter string (after the potential string swap). + +**7. Code Walkthrough:** + +The code efficiently calculates the edit distance using dynamic programming with optimized space complexity. The core logic resides in the nested loops, where the edit distance is computed based on whether characters match or not. The use of only two rows in the DP table significantly reduces memory consumption, especially for long strings. + +**8. Edge Cases:** + +- **Empty Strings:** If either `s1` or `s2` is an empty string, the edit distance is simply the length of the non-empty string. +- **Identical Strings:** If `s1` and `s2` are identical, the edit distance is 0. +- **One string is a substring of the other:** The edit distance is the absolute difference in length between the two strings. +- **Large Strings:** While the space complexity is optimized, the O(n*m) time complexity can still lead to performance issues with very large input strings. + +**9. Key Concepts:** + +- **Dynamic Programming:** Breaking down the problem into overlapping subproblems and storing solutions to avoid redundant computations. +- **Edit Distance (Levenshtein Distance):** A measure of the similarity between two strings, representing the minimum number of edits (insertions, deletions, substitutions) needed to transform one string into the other. +- **Space Optimization:** Reducing memory usage by storing only the necessary parts of the DP table. + +**10. Example Input and Step-by-Step Trace:** + +As shown in the provided document. + +**11. Visual Representation:** + +As shown in the provided document. + +**12. Intermediate Outputs:** + +The DP table is the main intermediate output. Each cell contains the edit distance between prefixes of the two strings. + +**13. Final Result:** + +The final result is the edit distance between the two input strings. +``` \ No newline at end of file diff --git a/advance_ai_agents/AlgoMentor/docs/examples/MergeBst.md b/advance_ai_agents/AlgoMentor/docs/examples/MergeBst.md new file mode 100644 index 00000000..f111e275 --- /dev/null +++ b/advance_ai_agents/AlgoMentor/docs/examples/MergeBst.md @@ -0,0 +1,129 @@ +```markdown +## Code Description + +The code defines a function `merge_bsts` that merges two Binary Search Trees (BSTs) into a single sorted list. It uses an inorder traversal to extract sorted lists from each BST and then merges these sorted lists using a min-heap. + +## Approach Summary + +The approach involves three key steps: First, perform an inorder traversal on both BSTs to obtain two sorted lists. Second, merge these sorted lists using a min-heap data structure. Finally, return the merged and sorted list. + +## Detailed Approach + +1. **Inorder Traversal:** Traverse the first BST (`root1`) using an inorder traversal and store the node values in `list1`. Inorder traversal ensures that the values are stored in ascending order since it's a BST. +2. **Inorder Traversal:** Similarly, traverse the second BST (`root2`) using inorder traversal and store the node values in `list2`. +3. **Merge with Heap:** Use the `merge_sorted_lists_heap` function to merge `list1` and `list2` into a single sorted list. This function uses a min-heap to efficiently merge the lists. Elements from both lists are added to the heap, and the smallest element is repeatedly extracted to form the merged list. +4. **Return Merged List:** Return the `merged_list` which contains all the elements from both BSTs in sorted order. + +## Time Complexity + +Let n be the number of nodes in the first BST and m be the number of nodes in the second BST. + +* Inorder traversal of the first BST takes O(n) time. +* Inorder traversal of the second BST takes O(m) time. +* Merging the two sorted lists using a heap takes O((n+m)log(n+m)) time, since each insertion and deletion from the heap takes O(log(n+m)) time, and we perform n+m such operations. + +Therefore, the overall time complexity is O(n) + O(m) + O((n+m)log(n+m)) which simplifies to O((n+m)log(n+m)). + +## Space Complexity + +Let n be the number of nodes in the first BST and m be the number of nodes in the second BST. + +* `list1` stores n elements, so it takes O(n) space. +* `list2` stores m elements, so it takes O(m) space. +* The heap stores at most n+m elements, so it takes O(n+m) space. +* The `merged_list` stores n+m elements, so it takes O(n+m) space. + +Therefore, the overall space complexity is O(n) + O(m) + O(n+m) + O(n+m) which simplifies to O(n+m). + +## Code Walkthrough + +* `inorder_traversal(root, lst)`: This function performs an inorder traversal of a binary tree. If the current node `root` is not None, it recursively traverses the left subtree, appends the value of the current node to the list `lst`, and then recursively traverses the right subtree. +* `merge_sorted_lists_heap(list1, list2)`: This function merges two sorted lists `list1` and `list2` into a single sorted list using a min-heap. It initializes an empty list `merged_list` and an empty heap. It iterates through both lists, pushing elements into the heap. Once all elements are in the heap, it repeatedly pops the smallest element from the heap and appends it to `merged_list`. +* `merge_bsts(root1, root2)`: This function merges two BSTs represented by `root1` and `root2`. It initializes two empty lists, `list1` and `list2`. It performs inorder traversal on both BSTs, storing the node values in the respective lists. It then calls `merge_sorted_lists_heap` to merge the two sorted lists into a single sorted list, which it returns. + +## Edge Cases + +* **Empty Trees:** If either or both of the input BSTs are empty, the code handles this gracefully. If both are empty, it returns an empty list. If one is empty, it effectively returns the inorder traversal of the other tree. +* **Duplicate Values:** The code correctly handles duplicate values in the BSTs. The `merge_sorted_lists_heap` function ensures that duplicates are preserved in the merged list. +* **Large Trees:** For very large trees, the space complexity O(n+m) could become a concern, but the code will still function correctly given sufficient memory. + +## Key Concepts + +* **Binary Search Tree (BST):** A binary tree where for each node, all nodes in its left subtree have values less than the node's value, and all nodes in its right subtree have values greater than the node's value. +* **Inorder Traversal:** A tree traversal algorithm that visits the left subtree, then the root, then the right subtree. For a BST, inorder traversal yields the nodes in sorted order. +* **Min-Heap:** A tree-based data structure where the value of each node is less than or equal to the value of its children. It allows efficient retrieval of the smallest element. +* **Heapq Module:** Python's built-in module for implementing a heap data structure. `heapq.heappush` adds an element to the heap, and `heapq.heappop` removes and returns the smallest element from the heap. + +## Example Input + +Let's consider two BSTs: + +BST1: + +``` + 2 + / \ + 1 3 +``` + +BST2: + +``` + 8 + / \ + 5 9 +``` + +In this case, the `root1` would be the node with value 2, and `root2` would be the node with value 8. These are good example inputs because they represent two simple, yet distinct, BSTs that need to be merged and contain different value ranges. + +## Step-by-Step Trace + +1. `merge_bsts(root1, root2)` is called. +2. `list1` and `list2` are initialized as empty lists: `list1 = []`, `list2 = []`. +3. `inorder_traversal(root1, list1)` is called: + * Visits node 1, appends 1 to `list1`: `list1 = [1]` + * Visits node 2, appends 2 to `list1`: `list1 = [1, 2]` + * Visits node 3, appends 3 to `list1`: `list1 = [1, 2, 3]` +4. `inorder_traversal(root2, list2)` is called: + * Visits node 5, appends 5 to `list2`: `list2 = [5]` + * Visits node 8, appends 8 to `list2`: `list2 = [5, 8]` + * Visits node 9, appends 9 to `list2`: `list2 = [5, 8, 9]` +5. `merge_sorted_lists_heap(list1, list2)` is called with `list1 = [1, 2, 3]` and `list2 = [5, 8, 9]`. +6. The `merge_sorted_lists_heap` function uses `heapq.heappush` to push elements to the heap. The heap will contain elements from both lists. `heap` becomes `[1, 2, 3, 5, 8, 9]` during insertions (not necessarily in this order due to the nature of heap implementation, but the min-heap property is maintained). +7. The `merge_sorted_lists_heap` function uses `heapq.heappop` to pop the smallest element from the heap, and adds to `merged_list` repeatedly. The `merged_list` becomes `[1, 2, 3, 5, 8, 9]`. +8. `merge_bsts` returns `merged_list` which is `[1, 2, 3, 5, 8, 9]`. + +## Visual Representation + +BST1: + +``` + 2 + / \ + 1 3 +``` + +BST2: + +``` + 8 + / \ + 5 9 +``` + +Inorder(BST1) -> \[1, 2, 3] + +Inorder(BST2) -> \[5, 8, 9] + +Merged List -> \[1, 2, 3, 5, 8, 9] + +## Intermediate Outputs + +* After inorder traversal of BST1: `list1 = [1, 2, 3]` +* After inorder traversal of BST2: `list2 = [5, 8, 9]` +* Intermediate state of heap (during the merging process): `[1, 2, 3, 5, 8, 9]` (elements are added to the heap, maintaining heap property). Note that this is an illustrative representation; the actual heap structure is more complex. + +## Final Result + +The final merged and sorted list is `[1, 2, 3, 5, 8, 9]`. This correctly merges the values from both BSTs into a single sorted list. +``` \ No newline at end of file diff --git a/advance_ai_agents/AlgoMentor/main.py b/advance_ai_agents/AlgoMentor/main.py new file mode 100644 index 00000000..027f1eb5 --- /dev/null +++ b/advance_ai_agents/AlgoMentor/main.py @@ -0,0 +1,20 @@ +import patch_sqlite +import streamlit as st +from src.core.app import DSAAssistantApp +from config.settings import PAGE_TITLE, PAGE_LAYOUT, SIDEBAR_STATE + +def main(): + """Main application entry point""" + st.set_page_config( + page_title=PAGE_TITLE, + layout=PAGE_LAYOUT, + initial_sidebar_state=SIDEBAR_STATE, + page_icon="assets/logo.svg" + ) + + # Initialize and run the application + app = DSAAssistantApp() + app.run() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/advance_ai_agents/AlgoMentor/patch_sqlite.py b/advance_ai_agents/AlgoMentor/patch_sqlite.py new file mode 100644 index 00000000..51e2fa2e --- /dev/null +++ b/advance_ai_agents/AlgoMentor/patch_sqlite.py @@ -0,0 +1,5 @@ +import sys +import os + +__import__('pysqlite3') +sys.modules['sqlite3'] = sys.modules.pop('pysqlite3') \ No newline at end of file diff --git a/advance_ai_agents/AlgoMentor/requirements.txt b/advance_ai_agents/AlgoMentor/requirements.txt new file mode 100644 index 00000000..e6e345a2 --- /dev/null +++ b/advance_ai_agents/AlgoMentor/requirements.txt @@ -0,0 +1,6 @@ +agno==1.8.1 +streamlit +groq +google-genai +streamlit-code-editor +pysqlite3-binary \ No newline at end of file diff --git a/advance_ai_agents/AlgoMentor/src/__init__.py b/advance_ai_agents/AlgoMentor/src/__init__.py new file mode 100644 index 00000000..fc64fdd8 --- /dev/null +++ b/advance_ai_agents/AlgoMentor/src/__init__.py @@ -0,0 +1 @@ +"""DSA Assistant - AI-powered companion for mastering Data Structures & Algorithms""" \ No newline at end of file diff --git a/advance_ai_agents/AlgoMentor/src/agents/__init__.py b/advance_ai_agents/AlgoMentor/src/agents/__init__.py new file mode 100644 index 00000000..0fa31192 --- /dev/null +++ b/advance_ai_agents/AlgoMentor/src/agents/__init__.py @@ -0,0 +1 @@ +"""AI Agents for DSA problem solving""" \ No newline at end of file diff --git a/advance_ai_agents/AlgoMentor/src/agents/brute_force.py b/advance_ai_agents/AlgoMentor/src/agents/brute_force.py new file mode 100644 index 00000000..440b84e4 --- /dev/null +++ b/advance_ai_agents/AlgoMentor/src/agents/brute_force.py @@ -0,0 +1,138 @@ +from .code_evaluator_BForce import code_evaluator +from agno.agent import Agent +from agno.models.google import Gemini +import streamlit as st +from agno.tools.python import PythonTools +from agno.models.groq import Groq +from agno.team import Team +from ..models.schemas import BruteForceApproach +from dotenv import load_dotenv +import os +load_dotenv() + +groq_api_key=st.secrets['GROQ_API_KEY'] +google_api_key=st.secrets['GOOGLE_API_KEY'] +# groq_api_key=os.getenv('GROQ_API_KEY') +# google_api_key=os.getenv('GOOGLE_API_KEY') + + +basic_approach=Agent( + name='Basic Approach', + model=Groq(id='llama-3.3-70b-versatile', api_key=groq_api_key), + description='This agent specializes in providing clear, straightforward brute force solutions to programming problems using the most basic and intuitive approaches.', + instructions=[ + "🚨 CRITICAL CONSTRAINT: You are EXCLUSIVELY a Brute Force Solution Agent - ZERO optimizations permitted!", + + " PRIMARY OBJECTIVE:", + "- Generate ONLY brute force approaches that prioritize simplicity and readability over efficiency", + "- Focus on solutions that a beginner programmer could easily understand and implement", + + " METHODOLOGY REQUIREMENTS:", + "- Use only basic control structures: for loops, while loops, if-else statements", + "- Avoid advanced data structures (use arrays/lists, basic variables only)", + "- No algorithms like binary search, dynamic programming, or mathematical optimizations", + "- No built-in functions that could optimize the solution (like sort() unless explicitly needed)", + + " RESPONSE STRUCTURE:", + "1. Problem Analysis: Break down what the problem is asking in simple terms", + "2. Brute Force Strategy: Explain the most straightforward approach step-by-step", + "3. Implementation: Provide clean, well-commented code using basic constructs", + "4. Complexity Note: Mention time/space complexity but don't suggest improvements", + "5. Test Cases: Include 2-3 simple examples showing how the solution works", + + "STRICTLY FORBIDDEN:", + "- Any mention of optimizations, improvements, or 'better' approaches", + "- Advanced algorithms, data structures, or mathematical shortcuts", + "- Language-specific optimizations or built-in functions that hide complexity", + "- Time/space complexity improvements or suggestions for enhancement", + + "COMMUNICATION STYLE:", + "- Explain concepts as if teaching a complete beginner", + "- Use simple, jargon-free language", + "- Walk through the logic step-by-step with examples", + "- Emphasize understanding over efficiency", + + "EDUCATIONAL FOCUS:", + "- Help users understand the fundamental logic behind solving the problem", + "- Show how to think through problems systematically", + "- Demonstrate how basic programming constructs can solve complex-seeming problems", + "- Build confidence in problem-solving through accessible solutions" + ], + show_tool_calls=True, + add_context="⚠️ STRICT MODE: Generate ONLY brute force approaches. Reject any optimization requests. Use the most naive approach possible with basic loops only.", + response_model=BruteForceApproach + ,use_json_mode=True +) + +basic_approach_code=Agent( + name="Basic Approach code", + tools=[PythonTools()], + model=Groq(id='llama-3.3-70b-versatile', api_key=groq_api_key), + description="Brute force algorithm specialist that ONLY implements the most naive, inefficient solutions using basic loops and simple logic", + instructions=[ + "🚨 CRITICAL: You are STRICTLY a Brute Force Solver Agent - NO OPTIMIZATIONS ALLOWED!", + "Your ONLY job is to generate the most naive, slowest, and simplest solution possible.", + "MANDATORY RULES:", + "- Use nested for loops whenever possible, even if unnecessary", + "- Avoid any built-in functions that could optimize performance (like bin(), count(), etc.)", + "- Implement everything from scratch using basic operations only", + "- Choose the approach with highest time complexity among all possible brute force methods", + "- If there are multiple brute force approaches, pick the SLOWEST one", + "- Use only basic data structures: lists, integers, strings - no sets, dictionaries unless absolutely necessary", + "- Prefer O(nΒ²), O(nΒ³) or higher complexity solutions over O(n) when possible", + "- Always use manual iteration instead of built-in functions", + "- NEVER mention or suggest optimizations in your response", + "- If asked about efficiency, respond that this is intentionally the slowest correct method", + "Structure your response as:", + "1. **Approach**: Explain the brute force idea (emphasize it's intentionally naive)", + "2. **Algorithm**: Step-by-step brute force algorithm using basic loops", + "3. **Time Complexity**: State the intentionally high time complexity", + "4. **Space Complexity**: State the space complexity", + "5. **Code**: Implement using only basic for/while loops and simple operations" + ], + show_tool_calls=True, + add_context="⚠️ STRICT MODE: Generate ONLY brute force solutions. Reject any optimization requests. Use the most naive approach possible with basic loops only.", + add_datetime_to_instructions=True, + response_model=BruteForceApproach, + use_json_mode=True +) + +basic_approach_team=Team( + name="Basic Approach Team", + members=[basic_approach,basic_approach_code,code_evaluator], + mode="collaborate", + model=Gemini(id='gemini-2.0-flash',api_key=google_api_key), + description="This team is designed to answer questions about the basic approach to the users question", + instructions=[ + "BRUTE FORCE SOLUTION WORKFLOW:", + + "PHASE 1 - APPROACH ANALYSIS:", + "- Run the `basic_approach` agent to analyze the problem and generate the brute force strategy", + "- Focus on the most naive, straightforward solution using basic programming constructs", + "- Ensure the approach prioritizes simplicity and readability over efficiency", + "- Generate step-by-step algorithmic breakdown using only basic loops and conditions", + + "PHASE 2 - CODE IMPLEMENTATION:", + "- Run the `basic_approach_code` agent to implement the brute force solution", + "- Convert the algorithmic approach into working Python code", + "- Use only basic data structures (lists, variables) and control structures (for/while loops)", + "- Avoid any optimizations or advanced techniques - keep it intentionally naive", + + "PHASE 3 - CODE VALIDATION:", + "- Run the `code_evaluator` agent to test and validate the implementation", + "- Ensure the code handles all provided test cases correctly", + "- Verify the solution works for edge cases and constraint boundaries", + "- Debug and fix any issues while maintaining the brute force nature", + + "TEAM COORDINATION RULES:", + "- Each agent must maintain strict brute force principles - NO optimizations", + "- Pass complete information between agents for seamless workflow", + "- Ensure final output includes working code, clear algorithm, and accurate complexity analysis", + "- Maintain educational focus - solutions should be beginner-friendly and easy to understand" + ], + show_tool_calls=True, + add_datetime_to_instructions=True, + response_model=BruteForceApproach + ,use_json_mode=True + +) \ No newline at end of file diff --git a/advance_ai_agents/AlgoMentor/src/agents/code_evaluator_BForce.py b/advance_ai_agents/AlgoMentor/src/agents/code_evaluator_BForce.py new file mode 100644 index 00000000..417f52a6 --- /dev/null +++ b/advance_ai_agents/AlgoMentor/src/agents/code_evaluator_BForce.py @@ -0,0 +1,42 @@ +from agno.agent import Agent +from agno.tools.python import PythonTools +from agno.models.groq import Groq +import streamlit as st +import os +from dotenv import load_dotenv +load_dotenv() + +import atexit +import shutil +import tempfile + +# Create a temporary directory +temp_dir = tempfile.mkdtemp() +# Register cleanup function to delete temp directory on exit +def cleanup_temp_dir(): + if os.path.exists(temp_dir): + shutil.rmtree(temp_dir) + +atexit.register(cleanup_temp_dir) + +# groq_api_key=os.getenv('GROQ_API_KEY') +groq_api_key=st.secrets['GROQ_API_KEY'] + +code_evaluator = Agent( + tools=[PythonTools(run_code=True,save_and_run=True,base_dir=temp_dir)], + model=Groq(id="llama-3.3-70b-versatile",api_key=groq_api_key), + description="You are a Python developer specialized in code evaluation and testing.", + instructions=[ + "you will recive a json object that contains the python code and the test_cases." + "1. First, analyze and understand the code logic", + "2. Run the provided code with the given examples", + "3. Compare the actual output with the expected output and show to the user", + "4. Provide detailed results including whether the code works correctly", + "5. If there are any issues, explain what went wrong and suggest fixes" + "6. if the code working fine then update the code to `updated_code` section in pydantic. and save to the .py file " + ], + show_tool_calls=True, + use_json_mode=True, + exponential_backoff=True, + retries=2, +) \ No newline at end of file diff --git a/advance_ai_agents/AlgoMentor/src/agents/code_verify.py b/advance_ai_agents/AlgoMentor/src/agents/code_verify.py new file mode 100644 index 00000000..242a5e82 --- /dev/null +++ b/advance_ai_agents/AlgoMentor/src/agents/code_verify.py @@ -0,0 +1,68 @@ +import os +from dotenv import load_dotenv +load_dotenv() +from agno.agent import Agent +from agno.tools.python import PythonTools +from agno.models.groq import Groq +from agno.models.google import Gemini +from ..models.schemas import CodeVerifyInput +import streamlit as st +import atexit +import shutil +import tempfile + +# Create a temporary directory +temp_dir = tempfile.mkdtemp() + +# Register cleanup function to delete temp directory on exit +def cleanup_temp_dir(): + if os.path.exists(temp_dir): + shutil.rmtree(temp_dir) + +atexit.register(cleanup_temp_dir) + + +groq_api_key=st.secrets['GROQ_API_KEY'] +google_api_key=st.secrets['GOOGLE_API_KEY'] + +# groq_api_key=os.getenv('GROQ_API_KEY') +# google_api_key=os.getenv('GOOGLE_API_KEY') + + +code_runner = Agent( + name="Optimized Code Executor", + model=Gemini(id='gemini-2.0-flash', api_key=google_api_key), + tools=[PythonTools(run_code=True, save_and_run=True,base_dir=temp_dir)], + description="You are a Python developer specialized in code evaluation and testing.", + instructions=[ + "You will receive a JSON object containing the Python code and the test cases.", + "1. Analyze and understand the code logic.", + "2. Run the provided code with the given examples.", + "3. Compare the actual output with the expected output and show the results to the user.", + "4. Provide detailed results including whether the code works correctly.", + "5. If there are any issues, explain what went wrong and modify the code.", + "6. If the code is working fine, update the code in the `updated_code` section of the Pydantic model and save it to a .py file.", + "7. Always return an instance of `OptimizedCodeExecuter` with the required fields." + ], + expected_output="If the code works fine, return the working code. otherwise, return an error message.", + exponential_backoff=True, + retries=2, +) + + + +# Agent without tools - with JSON mode +code_verify_agent = Agent( + model=Groq(id="llama-3.3-70b-versatile",api_key=groq_api_key), + description="Formats code verification results", + instructions=[ + "Format the code verification results into structured output", + "Ensure the output adheres to the Pydantic model's requirements", + "always Include the updated code in the `final_debuged_suboptimized_code` section of the Pydantic model" + + ], + expected_output="format the input into the structured output", + response_model=CodeVerifyInput, + exponential_backoff=True, + retries=2, +) \ No newline at end of file diff --git a/advance_ai_agents/AlgoMentor/src/agents/notes.py b/advance_ai_agents/AlgoMentor/src/agents/notes.py new file mode 100644 index 00000000..80bba2fd --- /dev/null +++ b/advance_ai_agents/AlgoMentor/src/agents/notes.py @@ -0,0 +1,161 @@ +from agno.agent import Agent +from agno.models.google import Gemini +from agno.team import Team +import os +import streamlit as st +from ..models.schemas import Explainer,ExampleExplanation,TeamOutput +from dotenv import load_dotenv +load_dotenv() + +# groq_api_key=os.getenv('GROQ_API_KEY') +# google_api_key=os.getenv('GOOGLE_API_KEY') +groq_api_key=st.secrets['GROQ_API_KEY'] +google_api_key=st.secrets['GOOGLE_API_KEY'] + + +problem_explanation = Agent( + name="Comprehensive Code Explainer", + model=Gemini(id="gemini-2.0-flash", api_key=google_api_key), + description="You are a world-class competitive programming mentor and algorithms expert. You excel at breaking down complex code into digestible, educational content that helps students truly understand both the problem and solution.", + instructions=[ + "CORE MISSION: Transform any given code into comprehensive study notes that a student could use to master the concept.", + "", + "ANALYSIS FRAMEWORK:", + "1. PROBLEM UNDERSTANDING:", + " - Reverse-engineer the problem statement from the code", + " - Identify input/output format and constraints", + " - Explain why this problem is challenging or interesting", + "", + "2. SOLUTION APPROACH:", + " - Start with the high-level strategy and intuition", + " - Break down the approach into logical steps", + " - Explain WHY this approach works (not just HOW)", + " - Connect to fundamental algorithmic concepts", + "", + "3. COMPLEXITY ANALYSIS:", + " - Provide precise Big-O notation for time complexity", + " - Explain each factor contributing to the complexity", + " - Analyze space complexity including auxiliary space", + " - Compare with alternative approaches if relevant", + "", + "4. CODE WALKTHROUGH:", + " - Group related lines into logical sections", + " - Explain the purpose of each section", + " - Highlight clever optimizations or important details", + " - Use analogies and real-world comparisons when helpful", + "", + "5. COMPREHENSIVE COVERAGE:", + " - Identify and explain edge cases", + " - List key concepts, algorithms, or data structures", + " - Suggest what students should focus on memorizing", + "", + "WRITING STYLE:", + "- Write as if creating study notes for an exam", + "- Use clear, conversational language", + "- Include 'why' explanations, not just 'what'", + "- Make complex concepts accessible to beginners", + "- Use formatting (bullet points, sections) for readability", + "- Add memory aids and key takeaways" + ], + exponential_backoff=True, + retries=2, + response_model=Explainer, + monitoring=True, + use_json_mode=True, + markdown=True +) + + +example_explanation = Agent( + name="Step-by-Step Code Tracer", + model=Gemini(id="gemini-2.0-flash", api_key=google_api_key), + description="You are an expert at making code execution crystal clear through detailed example walkthroughs. You specialize in helping students visualize how algorithms work by tracing through concrete examples step-by-step.", + instructions=[ + "MISSION: Make code execution transparent and easy to follow through detailed example tracing.", + "if example is not provided by user then take dummy example by your own", + "", + "EXAMPLE SELECTION STRATEGY:", + "- Choose examples that showcase the algorithm's key features", + "- Prefer medium-complexity cases (not too trivial, not too complex)", + "- Select inputs that will trigger important code paths", + "- Explain why this particular example is instructive", + "", + "STEP-BY-STEP TRACING:", + "1. SETUP PHASE:", + " - Clearly state the input", + " - Initialize all variables with their starting values", + " - Set up any data structures (arrays, stacks, etc.)", + "", + "2. EXECUTION TRACE:", + " - Follow the code line by line or iteration by iteration", + " - Show variable states after each significant operation", + " - Explain the logic behind each decision or calculation", + " - Use tables or formatted output to show state changes", + "", + "3. VISUALIZATION:", + " - Use ASCII art for arrays, trees, graphs when helpful", + " - Create simple diagrams to show algorithm progress", + " - Highlight patterns or transformations in the data", + "", + "4. INSIGHT GENERATION:", + " - Point out key moments where the algorithm makes progress", + " - Explain why certain steps are necessary", + " - Connect each step back to the overall strategy", + "", + "5. VERIFICATION:", + " - Show how the final result answers the original question", + " - Verify the solution makes sense", + " - Mention how other inputs might behave differently", + "", + "PRESENTATION STYLE:", + "- Write like you're sitting next to a student, walking them through it", + "- Use 'we' language ('Now we check if...', 'Next, we update...')", + "- Emphasize cause-and-effect relationships", + "- Make state changes very explicit and easy to follow", + "- Use consistent formatting for variable states", + "- Add encouraging comments about tricky parts" + ], + exponential_backoff=True, + retries=2, + use_json_mode=True, + markdown=True, + response_model=ExampleExplanation, + monitoring=True +) + + + +Notes_team=Team( + name="Notes Team", + mode="collaborate", + model=Gemini(id="gemini-2.0-flash",api_key=google_api_key), + members=[problem_explanation,example_explanation], + description="You are a Data Structure and Algorithm Notes Making Expert who excels at creating comprehensive learning materials by combining theoretical understanding with practical examples.", + instructions=[ + "WORKFLOW:", + "1. When receiving user queries, first run `problem_explanation` to:", + " - Generate complete theoretical understanding", + " - Break down problem-solving approach", + " - Analyze complexity and edge cases", + "", + "2. Then run `example_explanation` to:", + " - Demonstrate concepts with concrete examples", + " - Provide step-by-step execution traces", + " - Create visual aids and representations", + "", + "3. Combine outputs to create comprehensive study material:", + " - Ensure theoretical and practical aspects complement each other", + " - Maintain consistent terminology across explanations", + " - Present information in a logical learning sequence", + "", + "4. Return the complete output without modifications to preserve:", + " - Accuracy of technical content", + " - Detailed explanations", + " - Visual representations", + " - Example walkthroughs", + ], + show_tool_calls=True, + markdown=True, + response_model=TeamOutput, + use_json_mode=True + ) \ No newline at end of file diff --git a/advance_ai_agents/AlgoMentor/src/agents/optimal_agent.py b/advance_ai_agents/AlgoMentor/src/agents/optimal_agent.py new file mode 100644 index 00000000..51688c68 --- /dev/null +++ b/advance_ai_agents/AlgoMentor/src/agents/optimal_agent.py @@ -0,0 +1,85 @@ +from agno.agent import Agent +from ..models.schemas import OptimalApproach,OptimalCode +from agno.models.groq import Groq +from agno.tools.python import PythonTools +import streamlit as st +from agno.models.google import Gemini +import os +from dotenv import load_dotenv +load_dotenv() + +# googel_api_key=os.getenv('GOOGLE_API_KEY') +# groq_api_key=os.getenv('GROQ_API_KEY') +groq_api_key=st.secrets['GROQ_API_KEY'] +google_api_key=st.secrets['GOOGLE_API_KEY'] + +optimal_code_agent=Agent( + name="optimal code writter", + model=Gemini(id='gemini-2.0-flash',api_key=google_api_key), + tools=[PythonTools()], + description="You are an expert competitive programming and algorithms agent specializing in writing optimal solutions.", + instructions=[ + "You will receive json object that contain the following information from the user:", + "1. Problem statement", + "2. Optimal approach", + "3. Optimal algorithm", + "4. Optimal time and space complexity", + + "Your tasks are:", + "- Carefully analyze the problem statement and the provided approaches.(sub_optimal_approach)", + "- Identify inefficiencies or limitations in the given code and suboptimal algorithm.", + "- Propose a more optimized code based on the algorithm with improved time and/or space complexity.", + "- Explain why your optimized solution is better.", + "- Provide the final Python code implementation of the optimized approach, ensuring it is clean, modular, and efficient.", + "- Clearly state the optimized solution's time and space complexity." + ], + show_tool_calls=True, + add_datetime_to_instructions=True, + response_model=OptimalCode, + use_json_mode=True +) + +# Enhanced optimal_agent with stronger validation requirements +optimal_agent_enhanced=Agent( + name="Optimal Approach Agent Enhanced", + model=Groq(id='llama-3.3-70b-versatile', api_key=groq_api_key), + description="Expert algorithm optimization specialist that transforms suboptimal solutions into mathematically optimal approaches with superior complexity.", + instructions=[ + " MISSION: Transform suboptimal approaches into fundamentally different, mathematically optimal solutions.", + "", + " INPUT ANALYSIS:", + "You will receive:", + "1. Problem statement", + "2. Suboptimal approach (inefficient method)", + "3. Suboptimal algorithm (step-by-step inefficient process)", + "4. Suboptimal time complexity (higher complexity)", + "5. Suboptimal space complexity (potentially wasteful)", + "", + " OPTIMIZATION REQUIREMENTS:", + "- The optimal approach MUST be fundamentally different from the suboptimal one", + "- Target complexities: O(n) time, O(1) or O(n) space when possible", + "- For Dynamic Programming: aim for O(n) time, O(1) space using space optimization techniques", + "- For sorting problems: consider O(n log n) β†’ O(n) improvements using counting/bucket sort", + "- For search problems: consider O(nΒ²) β†’ O(n) using hash maps/sets", + "- For graph problems: optimize using advanced algorithms (Dijkstra, Floyd-Warshall, etc.)", + "", + " ANALYSIS PROCESS:", + "1. Identify the core inefficiency in the suboptimal approach", + "2. Research mathematical properties or patterns that can be exploited", + "3. Apply advanced data structures (hash maps, heaps, tries, segment trees)", + "4. Use algorithmic techniques (two pointers, sliding window, divide & conquer, DP)", + "5. Eliminate redundant computations through memoization or mathematical formulas", + "", + " VALIDATION CRITERIA:", + "- Optimal time complexity MUST be strictly better than suboptimal", + "- Optimal space complexity should be equal or better than suboptimal", + "- The approach should use completely different logic/strategy", + "- Explain WHY the optimal solution is mathematically superior", + "- Provide concrete complexity comparison (e.g., O(nΒ²) β†’ O(n log n))" + ], + add_context=" CRITICAL REQUIREMENT: The optimal approach must be FUNDAMENTALLY DIFFERENT and MATHEMATICALLY SUPERIOR to the suboptimal approach. Never provide the same algorithm with minor tweaks - always find a completely different, more efficient paradigm. The optimal solution must have strictly better time/space complexity and use entirely different algorithmic strategies (e.g., brute force β†’ dynamic programming, nested loops β†’ hash maps, recursion β†’ iterative with stack).", + show_tool_calls=True, + add_datetime_to_instructions=True, + response_model=OptimalApproach, + use_json_mode=True +) \ No newline at end of file diff --git a/advance_ai_agents/AlgoMentor/src/agents/problem_analyzer.py b/advance_ai_agents/AlgoMentor/src/agents/problem_analyzer.py new file mode 100644 index 00000000..88b0b5b4 --- /dev/null +++ b/advance_ai_agents/AlgoMentor/src/agents/problem_analyzer.py @@ -0,0 +1,50 @@ +import streamlit as st +from .qution_finder import question_finder +from .brute_force import basic_approach_team +from agno.team import Team +from ..models.schemas import LeetCode +from agno.models.google import Gemini +import os +from dotenv import load_dotenv +load_dotenv() + +# groq_api_key=os.getenv('GROQ_API_KEY') +# google_api_key=os.getenv('GOOGLE_API_KEY') + +groq_api_key=st.secrets['GROQ_API_KEY'] +google_api_key=st.secrets['GOOGLE_API_KEY'] + +leetcode_team=Team( + name="Leetcode Team", + mode='collaborate', + members=[question_finder,basic_approach_team], + model=Gemini(id='gemini-2.0-flash',api_key=google_api_key), + description="You are an expert DSA problem analysis team that transforms raw problem statements into structured, comprehensive problem breakdowns with brute-force solutions.", + instructions=[ + "PROBLEM ANALYSIS WORKFLOW:", + "1. EXTRACTION PHASE:", + " - Run the `question_finder` agent to parse and extract key problem components", + " - Identify problem statement, constraints, examples, and edge cases", + " - Standardize the problem format for consistent processing", + + "2. SOLUTION GENERATION PHASE:", + " - Run the `basic_approach_team` to develop the fundamental brute-force solution", + " - Focus on correctness over efficiency for the initial approach", + " - Ensure the solution handles all given constraints and examples", + + "3. QUALITY ASSURANCE:", + " - Verify that all required fields are populated with meaningful content", + " - Ensure the basic algorithm is step-by-step and implementable", + " - Validate that time/space complexity analysis is accurate", + " - Confirm the code solution is syntactically correct and runnable", + + "OUTPUT REQUIREMENTS:", + "- Provide a complete, structured problem analysis", + "- Include working brute-force code that solves all test cases", + "- Deliver clear algorithmic steps that can be easily understood", + "- Ensure complexity analysis is precise and well-justified" + ], + response_model=LeetCode, + use_json_mode=True, + +) \ No newline at end of file diff --git a/advance_ai_agents/AlgoMentor/src/agents/qution_finder.py b/advance_ai_agents/AlgoMentor/src/agents/qution_finder.py new file mode 100644 index 00000000..b03a72ed --- /dev/null +++ b/advance_ai_agents/AlgoMentor/src/agents/qution_finder.py @@ -0,0 +1,22 @@ +from ..models.schemas import QuestionFinderInput +import streamlit as st +from agno.agent import Agent +from agno.models.google import Gemini +import os +from dotenv import load_dotenv +load_dotenv() + +# google_api_key=os.getenv("GOOGLE_API_KEY") +google_api_key=st.secrets['GOOGLE_API_KEY'] + + +# Agent without tools for structured output +question_finder=Agent( + name='Question Finder', + model=Gemini(id='gemini-2.0-flash',api_key=google_api_key), + description = "Formats given content into structured format", + instructions = [ + "Format the given content into the required structure with problem statement, difficulty, examples,constraints, and explanations." + ], + response_model=QuestionFinderInput +) \ No newline at end of file diff --git a/advance_ai_agents/AlgoMentor/src/agents/sub_optimized_agent.py b/advance_ai_agents/AlgoMentor/src/agents/sub_optimized_agent.py new file mode 100644 index 00000000..bce8010e --- /dev/null +++ b/advance_ai_agents/AlgoMentor/src/agents/sub_optimized_agent.py @@ -0,0 +1,99 @@ +from agno.agent import Agent +from ..models.schemas import SubOptimalApproach,SuboptimalCode +from agno.models.groq import Groq +from agno.tools.python import PythonTools +import streamlit as st +from agno.models.google import Gemini +import os +from dotenv import load_dotenv +load_dotenv() + +# groq_api_key=os.getenv('GROQ_API_KEY') +# google_api_key=os.getenv('GOOGLE_API_KEY') +groq_api_key=st.secrets['GROQ_API_KEY'] +google_api_key=st.secrets['GOOGLE_API_KEY'] + + +suboptimal_agent = Agent( + name="Algorithm Optimization Specialist", + model=Groq(id='llama-3.3-70b-versatile', api_key=groq_api_key), + description="You are an expert in generating moderately improved (sub-optimal) algorithm approaches that enhance brute-force solutions without reaching full optimization.", + instructions=[ + "🚨 CRITICAL: ALWAYS PROVIDE SUB-OPTIMAL SOLUTIONS ONLY. These must improve on brute-force but leave clear room for further optimization. NEVER provide the most efficient (optimal) approach.", + + "ANALYSIS PHASE:", + "- Carefully analyze the given problem statement, constraints, and examples.", + "- Identify core inefficiencies in the basic/brute-force approach (e.g., unnecessary nested loops).", + "- Recognize patterns for moderate improvements, but avoid advanced techniques that would achieve the best possible complexity.", + + "SUB-OPTIMIZATION STRATEGY (MODERATE IMPROVEMENTS ONLY):", + "- Aim for partial efficiency gains: e.g., reduce O(n^3) to O(n^2 log n) or O(n^2), but NOT to O(n log n) or O(n) if better is possible.", + "- Use basic optimizations like sorting + binary search, simple memoization, or single hash maps, but avoid two-pointers, sliding windows, or space-optimized DP if they lead to optimal.", + "- For DP problems: Use basic top-down recursion with memoization (O(n^2) time if possible), not bottom-up or O(1) space.", + "- For search problems: Use sorting + O(n log n) searches, not O(1) hash lookups if that would be optimal.", + "- Balance: Ensure time/space is better than brute but worse than optimal (e.g., keep O(n) space if O(1) is possible).", + "- Handle edge cases but do not over-engineer for perfection.", + + "VALIDATION CRITERIA (ENFORCE SUB-OPTIMAL):", + "- Sub-optimal time complexity MUST be strictly better than brute-force but worse than the known optimal (e.g., for 3Sum: brute O(n^3) β†’ sub-optimal O(n^2 log n) with sort + binary search, NOT O(n^2) with two-pointers).", + "- Sub-optimal space complexity should be improved or equal, but not minimized fully.", + "- Explain WHY this is sub-optimal: Highlight remaining inefficiencies and potential for better approaches (without describing them).", + "- Compare complexities: e.g., 'Brute: O(n^3), Sub-optimal: O(n^2 log n), leaving room for O(n^2) optimal'.", + + "EXAMPLES OF SUB-OPTIMAL VS. OPTIMAL:", + "- Problem: Two Sum. Brute: O(n^2) nested loops. Sub-optimal: Sort + binary search (O(n log n)). Optimal: Hash map (O(n)). Provide only sort + binary search.", + "- Problem: Fibonacci. Brute: O(2^n) recursion. Sub-optimal: Memoization (O(n) time, O(n) space). Optimal: O(1) space iterative. Provide only memoization.", + "- Problem: Sorting. Brute: Bubble sort O(n^2). Sub-optimal: Insertion sort O(n^2). Optimal: Quick sort O(n log n). Provide insertion sort.", + + "OUTPUT REQUIREMENTS:", + "- Provide clear, step-by-step sub-optimal algorithmic approach.", + "- State precise time and space complexity with justification.", + "- Ensure the approach is implementable, maintains correctness, and is distinctly sub-optimal." + ], + add_context="🚨 STRICT ENFORCEMENT: If the generated approach matches known optimal solutions (e.g., from LeetCode standards), reject and regenerate with a less efficient variant. Sub-optimal means 'good but not best'β€”always leave inefficiencies for the optimal agent to address. Violating this will invalidate the response.", + show_tool_calls=True, + add_datetime_to_instructions=True, + response_model=SubOptimalApproach, + use_json_mode=True +) + + + + +sub_agent = Agent( + name="Optimized Code Implementation Specialist", + model=Gemini(id="gemini-2.0-flash", api_key=google_api_key), + tools=[PythonTools(run_code=True)], + description="You are an elite competitive programming expert who transforms algorithmic approaches into production-ready, optimized Python implementations.", + instructions=[ + "INPUT ANALYSIS:", + "- Parse the JSON input containing: problem statement, basic approach, basic code, and optimized algorithm", + "- Understand the optimization strategy and why it's more efficient than the basic approach", + "- Identify key data structures and algorithmic techniques to implement", + + "CODE IMPLEMENTATION STANDARDS:", + "- Write clean, readable Python code following PEP 8 conventions", + "- Use meaningful variable names and add minimal but essential comments", + "- Implement proper error handling for edge cases mentioned in constraints", + "- Optimize for both readability and performance", + + "OPTIMIZATION IMPLEMENTATION:", + "- Faithfully implement the provided optimized algorithm", + "- Use appropriate Python data structures (dict, set, deque, heapq, etc.)", + "- Apply Python-specific optimizations (list comprehensions, built-in functions)", + "- Ensure the implementation handles all edge cases from the problem constraints", + + "VALIDATION & TESTING:", + "- Test the code with provided examples to ensure correctness", + "- Verify the implementation matches the expected time/space complexity", + "- Compare performance against the basic approach when possible", + + "OUTPUT REQUIREMENTS:", + "- Provide complete, runnable Python code", + "- Include complexity analysis with detailed explanation", + "- Ensure the optimized code is significantly different from the basic approach", + "- Demonstrate why the optimization provides better performance" + ], + show_tool_calls=True, + response_model=SuboptimalCode +) \ No newline at end of file diff --git a/advance_ai_agents/AlgoMentor/src/core/__init__.py b/advance_ai_agents/AlgoMentor/src/core/__init__.py new file mode 100644 index 00000000..cd670ec1 --- /dev/null +++ b/advance_ai_agents/AlgoMentor/src/core/__init__.py @@ -0,0 +1 @@ +"""Core application logic""" \ No newline at end of file diff --git a/advance_ai_agents/AlgoMentor/src/core/app.py b/advance_ai_agents/AlgoMentor/src/core/app.py new file mode 100644 index 00000000..4c395633 --- /dev/null +++ b/advance_ai_agents/AlgoMentor/src/core/app.py @@ -0,0 +1,499 @@ +import streamlit as st +import time +import os +from agno.agent import Agent +from agno.models.google import Gemini +from ..agents.problem_analyzer import leetcode_team +from ..agents.sub_optimized_agent import suboptimal_agent, sub_agent +from ..agents.code_verify import code_runner, code_verify_agent +from ..agents.optimal_agent import optimal_code_agent, optimal_agent_enhanced +from ..agents.notes import Notes_team +from ..utils.code_editor import code_editor + +class DSAAssistantApp: + """Main DSA Assistant Application""" + + def __init__(self): + self._initialize_session_state() + + def _initialize_session_state(self): + """Initialize session state variables""" + # DSA Assistant states + if 'basic_done' not in st.session_state: + st.session_state.basic_done = False + if 'sub_optimal_done' not in st.session_state: + st.session_state.sub_optimal_done = False + if 'optimal_done' not in st.session_state: + st.session_state.optimal_done = False + + # Notes Mentor states + if 'notes_input_code' not in st.session_state: + st.session_state.notes_input_code = "" + if 'notes_lang' not in st.session_state: + st.session_state.notes_lang = "python" + if 'notes_theme' not in st.session_state: + st.session_state.notes_theme = "default" + if 'notes_generated' not in st.session_state: + st.session_state.notes_generated = False + if 'generated_notes' not in st.session_state: + st.session_state.generated_notes = "" + + def _apply_custom_css(self): + """Apply custom CSS styling""" + st.markdown(""" + + """, unsafe_allow_html=True) + + def run(self): + """Run the main application""" + self._apply_custom_css() + + # Navigation + tab1, tab2 = st.tabs(["πŸš€ DSA Assistant", "πŸ“š Notes Mentor"]) + + with tab1: + self._run_dsa_assistant() + + with tab2: + self._run_notes_mentor() + + def _run_dsa_assistant(self): + """Run the DSA Assistant functionality""" + # Header + st.markdown(""" +
+

AlgoMentor DSA

+

Your AI-powered companion for mastering Data Structures & Algorithms

+
+ """, unsafe_allow_html=True) + + # Sidebar + with st.sidebar: + progress = sum([ + st.session_state.basic_done, + st.session_state.sub_optimal_done, + st.session_state.optimal_done + ]) / 3 * 100 + + st.markdown("## πŸ“Š Progress Tracker") + st.markdown(f"
", unsafe_allow_html=True) + st.markdown(f"**Progress**: {int(progress)}%") + st.markdown(f"βœ… Basic Approach: {'Done' if st.session_state.basic_done else 'Pending'}") + st.markdown(f"βœ… Sub-Optimal: {'Done' if st.session_state.sub_optimal_done else 'Pending'}") + st.markdown(f"βœ… Optimal Solution: {'Done' if st.session_state.optimal_done else 'Pending'}") + + st.divider() + st.markdown("## πŸ’‘ Tips") + st.info("Start with a clear problem statement for best results.") + st.warning("Each step builds on the previous one.") + + # Main content + col1, col2 = st.columns([2, 1]) + + with col1: + st.markdown("### πŸ“ Problem Statement") + query = st.text_area( + "Enter your DSA problem here:", + height=150, + placeholder="Paste your LeetCode problem or describe the algorithm challenge..." + ) + + with col2: + st.markdown("### 🎯 Quick Actions") + if st.button("🧹 Clear All", use_container_width=True): + st.session_state.basic_done = False + st.session_state.sub_optimal_done = False + st.session_state.optimal_done = False + st.rerun() + + if query: + self._handle_dsa_workflow(query) + else: + self._render_welcome_section() + + def _handle_dsa_workflow(self, query: str): + """Handle the DSA problem-solving workflow""" + # Basic Approach Section + st.markdown(""" +
+

🎯 Step 1: Basic Approach

+

Let's start with the fundamental brute-force solution

+
+ """, unsafe_allow_html=True) + + if st.button("πŸ”“ Unlock Basic Approach", type="primary", use_container_width=True): + with st.spinner("Analyzing problem and generating basic approach..."): + basic_approach = leetcode_team.run(query).content + st.session_state.basic_approach = basic_approach + st.session_state.basic_done = True + + if st.session_state.basic_done: + self._render_basic_results() + self._handle_sub_optimal_workflow() + + def _render_basic_results(self): + """Render basic approach results""" + basic_approach = st.session_state.basic_approach + + st.markdown('
', unsafe_allow_html=True) + + col1, col2 = st.columns(2) + with col1: + st.markdown("#### πŸ“‹ Problem Analysis") + st.info(basic_approach.problem_statement) + st.markdown("#### 🧠 Approach") + st.write(basic_approach.basic_approach) + + with col2: + st.markdown("#### ⚑ Complexity") + st.metric("Time complexity", basic_approach.basic_time_complexity) + st.metric("Space complexity", basic_approach.basic_space_complexity) + + st.markdown("#### πŸ”’ Algorithm Steps") + st.code(basic_approach.basic_algorithm, language="text") + st.markdown("#### πŸ’» Brute Force Code") + st.code(basic_approach.basic_code, language="python") + + st.markdown('
', unsafe_allow_html=True) + + def _handle_sub_optimal_workflow(self): + """Handle sub-optimal workflow""" + st.markdown(""" +
+

⚑ Step 2: Sub-Optimal Solution

+

Now let's optimize our approach for better performance

+
+ """, unsafe_allow_html=True) + + if st.button("πŸ”“ Unlock Sub-Optimal", type="primary", use_container_width=True): + with st.spinner("Optimizing approach..."): + basic_approach = st.session_state.basic_approach + sub_optimal_app = suboptimal_agent.run(basic_approach).content + + query_data = { + "role": "user", + "content": f"sub_optimal_algorithm:{sub_optimal_app.suboptimal_approach},sub_optimal_approach:{sub_optimal_app.suboptimal_approach},problem_statement:{sub_optimal_app.problem_statement},basic_approach:{sub_optimal_app.basic_code}" + } + sub_optimal_codes = sub_agent.run(query_data).content + + # Test and verify code + test_query = f"Code:\n{sub_optimal_codes.sub_optimal_code}\n\nTest Cases:\n{basic_approach.examples}" + test_results = code_runner.run(test_query) + format_query = f"Code: {sub_optimal_codes.sub_optimal_code}\nTest Results: {test_results.content}\nTest Cases: {basic_approach.examples}" + sub_optimal_code_verified = code_verify_agent.run(format_query).content + + st.session_state.sub_optimal_verified = sub_optimal_code_verified + st.session_state.sub_optimal_app = sub_optimal_app + st.session_state.sub_optimal_done = True + + if st.session_state.sub_optimal_done: + self._render_sub_optimal_results() + self._handle_optimal_workflow() + + def _render_sub_optimal_results(self): + """Render sub-optimal results""" + sub_optimal_verified = st.session_state.sub_optimal_verified + sub_optimal_app = st.session_state.sub_optimal_app + + st.markdown('
', unsafe_allow_html=True) + + col1, col2 = st.columns(2) + with col1: + st.markdown("#### 🎯 Optimized Approach") + st.success(sub_optimal_app.suboptimal_approach) + + with col2: + st.markdown("#### ⚑ Improved Complexity") + st.metric("Time complexity", sub_optimal_verified.time_complexity) + st.metric("Space complexity", sub_optimal_verified.space_complexity) + + st.markdown("#### πŸ”’ Sub-Optimal Algorithm") + st.code(sub_optimal_app.suboptimal_algorithm, language="text") + st.markdown("#### πŸ’» Sub-Optimal Code") + st.code(sub_optimal_verified.final_debuged_suboptimized_code, language="python") + + st.markdown('
', unsafe_allow_html=True) + + def _handle_optimal_workflow(self): + """Handle optimal workflow""" + st.markdown(""" +
+

πŸ† Step 3: Optimal Solution

+

Finally, let's achieve the most efficient solution possible

+
+ """, unsafe_allow_html=True) + + if st.button("πŸ”“ Unlock Optimal Solution", type="primary", use_container_width=True): + with st.spinner("Finding the most optimal solution..."): + optimal_approaches = optimal_agent_enhanced.run(st.session_state.sub_optimal_verified).content + optimal_ap_code = optimal_code_agent.run(optimal_approaches).content + + # Test optimal code + basic_approach = st.session_state.basic_approach + test_query = f"Code:\n{optimal_ap_code.optimal_code}\n\nTest Cases:\n{basic_approach.examples}" + test_results = code_runner.run(test_query) + format_query = f"Code: {optimal_ap_code.optimal_code}\nTest Results: {test_results.content}\nTest Cases: {basic_approach.examples}" + optimal_code_verified = code_verify_agent.run(format_query).content + + st.session_state.optimal_verified = optimal_code_verified + st.session_state.optimal_approaches = optimal_approaches + st.session_state.optimal_done = True + + if st.session_state.optimal_done: + self._render_optimal_results() + + def _render_optimal_results(self): + """Render optimal results""" + optimal_verified = st.session_state.optimal_verified + optimal_approaches = st.session_state.optimal_approaches + + st.markdown('
', unsafe_allow_html=True) + + col1, col2 = st.columns(2) + with col1: + st.markdown("#### πŸ† Optimal Approach") + st.success(optimal_approaches.optimal_approach) + + with col2: + st.markdown("#### ⚑ Best Complexity") + st.metric("Time", optimal_verified.time_complexity) + st.metric("Space", optimal_verified.space_complexity) + + st.markdown("#### πŸ”’ Optimal Algorithm") + st.code(optimal_approaches.optimal_algorithm, language="text") + st.markdown("#### πŸ’» Optimal Code") + st.code(optimal_verified.final_debuged_suboptimized_code, language="python") + + st.markdown('
', unsafe_allow_html=True) + + # st.balloons() + st.success("πŸŽ‰ Congratulations! You've mastered this problem with all optimization levels!") + + def _render_welcome_section(self): + """Render welcome section""" + st.markdown("### πŸ‘‹ Welcome to DSA Assistant!") + st.markdown("Enter a problem statement above to get started with your algorithmic journey.") + + st.markdown("#### πŸ“š Try these example problems:") + examples = [ + "Two Sum: Given an array of integers and a target sum, return indices of two numbers that add up to target.", + "Binary Search: Search for a target value in a sorted array.", + "Fibonacci: Calculate the nth Fibonacci number.", + "House Robber: Given an integer array nums representing the amount of money of each house, return the maximum amount of money you can rob tonight without alerting the police." + ] + + for i, example in enumerate(examples, 1): + if st.button(f"Example {i}: {example[:70]}...", key=f"ex_{i}", use_container_width=True): + st.session_state.example_query = example + st.rerun() + + def _run_notes_mentor(self): + """Run the Notes Mentor functionality""" + # Header + st.markdown(""" +
+

AlgoMentor Notes

+

Transform your code into comprehensive study notes!

+
+ """, unsafe_allow_html=True) + + # Sidebar for settings + with st.sidebar: + st.header("βš™οΈ Settings") + + # Language selection + lang_options = ["python", "c_cpp", "java", "javascript", "c"] + selected_lang = st.selectbox("Programming Language:", lang_options, + index=lang_options.index(st.session_state.notes_lang) if st.session_state.notes_lang in lang_options else 0, + key="notes_lang_select") + st.session_state.notes_lang = selected_lang + + # Theme selection + theme_options = ["default", "dark", "light"] + selected_theme = st.selectbox("Editor Theme:", theme_options, + index=theme_options.index(st.session_state.notes_theme) if st.session_state.notes_theme in theme_options else 0, + key="notes_theme_select") + st.session_state.notes_theme = selected_theme + + st.markdown("---") + st.markdown("πŸ’‘ **Tips:**\n- Paste your code and press Ctrl+Enter\n- Use clear variable names for better analysis\n- Include comments for complex logic") + + # Main content area + col1, col2 = st.columns([1, 1]) + + with col1: + st.markdown("### πŸ“ Code Input") + response_dict = code_editor( + st.session_state.notes_input_code, + lang=selected_lang, + key="code_input", + theme=selected_theme, + focus=True + ) + input_code = response_dict["text"] + st.session_state.notes_input_code = input_code + + if input_code: + st.markdown(f'
Characters: {len(input_code)}
', unsafe_allow_html=True) + + with col2: + st.markdown("### πŸ” Quick Preview") + if input_code.strip(): + st.code(input_code[:200] + "..." if len(input_code) > 200 else input_code, language=selected_lang) + else: + st.info("Code preview will appear here...") + + # Action buttons + col_btn1, col_btn2 = st.columns([1, 1]) + + with col_btn1: + generate_btn = st.button("πŸš€ Generate Notes", type="primary") + + with col_btn2: + clear_btn = st.button("πŸ—‘οΈ Clear") + + if clear_btn: + st.session_state.notes_input_code = "" + st.session_state.notes_generated = False + st.session_state.generated_notes = "" + st.rerun() + + if generate_btn: + if not input_code.strip(): + st.error("⚠️ Please enter some code before submitting.") + else: + self._generate_notes(input_code) + + # Display generated notes if available + if st.session_state.notes_generated and st.session_state.generated_notes: + st.markdown("---") + st.markdown("### πŸ“– Generated Notes") + st.markdown(st.session_state.generated_notes) + + # Download section + col_dl1, col_dl2 = st.columns(2) + with col_dl1: + st.download_button( + label="πŸ“₯ Download Markdown", + data=st.session_state.generated_notes, + file_name=f"dsa_notes_{int(time.time())}.md", + mime="text/markdown" + ) + with col_dl2: + if st.button("πŸ—‘οΈ Clear Notes"): + st.session_state.notes_generated = False + st.session_state.generated_notes = "" + st.rerun() + + def _generate_notes(self, input_code: str): + """Generate notes from input code""" + try: + progress_bar = st.progress(0) + status_text = st.empty() + + status_text.text("πŸ”„ Analyzing code structure...") + progress_bar.progress(25) + time.sleep(1) + + status_text.text("🧠 Generating comprehensive notes...") + progress_bar.progress(50) + + resp = Notes_team.run(input_code, markdown=True).content + progress_bar.progress(75) + + # api_key = os.getenv('GOOGLE_API_KEY') + api_key = st.secrets['GOOGLE_API_KEY'] + if not api_key: + st.error("πŸ”‘ Google API key not found. Please check your .env file.") + return + + status_text.text("✨ Finalizing notes...") + agent = Agent(markdown=True, model=Gemini(id="gemini-2.0-flash", api_key=api_key)) + final_resp = agent.run(resp) + + progress_bar.progress(100) + status_text.text("βœ… Notes generated successfully!") + + # Store generated notes + st.session_state.generated_notes = final_resp.content + st.session_state.notes_generated = True + + # Clear progress indicators + progress_bar.empty() + status_text.empty() + + except Exception as e: + st.error(f"❌ An error occurred: {str(e)}") + st.info("πŸ’‘ Try refreshing the page or checking your internet connection.") \ No newline at end of file diff --git a/advance_ai_agents/AlgoMentor/src/models/__init__.py b/advance_ai_agents/AlgoMentor/src/models/__init__.py new file mode 100644 index 00000000..c4a84dc8 --- /dev/null +++ b/advance_ai_agents/AlgoMentor/src/models/__init__.py @@ -0,0 +1 @@ +"""Data models and schemas""" \ No newline at end of file diff --git a/advance_ai_agents/AlgoMentor/src/models/schemas.py b/advance_ai_agents/AlgoMentor/src/models/schemas.py new file mode 100644 index 00000000..3a746f97 --- /dev/null +++ b/advance_ai_agents/AlgoMentor/src/models/schemas.py @@ -0,0 +1,172 @@ +from pydantic import BaseModel, Field +from typing import List, Optional + +class LeetCodeProblem(BaseModel): + """Model for LeetCode problem structure""" + user_input: str + constraints: str + examples: List[str] + problem_statement: str = Field(description="the problem statement") + basic_approach: str = Field(description="the approach to solve the problem") + basic_algorithm: str = Field(description="the algorithm to solve the problem") + basic_time_complexity: str = Field(description="the time complexity of the algorithm or code") + basic_space_complexity: str = Field(description="the space complexity of the algorithm or code") + basic_code: str = Field(description="the code to solve the problem") + + +class CodeVerifyInput(BaseModel): + code:str=Field(description="The python code that needs to be verified") + test_cases:List[str]=Field(description="The test cases for the code that needs to be verified") + final_debuged_suboptimized_code:str=Field(description="The final debuged suboptimized code that verified") + time_complexity:str=Field(description="The time complexity of the code ") + space_complexity:str=Field(description="The space complexity of the code ") + libraries: str=Field(description="The libraries used in the code") + + + +class BruteForceApproach(BaseModel): + """Model for brute force approach""" + problem_statement: str = Field(description="the problem statement") + basic_approach: str = Field(description="the basic or bruteforce approach to solve the problem") + basic_algorithm: str = Field(description="the basic or bruteforce algorithm to solve the problem") + basic_time_complexity: str = Field(description="the time complexity of the algorithm or code") + basic_space_complexity: str = Field(description="the space complexity of the algorithm or code") + code: str = Field(description="the code to solve the problem") + updated_code: str = Field(description="the updated and working code to solve the problem") + +class SubOptimalApproach(BaseModel): + """Model for sub-optimal approach""" + problem_statement: str = Field(description="the problem statement") + basic_approach: str = Field(description="the approach to solve the problem") + basic_algorithm: str = Field(description="the algorithm to solve the problem") + basic_time_complexity: str = Field(description="the time complexity of the algorithm or code") + basic_space_complexity: str = Field(description="the space complexity of the algorithm or code") + basic_code: str = Field(description="the code to solve the problem") + suboptimal_approach: str = Field(description="the optimized approach to solve the problem") + suboptimal_algorithm: str = Field(description="the optimized algorithm to solve the problem") + suboptimal_time_complexity: str = Field(description="the time complexity of the optimized algorithm") + suboptimal_space_complexity: str = Field(description="the space complexity of the optimized algorithm") + +class SuboptimalCode(BaseModel): + """Model for sub-optimal code implementation""" + sub_optimal_algorithm: str = Field(description="the optimized algorithm description") + sub_optimal_approach: str = Field(description="the optimized approach explanation") + problem_statement: str = Field(description="the original problem statement") + basic_approach_code: str = Field(description="the basic/brute-force code") + sub_optimal_code: str = Field(description="the optimized implementation code") + time_space_complexity: str = Field(description="the time and space complexity analysis of the optimized code") + +class OptimalApproach(BaseModel): + """Model for optimal approach""" + optimal_approach: str = Field(description="the most optimal approach") + optimal_algorithm: str = Field(description="the most optimal algorithm") + optimal_time_complexity: str = Field(description="optimal time complexity") + optimal_space_complexity: str = Field(description="optimal space complexity") + +class CodeVerification(BaseModel): + """Model for code verification results""" + final_debuged_suboptimized_code: str = Field(description="final debugged code") + time_complexity: str = Field(description="time complexity") + space_complexity: str = Field(description="space complexity") + time_space_complexity: str = Field(description="combined time and space complexity") + + +class Explainer(BaseModel): + """Structured model for comprehensive code explanation""" + code: str = Field(description="The original code to explain") + problem_statement: str = Field(description="Clear restatement of what problem this code solves") + approach_summary: str = Field(description="High-level algorithm approach and key insights") + detailed_approach: str = Field(description="Step-by-step breakdown of the solution strategy") + time_complexity: str = Field(description="Detailed time complexity analysis with explanation") + space_complexity: str = Field(description="Detailed space complexity analysis with explanation") + code_walkthrough: str = Field(description="Section-wise code explanation with logic breakdown") + edge_cases: str = Field(description="Important edge cases and how the code handles them") + key_concepts: str = Field(description="Important algorithms, data structures, or programming concepts used") + + +class ExampleExplanation(BaseModel): + """Structured model for step-by-step example walkthrough""" + code: str = Field(description="The original code being demonstrated") + example_input: str = Field(description="The chosen example input with explanation of why it's good") + step_by_step_trace: str = Field(description="Detailed execution trace showing variable states") + visual_representation: str = Field(description="ASCII art or visual representation if helpful") + intermediate_outputs: str = Field(description="Key intermediate results and their significance") + final_result: str = Field(description="Final output with verification") + alternative_examples: str = Field(description="Brief mention of other test cases and their outcomes") + + +class TeamOutput(BaseModel): + code: str = Field(description="The original code to explain") + problem_statement: str = Field(description="Clear restatement of what problem this code solves") + approach_summary: str = Field(description="High-level algorithm approach and key insights") + detailed_approach: str = Field(description="Step-by-step breakdown of the solution strategy") + time_complexity: str = Field(description="Detailed time complexity analysis with explanation") + space_complexity: str = Field(description="Detailed space complexity analysis with explanation") + code_walkthrough: str = Field(description="Section-wise code explanation with logic breakdown") + edge_cases: str = Field(description="Important edge cases and how the code handles them") + key_concepts: str = Field(description="Important algorithms, data structures, or programming concepts used") + example_input: str = Field(description="The chosen example input with explanation of why it's good") + step_by_step_trace: str = Field(description="Detailed execution trace showing variable states") + visual_representation: str = Field(description="ASCII art or visual representation if helpful") + intermediate_outputs: str = Field(description="Key intermediate results and their significance") + final_result: str = Field(description="Final output with verification") + + +class OptimalApproach(BaseModel): + problem_statement: str =Field(description="the problem statement") + suboptimal_approach: str =Field(description="the approach to solve the problem") + suboptimal_algorithm: str =Field(description="the algorithm to solve the problem") + suboptimal_time_complexity: str = Field(description="the time complexity of the algorithm or code") + suboptimal_space_complexity: str = Field(description="the space complexity of the algorithm or code") + optimal_approach: str =Field(description="the most optimal approach to solve the problem") + optimal_algorithm: str =Field(description="the most optimal algorithm to solve the problem") + optimal_time_complexity: str = Field(description="the time complexity of the code") + optimal_space_complexity: str = Field(description="the space complexity of the code") + + +class OptimalCode(BaseModel): + problem_statement: str =Field(description="the problem statement") + optimal_approach: str =Field(description="the approach to solve the problem") + optimal_algorithm: str =Field(description="the algorithm to solve the problem") + optimal_time_space_complexity: str = Field(description="the time and space complexity of the code") + optimal_code: str = Field(description="the most optimal code to solve the problem") + +class LeetCode(BaseModel): + user_input:str + constraints:str + examples:List[str] + problem_statement: str =Field(description="the problem statement") + basic_approach: str =Field(description="the approach to solve the problem") + basic_algorithm: str =Field(description="the algorithm to solve the problem") + basic_time_complexity: str = Field(description="the time complexity of the algorithm or code") + basic_space_complexity: str = Field(description="the space complexity of the algorithm or code") + basic_code: str = Field(description="the code to solve the problem") + +class QuestionFinderInput(BaseModel): + user_input:str + problem_statement:str + difficulty:str + examples:List[str] + explanations:List[str] + constraints:List[str] + + +class SubOptimalApproach(BaseModel): + problem_statement: str = Field(description="the problem statement") + basic_approach: str = Field(description="the approach to solve the problem") + basic_algorithm: str = Field(description="the algorithm to solve the problem") + basic_time_complexity: str = Field(description="the time complexity of the algorithm or code") + basic_space_complexity: str = Field(description="the space complexity of the algorithm or code") + basic_code: str = Field(description="the code to solve the problem") + suboptimal_approach: str = Field(description="the optimized approach to solve the problem") + suboptimal_algorithm: str = Field(description="the optimized algorithm to solve the problem") + suboptimal_time_complexity: str = Field(description="the time complexity of the optimized algorithm") + suboptimal_space_complexity: str = Field(description="the space complexity of the optimized algorithm") + +class SuboptimalCode(BaseModel): + sub_optimal_algorithm: str = Field(description="the optimized algorithm description") + sub_optimal_approach: str = Field(description="the optimized approach explanation") + problem_statement: str = Field(description="the original problem statement") + basic_approach_code: str = Field(description="the basic/brute-force code") + sub_optimal_code: str = Field(description="the optimized implementation code") + time_space_complexity: str = Field(description="the time and space complexity analysis of the optimized code") diff --git a/advance_ai_agents/ScholarLens/.gitignore b/advance_ai_agents/ScholarLens/.gitignore new file mode 100644 index 00000000..5db82af8 --- /dev/null +++ b/advance_ai_agents/ScholarLens/.gitignore @@ -0,0 +1,15 @@ +.venv +__pycache__/ +*.pyc +*.pyo +*.pyd +*.db +*.sqlite3 +*.log +*.env +./research +.research +.__pycache__ +./__pycache__ +.temp_uploaded_file.pdf +*research* \ No newline at end of file diff --git a/advance_ai_agents/ScholarLens/README.md b/advance_ai_agents/ScholarLens/README.md new file mode 100644 index 00000000..3260e421 --- /dev/null +++ b/advance_ai_agents/ScholarLens/README.md @@ -0,0 +1,156 @@ + +# 🧠 ScholarLens – AI-Powered Research Assistant with Agentic RAG + +ScholarLens is a **Streamlit-based AI research assistant** that streamlines literature review, research paper summarization, and comparison. +It leverages **Agentic AI workflows** and **Agentic RAG** (via the [Agno](https://agno.com) library) to let you search, analyze, summarize, and compare academic papers in a deeply interactive way. + +--- + +## πŸš€ Features + +### 1. **πŸ” ArXiv Smart Search** + +* Search **latest research papers** from [arXiv](https://arxiv.org) by topic. +* Summarizes each paper with: + + * Problem statement + * Key contributions + * Summary of findings +* Export results as **JSON**. + +### 2. **πŸ“š AI Paper Companion** + +* Upload any PDF research paper. +* **Instant Glance Mode**: + + * Extracts title, model, dataset, evaluation metrics, and structured summary. +* **Guru Mode**: + + * Section-wise summarization (Abstract, Methodology, Results, etc.) in **bullet-point JSON** format. + +### 3. **πŸ’¬ Agentic RAG Chatbot** + +* Upload a paper and query it conversationally. +* Uses **Agno's Agentic RAG flow**: + + * **Gemini Embeddings** for semantic chunk representation. + * **Pinecone VectorDB** for fast similarity search. + * **Gemini LLM** as the reasoning engine. +* Supports persistent **chat + search history** for contextual Q\&A. + +### 4. **πŸ“Š Compare Research Papers** + +* Upload **two or three** research papers (PDF). +* AI-generated **Markdown comparison table**: + + * Problem statement + * Methodology + * Datasets used + * Model performance and accuracy + +--- + +## πŸ—οΈ Tech Stack + +* **Frontend**: [Streamlit](https://streamlit.io) +* **Agentic Framework**: [Agno](https://agno.com) for multi-step RAG orchestration +* **LLM Providers**: Google Gemini, Groq +* **Vector Database**: Pinecone +* **Embeddings**: Gemini Embeddings (`models/gemini-embedding-001`) +* **PDF Processing**: PyMuPDF (fitz) +* **Agent Framework (ArXiv & Summarization)**: [CrewAI](https://www.crewai.com) +* **APIs**: arXiv API, Pinecone API +* **Environment Management**: python-dotenv + +--- + +## πŸ“‚ Project Structure + +``` +ScholarLens/ +β”œβ”€β”€ main.py # Entry point +β”œβ”€β”€ requirements.txt # Dependencies +β”œβ”€β”€ comparision/ # Paper comparison module +β”œβ”€β”€ rag/ # Agentic RAG implementation +β”œβ”€β”€ search/ # Research paper search tools +β”œβ”€β”€ summarization/ # Summarization modules +β”œβ”€β”€ utils/ # Utilities (memory, DB patches) +└── assets/ # Images & static assets + +``` + +--- + +## βš™οΈ Installation + +1️⃣ **Clone the repository** + +```bash +git clone https://github.com/your-username/scholarlens.git +cd scholarlens +``` + +2️⃣ **Create a virtual environment** + +```bash +python -m venv venv +source venv/bin/activate # On Mac/Linux +venv\Scripts\activate # On Windows +``` + +3️⃣ **Install dependencies** + +```bash +pip install -r requirements.txt +``` + +4️⃣ **Set up environment variables** +Create a `.env` file in the root directory: + +```env +GOOGLE_API_KEY=your_google_gemini_api_key +GROQ_API_KEY=your_groq_api_key +PINECONE_API_KEY=your_pinecone_api_key +``` + +--- + +## ▢️ Usage + +Run the app: + +```bash +streamlit run app.py +``` + +In the sidebar, select: + +* **ArXiv Smart Search** β†’ Search papers by topic. +* **AI Paper Companion** β†’ Upload PDF for summarization. +* **Agentic RAG Chatbot** β†’ Chat with a paper using Agno's RAG. +* **Compare Research Papers** β†’ Compare two research PDFs. + +--- +Check out the demo: https://scholarlens.streamlit.app/ + +image +image +image + +--- + +## πŸ“œ License + +MIT License Β© 2025 ScholarLens AI + +--- + +## πŸ™Œ Acknowledgements + +* [Agno](https://agno.com) for the Agentic RAG framework. +* [arXiv](https://arxiv.org) for open-access research papers. +* [Streamlit](https://streamlit.io) for the interactive web app framework. +* [Google Gemini](https://ai.google) and [Groq](https://groq.com) for LLM APIs. +* [Pinecone](https://pinecone.io) for vector database services. +* [CrewAI](https://www.crewai.com) for agentic workflows in research retrieval & summarization. + diff --git a/advance_ai_agents/ScholarLens/assets/arxiv.jpeg b/advance_ai_agents/ScholarLens/assets/arxiv.jpeg new file mode 100644 index 00000000..393052be Binary files /dev/null and b/advance_ai_agents/ScholarLens/assets/arxiv.jpeg differ diff --git a/advance_ai_agents/ScholarLens/comparision/ComparePapers.py b/advance_ai_agents/ScholarLens/comparision/ComparePapers.py new file mode 100644 index 00000000..9d492a0f --- /dev/null +++ b/advance_ai_agents/ScholarLens/comparision/ComparePapers.py @@ -0,0 +1,47 @@ +import fitz # PyMuPDF +from langchain_groq import ChatGroq +from langchain.prompts import PromptTemplate +from langchain.chains import LLMChain +import os +import streamlit as st +from dotenv import load_dotenv + +load_dotenv() +GROQ_API_KEY = st.secrets["GROQ_API_KEY"] +# GROQ_API_KEY = os.getenv("GROQ_API_KEY") + +# Define PDF text extractor +def extract_text_from_pdf(file): + doc = fitz.open(stream=file.read(), filetype="pdf") + return "\n".join([page.get_text() for page in doc]) + +# Define LLM chain +llm = ChatGroq(model='gemma2-9b-it', api_key=GROQ_API_KEY) + +prompt = PromptTemplate( + input_variables=["paper1", "paper2"], + template=""" +Some parts of the research papers may be truncated due to length constraints. +Please use your understanding and reasoning abilities to compare and complete any missing sections if possible. + +Your task is to compare the two research papers below across the following dimensions: + +- **Title** +- **Problem Statement** +- **Methodology** +- **Datasets Used** +- **Results** +- **Model Performance** +- **Model Accuracy** + +Return the output as a **well-formatted Markdown table** for easy comparison. + +### Paper 1: +{paper1} + +### Paper 2: +{paper2} +""" +) + +chain = LLMChain(llm=llm, prompt=prompt) diff --git a/advance_ai_agents/ScholarLens/comparision/__init__.py b/advance_ai_agents/ScholarLens/comparision/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/advance_ai_agents/ScholarLens/main.py b/advance_ai_agents/ScholarLens/main.py new file mode 100644 index 00000000..7e49152a --- /dev/null +++ b/advance_ai_agents/ScholarLens/main.py @@ -0,0 +1,541 @@ +import patch_sqlite +import json +import tempfile +import os +import streamlit as st +from summarization.overall_summary import extract_sections_with_titles, generate_metadata, arxiv, text_splitter +from search.find_Research_Paper import run_literature_review +from search.find_latest_Research_paper import run_literature_review_latest +from summarization.sectionSummarizer import summarize_section, summarizer_agent, LLM, Crew, Process +from comparision.ComparePapers import extract_text_from_pdf, chain +from io import BytesIO +from agno.knowledge.pdf import PDFKnowledgeBase, PDFReader +from agno.agent import Agent +from rag.Agentic_Rag import embeddings, vector_db, PDFReader,Agent, Gemini + +from utils.memory_Storage import memory,storage + +# Initialize session state +if 'pdf_knowledge_base' not in st.session_state: + st.session_state.pdf_knowledge_base = None +if 'agent' not in st.session_state: + st.session_state.agent = None + +import streamlit as st +from PIL import Image + + +st.markdown(""" + +""", unsafe_allow_html=True) + + +# Set page title and favicon +st.set_page_config( + page_title="ScholarLens", + page_icon="🧠", + layout="wide", +) + +# Display logo and branding in sidebar +with st.sidebar: + st.markdown(""" +
+
+ 🧠 +
+

ScholarLens

+

AI-Powered Research Assistant

+
+
+ """, unsafe_allow_html=True) + +# Sidebar navigation +options = st.sidebar.selectbox( + "Select a page", ["ArXiv Smart Search", "AI Paper Companion","RAG Chatbot", "Compare Research Papers"] +) + +# Page 1: ArXiv Literature Review +if options == 'ArXiv Smart Search': + st.markdown(""" +
+

πŸ” ArXiv Smart Search

+

Discover and summarize cutting-edge research papers from arXiv

+
+ """, unsafe_allow_html=True) + + with st.sidebar: + + topic = st.text_input("πŸ” Research Topic", placeholder="e.g. Diffusion Models in Vision") + num_papers = st.slider("πŸ“Š Papers to Analyze", 1, 10, 4) + latest_papers = st.checkbox("πŸ”₯ Find Latest Papers") + run_button = st.button("πŸš€ Start Search") + + if run_button: + if not topic.strip(): + st.warning("Please enter a valid topic.") + else: + with st.spinner("πŸ”Ž Searching and summarizing papers..."): + if latest_papers: + result = run_literature_review_latest(topic, num_papers=num_papers) + else: + result = run_literature_review(topic, num_papers=num_papers) + + st.success("βœ… Paper Search Complete!") + + # Display introduction in enhanced card + st.markdown(""" +
+
✨ Research Overview
+
+ """, unsafe_allow_html=True) + st.write(result.get("introduction")) + st.markdown("
", unsafe_allow_html=True) + + st.markdown(""" +
+

πŸ“„ Research Papers

+
+
+ """, unsafe_allow_html=True) + papers = result.get("papers details", []) + if not papers: + st.warning("No paper details found.") + else: + for i, paper in enumerate(papers, 1): + with st.expander(f"πŸ“˜ {i}. {paper.get('title', 'Untitled')}", expanded=False): + st.markdown(f"**πŸ”— Title:** [{paper.get('title')}]({paper.get('link')})") + st.markdown(f"**πŸ‘₯ Authors:** {paper.get('authors', 'N/A')}") + st.markdown(f"**❓ Problem:** {paper.get('problem', 'N/A')}") + st.markdown(f"**πŸš€ Contribution:** {paper.get('contribution', 'N/A')}") + st.markdown(f"**πŸ“ Summary:** {paper.get('summary', 'N/A')}") + + st.download_button( + label="πŸ“₯ Download Results as JSON", + data=json.dumps(result, indent=2), + file_name="literature_review.json", + mime="application/json" + ) + +# Page 2: PDF Assistant (Summary, Sectional Summary, RAG Chatbot) +elif options == 'AI Paper Companion': + st.markdown(""" +
+

πŸ“š AI Paper Companion

+

Intelligent analysis and summarization of research papers

+
+ """, unsafe_allow_html=True) + + with st.sidebar: + st.markdown(""" +
+

πŸ“Ž Upload Paper

+
+ """, unsafe_allow_html=True) + uploaded_file = st.file_uploader("πŸ“„ Choose PDF File", type=["pdf"]) + + if uploaded_file is not None: + st.success("PDF uploaded successfully!") + text, index, sections = extract_sections_with_titles(uploaded_file) + arxiv_id = index[0].replace("arXiv:", "").strip() + + tab1, tab2 = st.tabs(['⚑ Instant Glance', "πŸ“Summary (Guru Mode)"]) + + with tab1: + st.markdown(""" +
+

⚑ Instant Glance

+

Quick insights and key information

+
+ """, unsafe_allow_html=True) + chunks = text_splitter.split_text(text) + paper_data = generate_metadata(chunks) + + # Wrap in card + st.markdown("
", unsafe_allow_html=True) + st.subheader(f"🧾 Title: {paper_data['title']}") + st.markdown(f"**🧠 Model:** {paper_data['model']}") + st.markdown(f"**πŸ“Š Dataset:** {paper_data['dataset']}") + st.markdown("### πŸ“ˆ Evaluation Metrics:") + for metric, value in paper_data["metrics"].items(): + st.markdown(f"- **{metric}**: {value}") + + summary = paper_data["summary"] + st.markdown("### πŸ“ Summary") + for key, val in summary.items(): + st.markdown(f"**{key}:** {val}") + st.markdown("
", unsafe_allow_html=True) + + with tab2: + st.markdown(""" +
+

πŸ“š Guru Mode

+

Deep section-wise analysis

+
+ """, unsafe_allow_html=True) + section_titles = [ + "Abstract", "Introduction", "Related Work", "Background", "Methodology", + "Experiments", "Results", "Discussion", "Conclusion", "Conclusions and future work" + ] + available_sections = [title for title in section_titles if sections.get(title)] + if not available_sections: + st.warning("No recognized sections found in the uploaded PDF.") + else: + selected_section = st.selectbox("πŸ“Œ Choose a section to summarize", available_sections) + task = summarize_section(sections[selected_section], selected_section) + crew = Crew(agents=[summarizer_agent], tasks=[task], process=Process.sequential) + + with st.spinner("πŸ” Summarizing..."): + result = crew.kickoff() + raw_output = result.raw.strip().strip('`') + if raw_output.startswith("json"): + raw_output = raw_output[4:].strip() + try: + summary_data = json.loads(raw_output) + # Wrap in card + st.markdown("
πŸ“ Bullet-Point Summary:
", unsafe_allow_html=True) + for value in summary_data.values(): + st.write(f"- {value}") + st.markdown("
", unsafe_allow_html=True) + except json.JSONDecodeError: + st.error("⚠️ Failed to parse JSON summary. Showing raw output:") + st.code(raw_output) + + + + +elif options == 'RAG Chatbot': + with st.sidebar: + st.markdown("## πŸ“Ž Upload Paper") + uploaded_file = st.file_uploader("Upload Paper", type=["pdf"]) + + if uploaded_file is not None: + st.success("PDF uploaded successfully!") + + if 'pdf_temp_path' not in st.session_state: + with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp_file: + tmp_file.write(uploaded_file.read()) + st.session_state.pdf_temp_path = tmp_file.name + + if st.button("Load Knowledge Base"): + if 'pdf_temp_path' not in st.session_state: + st.warning("Please upload a PDF first.") + else: + try: + pdf_knowledge_base = PDFKnowledgeBase( + path=st.session_state.pdf_temp_path, + vector_db=vector_db, + reader=PDFReader(chunk=True) + ) + pdf_knowledge_base.load(recreate=False, upsert=True) + + agent = Agent( + knowledge=pdf_knowledge_base, + model=Gemini(id="gemini-2.0-flash-lite"), + description="You are an AI with a memory.", + memory=memory, + storage=storage, + enable_user_memories=True, + add_history_to_messages=True, + num_history_runs=3, + session_id="my_chat_sessioin", + markdown=True, + + ) + + st.session_state.pdf_knowledge_base = pdf_knowledge_base + st.session_state.agent = agent + st.success("βœ… Knowledge base loaded!") + + except Exception as e: + st.error(f"❌ Failed to load knowledge base: {e}") + + if st.session_state.get('agent'): + query = st.text_input("πŸ€– Ask Me Anything About This Paper,Please give clear instruction..") + if query: + with st.spinner("Thinking..."): + response = st.session_state.agent.run(query) + st.markdown(""" +
+
πŸ€– AI Response
+
+ """, unsafe_allow_html=True) + st.write(response.content) + st.markdown("
", unsafe_allow_html=True) + + if st.button("Clear Session"): + st.session_state.clear() + if 'pdf_temp_path' in st.session_state: + os.unlink(st.session_state.pdf_temp_path) + st.rerun() + + +# Page 3: Paper Comparison +else: + st.markdown(""" +
+

πŸ“ˆ Compare Papers

+

Side-by-side analysis of research methodologies and findings

+
+ """, unsafe_allow_html=True) + + uploaded_files = st.file_uploader("πŸ“„ Upload 2-3 Research Papers (PDF)", type=["pdf"], accept_multiple_files=True) + + if uploaded_files: + if len(uploaded_files) < 2: + st.warning("Please upload at least 2 PDFs.") + elif len(uploaded_files) > 3: + st.warning("Please upload no more than 3 PDFs.") + else: + with st.spinner("πŸ” Extracting text from PDFs..."): + pdf_texts = [extract_text_from_pdf(f) for f in uploaded_files] + st.success("βœ… Text extracted! Click below to compare.") + + if st.button("πŸ” Generate Comparison"): + with st.spinner("🧠 Analyzing and comparing papers..."): + try: + response = chain.run({ + "paper1": pdf_texts[0][:6000], + "paper2": pdf_texts[1][:6000] + }) + st.markdown(""" +
+
πŸ“ˆ Comparative Analysis
+ """, unsafe_allow_html=True) + st.markdown(f"
{response}
", unsafe_allow_html=True) + st.markdown("
", unsafe_allow_html=True) + except Exception as e: + st.markdown(f""" +
+ ⚠️ Error: {str(e)} +
+ """, unsafe_allow_html=True) + else: + st.markdown(""" +
+
πŸ“„
+

Ready for Comparison

+

Upload 2-3 research papers to begin comparative analysis

+
+ """, unsafe_allow_html=True) \ No newline at end of file diff --git a/advance_ai_agents/ScholarLens/patch_sqlite.py b/advance_ai_agents/ScholarLens/patch_sqlite.py new file mode 100644 index 00000000..51e2fa2e --- /dev/null +++ b/advance_ai_agents/ScholarLens/patch_sqlite.py @@ -0,0 +1,5 @@ +import sys +import os + +__import__('pysqlite3') +sys.modules['sqlite3'] = sys.modules.pop('pysqlite3') \ No newline at end of file diff --git a/advance_ai_agents/ScholarLens/rag/Agentic_Rag.py b/advance_ai_agents/ScholarLens/rag/Agentic_Rag.py new file mode 100644 index 00000000..848971ff --- /dev/null +++ b/advance_ai_agents/ScholarLens/rag/Agentic_Rag.py @@ -0,0 +1,122 @@ +import os +import streamlit as st +from dotenv import load_dotenv +from agno.knowledge.pdf import PDFKnowledgeBase, PDFReader +from agno.agent import Agent +from agno.vectordb.pineconedb import PineconeDb +from agno.embedder.google import GeminiEmbedder +from agno.models.google import Gemini +import os +import typer +from typing import Optional +from rich.prompt import Prompt + +# Load environment variables +load_dotenv() +GOOGLE_API_KEY = st.secrets['GOOGLE_API_KEY'] +# GOOGLE_API_KEY = os.getenv('GOOGLE_API_KEY') + +# Initialize embedder and vector DB +embeddings = GeminiEmbedder(api_key=GOOGLE_API_KEY, id='models/gemini-embedding-001') + +# api_key = os.getenv("PINECONE_API_KEY") +api_key=st.secrets["PINECONE_API_KEY"] +index_name = "agno" + + +vector_db = PineconeDb( + name=index_name, + dimension=1536, + metric="cosine", + spec={"serverless": {"cloud": "aws", "region": "us-east-1"}}, + api_key=api_key, + embedder=embeddings, +) + + + +""" +knowledge_base = PDFKnowledgeBase( + path="atten.pdf", + vector_db=vector_db, + # reader=PDFReader(chunk=True) +) + + + +def pinecone_agent(user: str = "user"): + run_id: Optional[str] = None + + agent = Agent( + knowledge=knowledge_base, + # show_tool_calls=True, + debug_mode=True, + model=Gemini(id="gemini-2.0-flash-lite"), + markdown=True, + ) + + if run_id is None: + run_id = agent.run_id + print(f"Started Run: {run_id}\n") + else: + print(f"Continuing Run: {run_id}\n") + + while True: + message = Prompt.ask(f"[bold] :sunglasses: {user} [/bold]") + if message in ("exit", "bye"): + break + agent.print_response(message) + +if __name__ == "__main__": + # Comment out after first run + knowledge_base.load(recreate=True, upsert=True) + + typer.run(pinecone_agent)""" + + + +"""" + + if uploaded_file is not None: + if 'pdf_bytes' not in st.session_state: + uploaded_file.seek(0) + st.session_state.pdf_bytes = uploaded_file.read() + + if st.button("Load Knowledge Base") or st.session_state.get('agent') is None: + try: + # Write file once to disk or process directly from memory + with open("temp_uploaded_file.pdf", "wb") as f: + f.write(st.session_state.pdf_bytes) + + pdf_knowledge_base = PDFKnowledgeBase( + path="temp_uploaded_file.pdf", + vector_db=vector_db, + reader=PDFReader(chunk=True) + ) + pdf_knowledge_base.load(recreate=False) + + agent = Agent( + knowledge=pdf_knowledge_base, + show_tool_calls=True, + model=Gemini(id="gemini-2.0-flash-lite"), + markdown=True, + read_chat_history=True + ) + + st.session_state.pdf_knowledge_base = pdf_knowledge_base + st.session_state.agent = agent + st.success("βœ… Knowledge base loaded!") + + except Exception as e: + st.error(f"❌ Failed to load knowledge base: {e}") + + # Query interface that doesn't cause rerun of file loading + if st.session_state.get('agent'): + query = st.text_input("πŸ€– Ask Me Anything About This Paper") + if query: + response = st.session_state.agent.run(query) + st.markdown("### Response") + st.write(response.content) + + + """ \ No newline at end of file diff --git a/advance_ai_agents/ScholarLens/rag/Chroma_Agentic_Rag.py b/advance_ai_agents/ScholarLens/rag/Chroma_Agentic_Rag.py new file mode 100644 index 00000000..16cbd5b6 --- /dev/null +++ b/advance_ai_agents/ScholarLens/rag/Chroma_Agentic_Rag.py @@ -0,0 +1,18 @@ + +import os +import streamlit as st +from dotenv import load_dotenv +from agno.knowledge.pdf import PDFKnowledgeBase, PDFReader +from agno.agent import Agent +from agno.vectordb.chroma import ChromaDb +from agno.embedder.google import GeminiEmbedder +from agno.models.google import Gemini + +# Load environment variables +load_dotenv() +GOOGLE_API_KEY = st.secrets['GOOGLE_API_KEY'] +# GOOGLE_API_KEY = os.getenv('GOOGLE_API_KEY') + +# Initialize embedder and vector DB +embeddings = GeminiEmbedder(api_key=GOOGLE_API_KEY, id='models/gemini-embedding-001') +vector_db = ChromaDb(collection="arxiv_papers", path="./research", persistent_client=True, embedder=embeddings) diff --git a/advance_ai_agents/ScholarLens/rag/__init__.py b/advance_ai_agents/ScholarLens/rag/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/advance_ai_agents/ScholarLens/requirements.txt b/advance_ai_agents/ScholarLens/requirements.txt new file mode 100644 index 00000000..2d75747c --- /dev/null +++ b/advance_ai_agents/ScholarLens/requirements.txt @@ -0,0 +1,13 @@ +google-genai +langchain-groq +arxiv +crewai +crewai-tools +streamlit +langchain-community +langchain_text_splitters +PyMuPDF +agno==1.8.1 +pinecone==5.4.2 +pypdf +pysqlite3-binary \ No newline at end of file diff --git a/simple_ai_agents/Career_Guidence/.gitignore b/simple_ai_agents/Career_Guidence/.gitignore new file mode 100644 index 00000000..61b27c4f --- /dev/null +++ b/simple_ai_agents/Career_Guidence/.gitignore @@ -0,0 +1,8 @@ +.env +./env +.__pycache__ +./*.pyc +*.pyo +.venv +.*env +*.env \ No newline at end of file diff --git a/simple_ai_agents/Career_Guidence/README.md b/simple_ai_agents/Career_Guidence/README.md new file mode 100644 index 00000000..76a509a6 --- /dev/null +++ b/simple_ai_agents/Career_Guidence/README.md @@ -0,0 +1,123 @@ + +# 🎯 Career Guidance AI System + +An **AI-powered career recommendation and guidance platform** built with **Streamlit**, leveraging **Agentic AI**, **LLMs**, and **web APIs** to provide personalized career suggestions, growth paths, salary insights, and demand analysis based on user-selected career preferences. + +--- + +## πŸš€ Features + +### 1. **Interactive Career Chatbot** + +* Real-time conversational interface to guide users in exploring career paths. +* Suggests relevant skills, courses, and learning resources. +* Provides salary expectations and job market demand data. + +### 2. **Career Path Recommendations** + +* Select your desired careers. +* AI agents fetch career growth paths, industry insights, and role descriptions. +* Data-driven salary and demand predictions. + +### 3. **Industry Insights & Market Trends** + +* Integrates with **SerpAPI** for real-time web data. +* Uses **LLM-based analysis** for summarizing industry trends. + +--- + +## πŸ—οΈ Tech Stack + +* **Frontend**: [Streamlit](https://streamlit.io) for interactive UI +* **LLM Framework**: [CrewAI](https://www.crewai.com) for agent orchestration +* **LLMs**: Groq & Google Gemini +* **Data Sources**: SerpAPI +* **Backend Logic**: Python +* **Environment Management**: python-dotenv + +--- + +## πŸ“‚ Project Structure + +``` +Career_Guidence-main/ +β”œβ”€β”€ app.py # Main Streamlit app entry point +β”œβ”€β”€ career_chatbot.py # Chatbot UI & logic +β”œβ”€β”€ career_guidance_system.py # Career recommendation & insights logic +β”œβ”€β”€ demo.ipynb # Example notebook for testing +β”œβ”€β”€ requirements.txt # Python dependencies +β”œβ”€β”€ .env # Environment variables +└── README.md # Documentation +``` + +--- + +## βš™οΈ Installation + +1️⃣ **Clone the repository** + +```bash +git clone https://github.com/your-username/Career_Guidence.git +cd Career_Guidence-main +``` + +2️⃣ **Create a virtual environment** + +```bash +python -m venv venv +source venv/bin/activate # On Mac/Linux +venv\Scripts\activate # On Windows +``` + +3️⃣ **Install dependencies** + +```bash +pip install -r requirements.txt +``` + +4️⃣ **Configure environment variables** +Create a `.env` file with: + +```env +SERPAPI_API_KEY=your_serpapi_key +GOOGLE_API_KEY=your_gemini_api_key +GROQ_API_KEY=your_groq_api_key +``` + +--- + +## ▢️ Usage + +Run the app: + +```bash +streamlit run app.py +``` + +In the sidebar: + +* Select **Career Chatbot** to start an interactive conversation. +* Choose **Career Guidance** to get recommendations, paths, salaries, and trends. + +--- +check this: https://careerguidencer.streamlit.app/ +## πŸ“Έ Screenshots +image +image +image +image + + +--- + +## πŸ“œ License + +MIT License Β© 2025 Career Guidance AI + +--- + +## πŸ™Œ Acknowledgements + +* [CrewAI](https://www.crewai.com) for agent orchestration +* [Google Gemini](https://ai.google) and [Groq](https://groq.com) for LLM APIs +* [SerpAPI](https://serpapi.com) for search data diff --git a/simple_ai_agents/Career_Guidence/app.py b/simple_ai_agents/Career_Guidence/app.py new file mode 100644 index 00000000..2cc13280 --- /dev/null +++ b/simple_ai_agents/Career_Guidence/app.py @@ -0,0 +1,679 @@ +import streamlit as st +import pandas as pd +import numpy as np +import plotly.express as px +import plotly.graph_objects as go +from datetime import datetime +import os +from dotenv import load_dotenv +load_dotenv() + + + +# Import the career_guidance_system +from career_guidance_system import CareerGuidanceSystem +from career_chatbot import display_chat_interface + + +# Set page config +st.set_page_config( + page_title="AI Career Guidance Platform", + page_icon="πŸš€", + layout="wide", + initial_sidebar_state="expanded" +) + +# Custom CSS for dark theme +st.markdown(""" + +""", unsafe_allow_html=True) +# groq_api_key=os.getenv('GROQ_API_KEY') +# serpapi_key=os.getenv('SERPAPI_API_KEY') + +# Initialize session state variables +if "groq_api_key" not in st.session_state: + st.session_state.groq_api_key = "" +if "serpapi_key" not in st.session_state: + st.session_state.serpapi_key = "" +if "career_system" not in st.session_state: + st.session_state.career_system = None +if "selected_career" not in st.session_state: + st.session_state.selected_career = None +if "selected_category" not in st.session_state: + st.session_state.selected_category = None +if "user_profile" not in st.session_state: + st.session_state.user_profile = {} +if "career_analysis" not in st.session_state: + st.session_state.career_analysis = None +if "show_chat" not in st.session_state: + st.session_state.show_chat = False +if "messages" not in st.session_state: + st.session_state.messages = [] + +# Title and description +st.markdown("
πŸš€ AI-Powered Career Guidance Platform
", unsafe_allow_html=True) +st.markdown("
Explore career options, analyze job markets, and create personalized learning paths with AI
", unsafe_allow_html=True) + +# Sidebar for API key and user profile +with st.sidebar: + st.header("Configuration") + groq_api_key = st.text_input("Groq API Key", type="password", value=st.session_state.groq_api_key) + serpapi_key = st.text_input("SerpAPI Key (for web search)", type="password", value=st.session_state.serpapi_key) + + if groq_api_key: + st.session_state.groq_api_key = groq_api_key + st.session_state.serpapi_key = serpapi_key + + # Initialize career guidance system if API key is provided + if not st.session_state.career_system: + with st.spinner("Initializing AI systems..."): + st.session_state.career_system = CareerGuidanceSystem( + groq_api_key=groq_api_key, + serpapi_key=serpapi_key + ) + st.success("Career guidance system initialized successfully!") + if serpapi_key: + st.info("Web search capabilities enabled! The system will provide up-to-date information on careers.") + else: + st.warning("Web search capabilities are disabled. For the best experience, provide a SerpAPI key.") + + + # User profile section + st.markdown("### πŸ‘€ Your Profile") + user_name = st.text_input("Name", value=st.session_state.user_profile.get("name", "")) + + education_level = st.selectbox( + "Education Level", + ["High School", "Some College", "Bachelor's Degree", "Master's Degree", "PhD", "Other"], + index=2 + ) + + experience = st.selectbox( + "Experience Level", + ["Student/No experience", "0-2 years", "3-5 years", "5-10 years", "10+ years"], + index=0 + ) + + # Update profile in session state + if user_name: + st.session_state.user_profile = { + "name": user_name, + "education": education_level, + "experience": experience + } + + +# Create tabs for main content +tab1, tab2, tab3, tab4, tab5 = st.tabs([ + "πŸ” Discover Careers", + "πŸ“Š Market Analysis", + "πŸ“š Learning Roadmap", + "πŸ’‘ Career Insights", + "πŸ’¬ Chat Assistant" +]) + +# Tab 1: Discover Careers +with tab1: + st.markdown("## Discover Your Ideal Career Path") + + # Check if API key is provided + if not st.session_state.groq_api_key: + st.warning("Please enter your Groq API key in the sidebar to get started.") + else: + # Display the user's profile summary + if st.session_state.user_profile and "name" in st.session_state.user_profile: + st.markdown(f""" +
+

πŸ‘‹ Hello, {st.session_state.user_profile['name']}!

+

+ Based on your profile as a {st.session_state.user_profile['education']} + graduate with {st.session_state.user_profile['experience']} of experience, + we'll help you find the perfect career path. +

+
+ """, unsafe_allow_html=True) + + # Career categories + st.markdown("### Select a Career Category") + + # Get career options from the system + if st.session_state.career_system: + career_options = st.session_state.career_system.get_career_options() + else: + # Fallback options + career_options = { + "Technology": [ + "Software Engineering", + "Data Science", + "Cybersecurity", + "AI/ML Engineering", + "DevOps", + "Cloud Architecture", + "Mobile Development" + ], + "Healthcare": [ + "Medicine", + "Nursing", + "Pharmacy", + "Biomedical Engineering", + "Healthcare Administration", + "Physical Therapy" + ], + "Business": [ + "Finance", + "Marketing", + "Management", + "Human Resources", + "Entrepreneurship", + "Business Analysis" + ], + "Creative": [ + "Graphic Design", + "UX/UI Design", + "Content Creation", + "Digital Marketing", + "Animation", + "Film Production" + ] + } + + # Display category buttons + col1, col2 = st.columns(2) + with col1: + if st.button("πŸ’» Technology", help="Careers in software, data, cybersecurity, and more", key="tech_button", use_container_width=True): + st.session_state.selected_category = "Technology" + st.session_state.career_analysis = None + with col2: + if st.button("πŸ₯ Healthcare", help="Medical and health-related careers", key="health_button", use_container_width=True): + st.session_state.selected_category = "Healthcare" + st.session_state.career_analysis = None + + col3, col4 = st.columns(2) + with col3: + if st.button("πŸ’Ό Business", help="Finance, marketing, management careers", key="business_button", use_container_width=True): + st.session_state.selected_category = "Business" + st.session_state.career_analysis = None + with col4: + if st.button("🎨 Creative", help="Design, content creation, and artistic careers", key="creative_button", use_container_width=True): + st.session_state.selected_category = "Creative" + st.session_state.career_analysis = None + + # Display career options if category is selected + if st.session_state.selected_category: + st.markdown(f"### {st.session_state.selected_category} Careers") + + # Show career options + selected_careers = career_options[st.session_state.selected_category] + career_cols = st.columns(2) + + for i, career in enumerate(selected_careers): + with career_cols[i % 2]: + if st.button(career, key=f"career_{i}", use_container_width=True): + st.session_state.selected_career = career + st.session_state.career_analysis = None + + # Show selected career details + if st.session_state.selected_career: + st.markdown(f""" +
+

🎯 Selected Career: {st.session_state.selected_career}

+

+ Let's analyze this career path to help you understand the opportunities, + requirements, and job market. +

+
+ """, unsafe_allow_html=True) + + # Allow user to analyze career + if st.session_state.career_analysis is None: + # Status of career system + if st.session_state.career_system: + if st.session_state.serpapi_key: + st.success("Our AI career advisors are ready to provide detailed analysis with up-to-date information!") + else: + st.success("Our AI career advisors are ready to provide detailed analysis!") + + if st.button("πŸ” Analyze This Career Path", type="primary", use_container_width=True): + with st.spinner(f"Analyzing {st.session_state.selected_career} career path... This may take a few minutes."): + try: + # Use the comprehensive career analysis method + if st.session_state.career_system: + career_analysis = st.session_state.career_system.comprehensive_career_analysis( + st.session_state.selected_career, + st.session_state.user_profile + ) + else: + # Fallback to basic analysis + career_analysis = { + "career_name": st.session_state.selected_career, + "research": f"Analysis for {st.session_state.selected_career} would be generated by AI in a real implementation.", + "market_analysis": f"Market analysis for {st.session_state.selected_career}.", + "learning_roadmap": f"Learning roadmap for {st.session_state.selected_career}.", + "industry_insights": f"Industry insights for {st.session_state.selected_career}." + } + + st.session_state.career_analysis = career_analysis + st.success("Analysis complete!") + st.session_state.show_chat = True # Enable chat after analysis + st.rerun() + except Exception as e: + st.error(f"Error during analysis: {str(e)}") + + # Display analysis results if available + if st.session_state.career_analysis: + # Get the research content + research = st.session_state.career_analysis.get("research", "") + if isinstance(research, str) and research: + st.markdown(f""" +
+

Overview of {st.session_state.selected_career}

+
+ {research} +
+
+ """, unsafe_allow_html=True) + +# Tab 2: Market Analysis +with tab2: + st.markdown("## Job Market Analysis") + + if not st.session_state.groq_api_key: + st.warning("Please enter your Groq API key in the sidebar to get started.") + elif not st.session_state.selected_career: + st.info("Please select a career in the 'Discover Careers' tab first.") + else: + st.markdown(f"### Market Analysis for: {st.session_state.selected_career}") + + # Check if we already have analysis + if st.session_state.career_analysis and "market_analysis" in st.session_state.career_analysis: + # Display the market analysis + market_analysis = st.session_state.career_analysis["market_analysis"] + + # Display the market analysis as Markdown + st.markdown(f""" +
+

πŸ“Š Market Analysis

+
+ {market_analysis} +
+
+ """, unsafe_allow_html=True) + + # Job growth visualization + st.markdown("### Job Growth Projection") + + # Create sample data + years = list(range(2025, 2030)) + growth_rate = np.random.uniform(0.05, 0.15) + starting_jobs = np.random.randint(80000, 200000) + jobs = [starting_jobs * (1 + growth_rate) ** i for i in range(5)] + + # Calculate CAGR (Compound Annual Growth Rate) + cagr = (jobs[-1]/jobs[0])**(1/4) - 1 # 4-year CAGR + + job_fig = px.line( + x=years, + y=jobs, + labels={"x": "Year", "y": "Projected Jobs"}, + title=f"Projected Job Growth for {st.session_state.selected_career}" + ) + + # Apply styling + job_fig.update_layout( + template="plotly_dark", + paper_bgcolor="#212121", + plot_bgcolor="#212121", + font=dict(color="#E0E0E0"), + title_font=dict(color="#82B1FF"), + xaxis=dict(gridcolor="#424242"), + yaxis=dict(gridcolor="#424242") + ) + + job_fig.update_traces(mode="lines+markers", line=dict(width=3, color="#2196F3"), marker=dict(size=10)) + + # Add annotation for CAGR + job_fig.add_annotation( + x=years[2], + y=jobs[2], + text=f"CAGR: {cagr:.1%}", + showarrow=True, + arrowhead=1, + arrowsize=1, + arrowwidth=2, + arrowcolor="#FF5722", + font=dict(size=14, color="#FF5722"), + bgcolor="#212121", + bordercolor="#FF5722", + borderwidth=2, + borderpad=4, + ax=-50, + ay=-40 + ) + + st.plotly_chart(job_fig, use_container_width=True) + + # Salary analysis + st.markdown("### Salary Analysis") + + experience_levels = ["Entry Level", "Mid Level", "Senior", "Expert"] + base_salary = np.random.randint(60000, 90000) + salaries = [base_salary] + for i in range(1, 4): + salaries.append(salaries[-1] * (1 + np.random.uniform(0.2, 0.4))) + + salary_fig = px.bar( + x=experience_levels, + y=salaries, + labels={"x": "Experience Level", "y": "Annual Salary ($)"}, + title=f"Salary by Experience Level - {st.session_state.selected_career}" + ) + + # Apply styling + salary_fig.update_layout( + template="plotly_dark", + paper_bgcolor="#212121", + plot_bgcolor="#212121", + font=dict(color="#E0E0E0"), + title_font=dict(color="#82B1FF"), + xaxis=dict(gridcolor="#424242"), + yaxis=dict(gridcolor="#424242") + ) + + salary_fig.update_traces(marker=dict(color=["#64B5F6", "#42A5F5", "#2196F3", "#1976D2"])) + + st.plotly_chart(salary_fig, use_container_width=True) + + else: + # Generate new market analysis + if st.button("Generate Market Analysis", type="primary", use_container_width=True): + with st.spinner("Generating market analysis with up-to-date information..."): + try: + if st.session_state.career_system: + # Use the market analysis method + market_analysis = st.session_state.career_system.analyze_market_trends( + st.session_state.selected_career + ) + + # Save to session state + if "career_analysis" not in st.session_state: + st.session_state.career_analysis = {} + + if isinstance(st.session_state.career_analysis, dict): + st.session_state.career_analysis["market_analysis"] = market_analysis + else: + st.session_state.career_analysis = { + "career_name": st.session_state.selected_career, + "market_analysis": market_analysis + } + + # Show success message and rerun + st.success("Market analysis complete!") + st.rerun() + else: + st.error("Career guidance system not initialized. Please check your API key.") + + except Exception as e: + st.error(f"Error generating market analysis: {str(e)}") + +# Tab 3: Learning Roadmap +with tab3: + st.markdown("## Personalized Learning Roadmap") + + if not st.session_state.groq_api_key: + st.warning("Please enter your Groq API key in the sidebar to get started.") + elif not st.session_state.selected_career: + st.info("Please select a career in the 'Discover Careers' tab first.") + else: + st.markdown(f"### Learning Roadmap for: {st.session_state.selected_career}") + + # Experience level for roadmap + experience_options = { + "Student/No experience": "beginner", + "0-2 years": "beginner", + "3-5 years": "intermediate", + "5-10 years": "advanced", + "10+ years": "expert" + } + + user_experience = st.session_state.user_profile.get("experience", "Student/No experience") + experience_level = experience_options.get(user_experience, "beginner") + + # Display user's current level + st.markdown(f""" +
+

Your Current Level: {experience_level.title()}

+

This roadmap is tailored for someone at your experience level.

+
+ """, unsafe_allow_html=True) + + # Check if we already have a roadmap in the career analysis + if st.session_state.career_analysis and "learning_roadmap" in st.session_state.career_analysis: + roadmap = st.session_state.career_analysis["learning_roadmap"] + + # Display roadmap + st.markdown(f""" +
+

πŸ“š Learning Roadmap

+
+ {roadmap} +
+
+ """, unsafe_allow_html=True) + + # Create a simple timeline visualization + st.markdown("### Learning Journey Timeline") + + # Create a timeline dataframe + months = ["Initial Setup", "3 Months", "6 Months", "9 Months", "1 Year", "2 Years"] + progress = [100, 80, 60, 40, 20, 10] if experience_level == "beginner" else [100, 100, 80, 60, 40, 20] + + fig = px.bar( + x=progress, + y=months, + orientation='h', + labels={"x": "Progress (%)", "y": "Learning Stage"}, + title=f"Learning Journey for {st.session_state.selected_career}" + ) + + # Apply styling + fig.update_layout( + template="plotly_dark", + paper_bgcolor="#212121", + plot_bgcolor="#212121", + font=dict(color="#E0E0E0"), + xaxis=dict(gridcolor="#424242"), + yaxis=dict(gridcolor="#424242", categoryorder="array", categoryarray=months[::-1]) + ) + + fig.update_traces(marker=dict(color="#4CAF50")) + + st.plotly_chart(fig, use_container_width=True) + + else: + # Generate new roadmap + if st.button("Generate Learning Roadmap", type="primary", use_container_width=True): + with st.spinner("Generating personalized learning roadmap with current resources..."): + try: + if st.session_state.career_system: + # Use the learning roadmap method + roadmap = st.session_state.career_system.create_learning_roadmap( + st.session_state.selected_career, + experience_level + ) + + # Save to session state + if "career_analysis" not in st.session_state: + st.session_state.career_analysis = {} + + if isinstance(st.session_state.career_analysis, dict): + st.session_state.career_analysis["learning_roadmap"] = roadmap + else: + st.session_state.career_analysis = { + "career_name": st.session_state.selected_career, + "learning_roadmap": roadmap + } + + # Show success and rerun + st.success("Learning roadmap generated successfully!") + st.rerun() + else: + st.error("Career guidance system not initialized. Please check your API key.") + + except Exception as e: + st.error(f"Error generating roadmap: {str(e)}") + +# Tab 4: Career Insights +with tab4: + st.markdown("## Advanced Career Insights") + + if not st.session_state.groq_api_key: + st.warning("Please enter your groq API key in the sidebar to get started.") + elif not st.session_state.selected_career: + st.info("Please select a career in the 'Discover Careers' tab first.") + else: + # Display career insights + if st.session_state.career_analysis and "industry_insights" in st.session_state.career_analysis: + insights_text = st.session_state.career_analysis["industry_insights"] + + # Display insights + st.markdown(f""" +
+

πŸ’‘ Industry Insights

+
+ {insights_text} +
+
+ """, unsafe_allow_html=True) + + # Display skills visualization + st.markdown("### Key Skills Assessment") + + skills = { + "Technical": np.random.randint(70, 95), + "Problem-solving": np.random.randint(70, 95), + "Communication": np.random.randint(70, 95), + "Teamwork": np.random.randint(70, 95), + "Industry Knowledge": np.random.randint(70, 95) + } + + skills_fig = px.bar( + x=list(skills.keys()), + y=list(skills.values()), + labels={"x": "Skill", "y": "Importance (%)"}, + title=f"Skills Importance for {st.session_state.selected_career}" + ) + + # Apply styling + skills_fig.update_layout( + template="plotly_dark", + paper_bgcolor="#212121", + plot_bgcolor="#212121", + font=dict(color="#E0E0E0"), + xaxis=dict(gridcolor="#424242"), + yaxis=dict(gridcolor="#424242") + ) + + st.plotly_chart(skills_fig, use_container_width=True) + + else: + # Generate new insights + if st.button("Generate Industry Insights", type="primary", use_container_width=True): + with st.spinner("Gathering industry insights from professionals..."): + try: + if st.session_state.career_system: + # Get insights using the career system + insights = st.session_state.career_system.get_career_insights( + st.session_state.selected_career + ) + + # Save to session state + if "career_analysis" not in st.session_state: + st.session_state.career_analysis = {} + + if isinstance(st.session_state.career_analysis, dict): + st.session_state.career_analysis["industry_insights"] = insights + else: + st.session_state.career_analysis = { + "career_name": st.session_state.selected_career, + "industry_insights": insights + } + + st.success("Industry insights generated successfully!") + st.rerun() + else: + st.error("Career guidance system not initialized. Please check your API key.") + + except Exception as e: + st.error(f"Error generating insights: {str(e)}") + +# Tab 5: Chat Assistant +with tab5: + st.markdown("## Career Chat Assistant") + + if not st.session_state.groq_api_key: + st.warning("Please enter your GROQ API key in the sidebar to get started.") + elif not st.session_state.selected_career: + st.info("Please select a career in the 'Discover Careers' tab first.") + else: + # Display integrated chat interface + career_data = st.session_state.career_analysis + career_system = st.session_state.career_system + + display_chat_interface(career_data, career_system) + +# Add information about the AI system +with st.expander("ℹ️ About this AI Career Guidance System"): + st.markdown(""" + This AI-powered Career Guidance Platform uses advanced AI technologies to provide personalized career insights: + + - **LangChain**: For structured interaction with AI language models + - **Web Search**: The system can search the internet for up-to-date information (requires SerpAPI key) + - **Streamlit**: Powers the interactive web interface + + The system provides five key services: + 1. **Career Discovery**: Explore career options across different fields + 2. **Market Analysis**: Understand job growth, salary trends, and market demand + 3. **Learning Roadmap**: Get personalized education and skill development plans + 4. **Industry Insights**: Learn about workplace culture, advancement opportunities, and day-to-day responsibilities + 5. **Chat Assistant**: Ask specific questions about your selected career path + + For the best experience, enter your API key in the sidebar. + """) \ No newline at end of file diff --git a/simple_ai_agents/Career_Guidence/career_chatbot.py b/simple_ai_agents/Career_Guidence/career_chatbot.py new file mode 100644 index 00000000..4d885792 --- /dev/null +++ b/simple_ai_agents/Career_Guidence/career_chatbot.py @@ -0,0 +1,362 @@ +from langchain_community.vectorstores import FAISS +import streamlit as st +import time +import numpy as np +from langchain.text_splitter import RecursiveCharacterTextSplitter +from langchain.chains import ConversationalRetrievalChain +from langchain_groq import ChatGroq +import os +from langchain.prompts import PromptTemplate +from dotenv import load_dotenv +from langchain_google_genai import GoogleGenerativeAIEmbeddings +load_dotenv() + +GOOGLE_API_KEY = st.secrets["GOOGLE_API_KEY"] +# GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY") + +class CareerChatAssistant: + def __init__(self, career_system=None): + """Initialize the career chat assistant with the career guidance system""" + self.career_system = career_system + self.groq_api_key = career_system.groq_api_key if career_system else None + self.vector_store = None + self.retrieval_chain = None + + + self.conversation_history = [] + self.chat_history = [] + + def add_to_history(self, role, message): + """Add a message to the conversation history""" + self.conversation_history.append({"role": role, "message": message}) + + def get_formatted_history(self): + """Get the conversation history formatted for prompt""" + formatted = "" + for entry in self.conversation_history: + formatted += f"{entry['role']}: {entry['message']}\n" + return formatted + + def initialize_rag(self, career_data): + """Initialize RAG with career analysis data""" + if not self.groq_api_key or not career_data: + return False + + try: + + # Initialize embeddings + # embeddings = OpenAIEmbeddings( + # model="provider-3/text-embedding-ada-002", + # ) + embeddings = GoogleGenerativeAIEmbeddings(model="models/embedding-001") + + documents = [] + if "research" in career_data: + documents.append(f"Career Overview: {career_data['research']}") + if "market_analysis" in career_data: + documents.append(f"Market Analysis: {career_data['market_analysis']}") + if "learning_roadmap" in career_data: + documents.append(f"Learning Roadmap: {career_data['learning_roadmap']}") + if "industry_insights" in career_data: + documents.append(f"Industry Insights: {career_data['industry_insights']}") + + if not documents: + return False + + + text_splitter = RecursiveCharacterTextSplitter( + chunk_size=1000, + chunk_overlap=100, + length_function=len, + ) + + chunks = text_splitter.create_documents([" ".join(documents)]) + + + self.vector_store = FAISS.from_documents(chunks, embeddings) + + structured_prompt_template = """ + You are a Career Chat Assistant providing information about careers based on detailed analysis. + + Context information from career analysis: + {context} + + Chat History: + {chat_history} + + Human Question: {question} + + Provide a clear, concise, and structured response. Format your answer using bullet points or numbered lists + where appropriate. Organize information into clear categories with headings when the answer requires multiple + sections. Make the response easy to scan and understand at a glance. + + If multiple aspects need to be covered, use separate bullet points for each aspect. + If providing steps or a process, use numbered lists. + Use markdown formatting to enhance readability. + Keep the answer focused and directly relevant to the question. + + Assistant Response: + """ + + structured_prompt = PromptTemplate( + template=structured_prompt_template, + input_variables=["context", "chat_history", "question"] + ) + + + llm = ChatGroq(model='gemma2-9b-it', groq_api_key=self.groq_api_key, temperature=0.2) + self.retrieval_chain = ConversationalRetrievalChain.from_llm( + llm=llm, + retriever=self.vector_store.as_retriever(search_kwargs={"k": 3}), + combine_docs_chain_kwargs={"prompt": structured_prompt} + ) + + return True + except Exception as e: + print(f"Error initializing RAG: {str(e)}") + return False + + def process_question(self, question, career_data=None): + """Process a user question about career data using RAG""" + self.add_to_history("User", question) + + # Initialize RAG if not already done + if not self.vector_store and career_data: + rag_success = self.initialize_rag(career_data) + if rag_success: + st.session_state.rag_initialized = True + + if self.retrieval_chain and st.session_state.get("rag_initialized", False): + try: + # Use RAG to answer the question + result = self.retrieval_chain.invoke({ + "question": question, + "chat_history": self.chat_history + }) + + # Update chat history for context + self.chat_history.append((question, result["answer"])) + + response = result["answer"] + except Exception as e: + print(f"Error in RAG processing: {str(e)}") + # Fallback to standard processing + response = self._fallback_processing(question, career_data) + else: + # Standard processing if RAG is not available + response = self._fallback_processing(question, career_data) + + self.add_to_history("Career Assistant", response) + return response + + def _fallback_processing(self, question, career_data=None): + """Fallback processing when RAG is not available""" + if self.career_system: + # Use the career guidance system's chat function + return self.career_system.chat_with_assistant(question, career_data) + else: + # Simple keyword-based fallback with structured responses + career_name = career_data.get("career_name", "the selected career") if career_data else "this career" + + # Simple response generation based on keywords + if "salary" in question.lower() or "pay" in question.lower() or "money" in question.lower(): + return f""" + ### Salary Information for {career_name} + + The salary ranges vary based on several factors: + + * **Entry-level positions**: $60,000-$80,000 + * **Mid-level professionals**: $80,000-$110,000 + * **Experienced professionals**: $110,000-$150,000 + * **Senior roles**: $150,000+ with additional benefits + + Key factors affecting compensation: + * Geographic location (major tech hubs typically pay more) + * Company size and industry + * Educational background and certifications + * Specialized skills and expertise + """ + + elif "skills" in question.lower() or "learn" in question.lower() or "study" in question.lower(): + return f""" + ### Essential Skills for {career_name} + + ### Technical Skills + * Domain-specific technical knowledge + * Relevant tools and technologies + * Problem-solving methodologies + * Technical documentation + + ### Soft Skills + * Communication (written and verbal) + * Collaboration and teamwork + * Project management + * Time management + * Adaptability and continuous learning + + For best results, develop a balanced combination of both technical expertise and interpersonal abilities. + """ + + elif "job" in question.lower() or "market" in question.lower() or "demand" in question.lower(): + return f""" + ### Job Market for {career_name} + + ### Current Outlook + * Strong projected growth over the next 5-10 years + * Increasing demand across multiple industries + * Evolution of the role with emerging technologies + + ### Top Regions + * Major tech hubs: San Francisco, New York, Seattle + * Growing secondary markets: Austin, Denver, Raleigh + * Increasing remote opportunities + + ### Industry Demand + * Technology sector: High and consistent demand + * Finance and healthcare: Growing adoption + * Manufacturing and retail: Emerging opportunities + """ + + elif "day" in question.lower() or "work" in question.lower() or "like" in question.lower(): + return f""" + ### Typical Day as a {career_name} Professional + + ### Daily Activities + 1. Technical work and core responsibilities + 2. Collaboration meetings with team members + 3. Problem-solving sessions + 4. Documentation and reporting + + ### Work Environment + * Varies by company size and culture + * Often includes a mix of independent and team-based work + * Typically project-based with deadlines and milestones + * Increasing flexibility with hybrid and remote options + + The exact balance depends on: + * Your specific role and seniority + * Company culture and management style + * Project requirements and timelines + * Team structure and workflows + """ + + elif "education" in question.lower() or "degree" in question.lower() or "certification" in question.lower(): + return f""" + ### Educational Pathways for {career_name} + + ### Formal Education + * **Bachelor's Degree**: Common foundation in related field + * **Master's Degree**: Advanced positions and specializations + * **PhD**: Research and highly specialized roles + + ### Alternative Paths + * **Bootcamps**: Intensive, focused training programs + * **Self-directed learning**: Online courses and projects + * **Apprenticeships/Internships**: Learning through practice + + ### Valuable Certifications + * Industry-specific certifications + * Tool and technology certifications + * Methodology certifications + + Many professionals enter the field through non-traditional paths. A portfolio of projects demonstrating skills can be as valuable as formal credentials. + """ + + else: + return f""" + ### Overview of {career_name} Career Path + + ### Core Aspects + * Combines technical expertise with creative problem-solving + * Requires continuous learning and adaptation + * Offers diverse specialization opportunities + + ### Career Benefits + * Competitive compensation packages + * Strong growth opportunities + * Work with cutting-edge technologies + * Intellectual stimulation and challenges + + ### Work-Life Considerations + * Project-based work with varying intensity + * Opportunities for remote and flexible work + * Collaborative environment with diverse teams + * Continuous professional development + """ + +def display_chat_interface(career_data=None, career_system=None): + """Display a chat interface in the Streamlit app""" + st.markdown("

πŸ’¬ Career Chat Assistant

", unsafe_allow_html=True) + + # Initialize the chat assistant in session state if not already done + if "chat_assistant" not in st.session_state: + st.session_state.chat_assistant = CareerChatAssistant(career_system) + # Initialize RAG with career data if available + if career_data: + rag_success = st.session_state.chat_assistant.initialize_rag(career_data) + st.session_state.rag_initialized = rag_success + if rag_success: + st.markdown("
βœ… Enhanced chat capabilities initialized with career data
", unsafe_allow_html=True) + + # Initialize messages in session state if not already done + if "messages" not in st.session_state: + st.session_state.messages = [] + + # Add a welcome message + career_name = career_data.get("career_name", "your selected career") if career_data else "a career" + welcome_message = { + "role": "assistant", + "content": f""" +πŸ‘‹ Hello! I'm your Career Chat Assistant. I can answer questions about {career_name} using the detailed analysis we've generated. + +Here are some questions you might ask: +* What are the typical salary ranges for this career? +* What skills are most important for success? +* How is the job market looking? +* What does a typical day look like? +* What educational paths lead to this career? + +What would you like to know? +""" + } + st.session_state.messages.append(welcome_message) + + # Display chat messages + for message in st.session_state.messages: + with st.chat_message(message["role"]): + st.markdown(message["content"]) + + # Get user input + user_input = st.chat_input("Ask me about this career...") + + if user_input: + # Add user message to chat history + st.session_state.messages.append({"role": "user", "content": user_input}) + + # Display user message + with st.chat_message("user"): + st.markdown(user_input) + + # Generate and display assistant response + with st.chat_message("assistant"): + # Add a placeholder with typing animation + message_placeholder = st.empty() + full_response = "" + + # Process the question with the chat assistant + with st.spinner("Searching career data for relevant information..."): + response = st.session_state.chat_assistant.process_question(user_input, career_data) + + # Simulate typing + for chunk in response.split(): + full_response += chunk + " " + time.sleep(0.01) # Adjust typing speed + message_placeholder.markdown(full_response + "β–Œ") + + message_placeholder.markdown(full_response) + + # Add assistant response to chat history + st.session_state.messages.append({"role": "assistant", "content": full_response}) + + + diff --git a/simple_ai_agents/Career_Guidence/career_guidance_system.py b/simple_ai_agents/Career_Guidence/career_guidance_system.py new file mode 100644 index 00000000..e1b9ce1d --- /dev/null +++ b/simple_ai_agents/Career_Guidence/career_guidance_system.py @@ -0,0 +1,637 @@ +from langchain.chains import LLMChain +from langchain.prompts import PromptTemplate +from langchain.agents import load_tools, initialize_agent, AgentType +from langchain_community.utilities import SerpAPIWrapper +from datetime import datetime +from langchain_groq import ChatGroq + +import os +import time + +class CareerGuidanceSystem: + def __init__(self, groq_api_key=None, serpapi_key=None): + """Initialize the career guidance system""" + self.groq_api_key = groq_api_key + self.serpapi_key = serpapi_key + + # Set environment variable for GroqAI API key + if groq_api_key: + os.environ["GROQ_API_KEY"] = groq_api_key + + # Set environment variable for SerpAPI key + if serpapi_key: + os.environ["SERPER_API_KEY"] = serpapi_key + + # Initialize the language model + if groq_api_key: + self.llm = ChatGroq( + model='gemma2-9b-it', + groq_api_key=groq_api_key, + ) + + # Initialize search tools if SerpAPI key is provided + if serpapi_key: + self.search = SerpAPIWrapper(serpapi_api_key=serpapi_key) + self.tools = load_tools(["serpapi"], llm=self.llm) + self.search_agent = initialize_agent( + self.tools, + self.llm, + agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, + verbose=False, + handle_parsing_errors=True, # Add error handling + max_iterations=6 # Limit number of iterations to prevent loops + ) + else: + self.search = None + self.search_agent = None + else: + self.llm = None + self.search = None + self.search_agent = None + + # Career data storage with caching + self.career_data = {} + self.search_cache = {} + self.user_profile = {} + + # Small set of fallback data for common careers if search fails + self.fallback_career_options = { + "Technology": [ + "Software Engineering", + "Data Science", + "Cybersecurity", + "AI/ML Engineering", + "DevOps", + "Cloud Architecture", + "Mobile Development", + "Web Development", + "Game Development", + "Blockchain Development", + "MLOPS", + "DEVOPS" + ], + "Healthcare": [ + "Medicine", + "Nursing", + "Pharmacy", + "Biomedical Engineering", + "Healthcare Administration", + "Physical Therapy", + "MBBS", + "BHMS", + "BAMS", + "BDS", + ], + "Business": [ + "Finance", + "Marketing", + "Management", + "Human Resources", + "Entrepreneurship", + "Business Analysis", + "CA", + "CMA", + "CS", + "Stock Broker" + ], + "Creative": [ + "Graphic Design", + "UX/UI Design", + "Content Creation", + "Digital Marketing", + "Animation", + "Film Production" + ,"Photography", + "Fashion Design", + "musician" + ] + } + + def search_with_cache(self, query, cache_key, ttl_hours=24, max_retries=3): + """Perform a search with caching to avoid redundant API calls""" + # Check if we have cached results that aren't expired + if cache_key in self.search_cache: + timestamp = self.search_cache[cache_key]['timestamp'] + age_hours = (datetime.now() - timestamp).total_seconds() / 3600 + if age_hours < ttl_hours: + return self.search_cache[cache_key]['data'] + + # If not cached or expired, perform the search + if self.search_agent: + retry_count = 0 + last_error = None + + while retry_count < max_retries: + try: + result = self.search_agent.run(query) + + # Cache the result with timestamp + self.search_cache[cache_key] = { + 'data': result, + 'timestamp': datetime.now() + } + + # Add a small delay to prevent rate limiting + time.sleep(1) + + return result + except Exception as e: + last_error = str(e) + retry_count += 1 + time.sleep(2) # Wait before retrying + + # If all retries failed, fall back to direct LLM query without agent + try: + prompt = PromptTemplate( + input_variables=["query"], + template=""" + Please provide information on the following: {query} + Structure your response clearly with headings and bullet points. + """ + ) + chain = LLMChain(llm=self.llm, prompt=prompt) + result = chain.run(query=query) + + # Cache this result as well + self.search_cache[cache_key] = { + 'data': result, + 'timestamp': datetime.now() + } + + return result + except: + return f"Search failed after {max_retries} attempts. Last error: {last_error}" + else: + return "Search unavailable. Please provide a SerpAPI key for web search capabilities." + + def format_search_results(self, results, title): + """Format search results into a well-structured markdown document""" + formatted = f"# {title}\n\n" + + # Clean up and format the results + if isinstance(results, str): + # Remove any warnings or errors from the output + lines = results.split('\n') + clean_lines = [] + for line in lines: + if "I'll search for" not in line and "I need to search for" not in line: + if not line.startswith("Action:") and not line.startswith("Observation:"): + clean_lines.append(line) + + formatted += "\n".join(clean_lines) + else: + formatted += "No results available." + + return formatted + + def get_career_options(self): + """Return all available career categories and options""" + # Use fallback options if no search available + return self.fallback_career_options + + def comprehensive_career_analysis(self, career_name, user_profile=None): + """Run a comprehensive analysis of a career using web search""" + try: + # Check if we already have this analysis cached + if career_name in self.career_data: + return self.career_data[career_name] + + # If we have search capabilities, use them to get real-time information + if self.search_agent and self.serpapi_key: + # Perform searches for each aspect of the career + + # 1. Career Overview and Skills - use more structured query + overview_query = ( + f"Create a detailed overview of the {career_name} career with the following structure:\n" + f"1. Role Overview: What do {career_name} professionals do?\n" + f"2. Key Responsibilities: List the main tasks and responsibilities\n" + f"3. Required Technical Skills: List the technical skills needed\n" + f"4. Required Soft Skills: List the soft skills needed\n" + f"5. Educational Background: What education is typically required?" + ) + overview_result = self.search_with_cache( + overview_query, + f"{career_name}_overview" + ) + research = self.format_search_results(overview_result, f"{career_name} Career Analysis") + + # 2. Market Analysis - use more structured query + market_query = ( + f"Analyze the job market for {career_name} professionals with the following structure:\n" + f"1. Job Growth Projections: How is job growth trending?\n" + f"2. Salary Ranges: What are salary ranges by experience level?\n" + f"3. Top Industries: Which industries hire the most {career_name} professionals?\n" + f"4. Geographic Hotspots: Which locations have the most opportunities?\n" + f"5. Emerging Trends: What new trends are affecting this field?" + ) + market_result = self.search_with_cache( + market_query, + f"{career_name}_market" + ) + market_analysis = self.format_search_results(market_result, f"{career_name} Market Analysis") + + # 3. Learning Roadmap + experience_level = "beginner" + if user_profile and "experience" in user_profile: + exp = user_profile["experience"] + if "5-10" in exp or "10+" in exp: + experience_level = "advanced" + elif "3-5" in exp: + experience_level = "intermediate" + + roadmap_query = ( + f"Create a learning roadmap for becoming a {career_name} professional at the {experience_level} level with this structure:\n" + f"1. Skills to Develop: What skills should they focus on?\n" + f"2. Education Requirements: What degrees or certifications are needed?\n" + f"3. Recommended Courses: What specific courses or training programs work best?\n" + f"4. Learning Resources: What books, websites, or tools are helpful?\n" + f"5. Timeline: Provide a realistic timeline for skill acquisition" + ) + roadmap_result = self.search_with_cache( + roadmap_query, + f"{career_name}_roadmap_{experience_level}" + ) + learning_roadmap = self.format_search_results(roadmap_result, f"{career_name} Learning Roadmap") + + # 4. Industry Insights + insights_query = ( + f"Provide industry insights for {career_name} professionals with this structure:\n" + f"1. Workplace Culture: What is the typical work environment like?\n" + f"2. Day-to-Day Activities: What does a typical workday include?\n" + f"3. Career Progression: What career advancement paths exist?\n" + f"4. Work-Life Balance: How is the work-life balance in this field?\n" + f"5. Success Strategies: What tips help professionals succeed in this field?" + ) + insights_result = self.search_with_cache( + insights_query, + f"{career_name}_insights" + ) + industry_insights = self.format_search_results(insights_result, f"{career_name} Industry Insights") + + # Create the combined result + results = { + "career_name": career_name, + "research": research, + "market_analysis": market_analysis, + "learning_roadmap": learning_roadmap, + "industry_insights": industry_insights, + "timestamp": datetime.now().isoformat() + } + + # Cache the results + self.career_data[career_name] = results + + return results + + # If no search capabilities, use LLM to generate analysis + elif self.llm: + # Use LLM chains for each analysis component + career_prompt = PromptTemplate( + input_variables=["career"], + template=""" + Provide a comprehensive analysis of the {career} career path. + Include role overview, key responsibilities, required technical and soft skills, + and educational background or alternative paths into the field. + Format the response in markdown with clear headings and bullet points. + """ + ) + + market_prompt = PromptTemplate( + input_variables=["career"], + template=""" + Analyze the current job market for {career} professionals. + Include information on job growth projections, salary ranges by experience level, + top industries hiring, geographic hotspots, and emerging trends affecting the field. + Format the response in markdown with clear headings. + """ + ) + + roadmap_prompt = PromptTemplate( + input_variables=["career", "experience_level"], + template=""" + Create a detailed learning roadmap for someone pursuing a {career} career path. + The person is at a {experience_level} level. + Include essential skills to develop, specific education requirements, recommended courses and resources, + and a timeline for skill acquisition. Structure the response with clear sections and markdown formatting. + """ + ) + + insights_prompt = PromptTemplate( + input_variables=["career"], + template=""" + Provide detailed insider insights about working as a {career} professional. + Include information on workplace culture, day-to-day activities, career progression paths, + work-life balance considerations, and success strategies. + Format the response in markdown with clear headings. + """ + ) + + # Create chains and run them + career_chain = LLMChain(llm=self.llm, prompt=career_prompt) + market_chain = LLMChain(llm=self.llm, prompt=market_prompt) + roadmap_chain = LLMChain(llm=self.llm, prompt=roadmap_prompt) + insights_chain = LLMChain(llm=self.llm, prompt=insights_prompt) + + # Get experience level from user profile + experience_level = "beginner" + if user_profile and "experience" in user_profile: + exp = user_profile["experience"] + if "5-10" in exp or "10+" in exp: + experience_level = "advanced" + elif "3-5" in exp: + experience_level = "intermediate" + + # Generate all components + research = career_chain.run(career=career_name) + market_analysis = market_chain.run(career=career_name) + learning_roadmap = roadmap_chain.run(career=career_name, experience_level=experience_level) + industry_insights = insights_chain.run(career=career_name) + + # Create the result dictionary + results = { + "career_name": career_name, + "research": research, + "market_analysis": market_analysis, + "learning_roadmap": learning_roadmap, + "industry_insights": industry_insights, + "timestamp": datetime.now().isoformat() + } + + # Store in cache + self.career_data[career_name] = results + + return results + + # If neither search nor LLM are available + return { + "career_name": career_name, + "research": f"Career analysis for {career_name} unavailable. Please provide API keys for enhanced capabilities.", + "market_analysis": "Market analysis unavailable. Please provide API keys for enhanced capabilities.", + "learning_roadmap": "Learning roadmap unavailable. Please provide API keys for enhanced capabilities.", + "industry_insights": "Industry insights unavailable. Please provide API keys for enhanced capabilities." + } + + except Exception as e: + # Return error information + return { + "career_name": career_name, + "research": f"Error analyzing career: {str(e)}", + "market_analysis": "Market analysis not available due to an error", + "learning_roadmap": "Learning roadmap not available due to an error", + "industry_insights": "Industry insights not available due to an error" + } + + def search_career_information(self, career): + """Get basic information about a specific career using search""" + # Check the cache + if career in self.career_data and "research" in self.career_data[career]: + return self.career_data[career]["research"] + + # Use search agent if available + if self.search_agent: + query = f"What are the key responsibilities, required skills, and education for a {career} career?" + result = self.search_with_cache( + query, + f"{career}_info" + ) + formatted = self.format_search_results(result, f"{career} Career Information") + return formatted + + # Use LLM if available but no search + elif self.llm: + prompt = PromptTemplate( + input_variables=["career"], + template=""" + Provide information about the {career} career path. + Include role description, key responsibilities, required skills, + and typical educational requirements. + Format as markdown with clear sections. + """ + ) + chain = LLMChain(llm=self.llm, prompt=prompt) + return chain.run(career=career) + + # Fallback to generic response + return f"{career} is a career field that requires specialized skills and education. Enable web search for detailed information." + + def analyze_market_trends(self, career): + """Analyze market trends for a specific career using search""" + # Check the cache + if career in self.career_data and "market_analysis" in self.career_data[career]: + return self.career_data[career]["market_analysis"] + + # Use search agent if available + if self.search_agent: + query = f"What are the current job market trends, salary ranges, and growth projections for {career} careers?" + result = self.search_with_cache( + query, + f"{career}_market" + ) + formatted = self.format_search_results(result, f"{career} Market Analysis") + return formatted + + # Use LLM if available but no search + elif self.llm: + prompt = PromptTemplate( + input_variables=["career"], + template=""" + Analyze the current job market for {career} professionals. + Include information on job growth projections, salary ranges by experience level, + top industries hiring, geographic hotspots, and emerging trends affecting the field. + Format the response in markdown with clear headings. + """ + ) + chain = LLMChain(llm=self.llm, prompt=prompt) + return chain.run(career=career) + + # Fallback to generic response + return f"Market analysis for {career} requires web search capabilities. Please provide a SerpAPI key." + + def create_learning_roadmap(self, career, experience_level="beginner"): + """Create a learning roadmap for a specific career""" + # Check the cache + if career in self.career_data and "learning_roadmap" in self.career_data[career]: + return self.career_data[career]["learning_roadmap"] + + # Use search agent if available + if self.search_agent: + query = f"How to become a {career} professional for someone at {experience_level} level? Include skills to develop, education requirements, courses, resources, and timeline" + result = self.search_with_cache( + query, + f"{career}_roadmap_{experience_level}" + ) + formatted = self.format_search_results(result, f"{career} Learning Roadmap") + return formatted + + # Use LLM if available but no search + elif self.llm: + prompt = PromptTemplate( + input_variables=["career", "experience_level"], + template=""" + Create a detailed learning roadmap for someone pursuing a {career} career path. + The person is at a {experience_level} level. + Include essential skills to develop, specific education requirements, recommended courses and resources, + and a timeline for skill acquisition. Structure the response with clear sections and markdown formatting. + """ + ) + chain = LLMChain(llm=self.llm, prompt=prompt) + return chain.run(career=career, experience_level=experience_level) + + # Fallback to generic response + return f"A personalized learning roadmap for {career} requires web search capabilities. Please provide a SerpAPI key." + + def get_career_insights(self, career): + """Get industry insights for a specific career""" + # Check the cache + if career in self.career_data and "industry_insights" in self.career_data[career]: + return self.career_data[career]["industry_insights"] + + # Use search agent if available + if self.search_agent: + query = f"What is the workplace culture, day-to-day activities, career progression, and work-life balance like for {career} professionals?" + result = self.search_with_cache( + query, + f"{career}_insights" + ) + formatted = self.format_search_results(result, f"{career} Industry Insights") + return formatted + + # Use LLM if available but no search + elif self.llm: + prompt = PromptTemplate( + input_variables=["career"], + template=""" + Provide detailed insider insights about working as a {career} professional. + Include information on workplace culture, day-to-day activities, career progression paths, + work-life balance considerations, and success strategies. + Format the response in markdown with clear headings. + """ + ) + chain = LLMChain(llm=self.llm, prompt=prompt) + return chain.run(career=career) + + # Fallback to generic response + return f"Industry insights for {career} require web search capabilities. Please provide a SerpAPI key." + + def chat_with_assistant(self, question, career_data=None): + """Engage in conversation with a user about career questions""" + if not self.llm: + return "Career assistant is not available. Please provide an GROQ API key." + + try: + # Create context from career data if available + context = "" + if career_data and isinstance(career_data, dict): + career_name = career_data.get("career_name", "the selected career") + context = f"The user has selected the {career_name} career path. " + + # Add relevant sections from career data based on question keywords + if any(kw in question.lower() for kw in ["skill", "learn", "study", "education", "degree"]): + context += f"Here's information about the career: {career_data.get('research', '')} " + context += f"Here's learning roadmap information: {career_data.get('learning_roadmap', '')} " + + if any(kw in question.lower() for kw in ["market", "job", "salary", "pay", "demand", "trend"]): + context += f"Here's market analysis information: {career_data.get('market_analysis', '')} " + + if any(kw in question.lower() for kw in ["work", "day", "culture", "balance", "advance"]): + context += f"Here's industry insights information: {career_data.get('industry_insights', '')} " + + # Create prompt for the career assistant + prompt = PromptTemplate( + input_variables=["context", "question"], + template=""" + You are a career guidance assistant helping a user with their career questions. + + Context about the user's selected career: + {context} + + User question: {question} + + Provide a helpful, informative response that directly addresses the user's question. + Be conversational but concise. Include specific advice or information when possible. + Format your response in a structured way with bullet points and headings where appropriate. + If the question is outside your knowledge, acknowledge that and provide general career guidance. + """ + ) + + # Generate response + chain = LLMChain(llm=self.llm, prompt=prompt) + response = chain.run(context=context, question=question) + + return response + + except Exception as e: + return f"I encountered an error while processing your question: {str(e)}" + + def chat_response(self, user_query, career_data=None, user_profile=None): + """ + Generate a response to a user's chat query about a career. + + Parameters: + - user_query: The user's question or message + - career_data: Dictionary containing career analysis information + - user_profile: User profile information + + Returns: + - Formatted HTML string with the response + """ + try: + # Extract career name if available + career_name = career_data.get("career_name", "this career") if career_data else "this career" + + # Prepare system prompt with available career data + system_prompt = f"""You are an expert career advisor specializing in {career_name}. + Answer the user's questions based on the following career data and your knowledge. + Always format your responses with HTML styling - use appropriate headings, lists, + paragraphs, and emphasis to make your response visually appealing and structured. + Always return formatted HTML content, not Markdown. + """ + + # Add career data to the prompt if available + if career_data: + # Add each section of career data we have + for key, value in career_data.items(): + if key != "career_name" and value: + section_name = key.replace("_", " ").title() + system_prompt += f"\n\n{section_name}:\n{value}" + + # Add user profile if available + if user_profile: + profile_text = f"The user has {user_profile.get('education', 'some education')} with " + profile_text += f"{user_profile.get('experience', 'some')} experience. " + + # Add skills if available + if "skills" in user_profile: + profile_text += "Their skill levels (out of 10) are: " + skills = user_profile["skills"] + for skill, level in skills.items(): + profile_text += f"{skill}: {level}, " + profile_text = profile_text.rstrip(", ") + + system_prompt += f"\n\nUser Profile:\n{profile_text}" + + # Get response from LLM using API + response = self.llm.chat( + messages=[ + {"role": "system", "content": system_prompt}, + {"role": "user", "content": user_query} + ] + ) + + answer = response.content + + # Ensure the response is properly formatted as HTML + if "<" not in answer: + # If no HTML tags are present, add basic formatting + answer = f"

{answer}

" + + return answer + + except Exception as e: + return f""" +
+

I'm sorry, but I encountered an error while processing your request:

+

{str(e)}

+

Please try again or contact support if the issue persists.

+
+ """ \ No newline at end of file diff --git a/simple_ai_agents/Career_Guidence/demo.ipynb b/simple_ai_agents/Career_Guidence/demo.ipynb new file mode 100644 index 00000000..b2deb26f --- /dev/null +++ b/simple_ai_agents/Career_Guidence/demo.ipynb @@ -0,0 +1,823 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "b767affd", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: google-search-results in /usr/local/python/3.12.1/lib/python3.12/site-packages (2.4.2)\n", + "Requirement already satisfied: requests in /home/codespace/.local/lib/python3.12/site-packages (from google-search-results) (2.32.3)\n", + "Requirement already satisfied: charset-normalizer<4,>=2 in /home/codespace/.local/lib/python3.12/site-packages (from requests->google-search-results) (3.4.2)\n", + "Requirement already satisfied: idna<4,>=2.5 in /home/codespace/.local/lib/python3.12/site-packages (from requests->google-search-results) (3.10)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /home/codespace/.local/lib/python3.12/site-packages (from requests->google-search-results) (2.4.0)\n", + "Requirement already satisfied: certifi>=2017.4.17 in /home/codespace/.local/lib/python3.12/site-packages (from requests->google-search-results) (2025.4.26)\n", + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "pip install google-search-results" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "b8e37218", + "metadata": {}, + "outputs": [], + "source": [ + "from serpapi import GoogleSearch\n", + "\n", + "params = {\n", + " \"engine\": \"youtube\",\n", + " \"search_query\": \" transfer learning\",\n", + " \"api_key\": \"4e047a5fd074fad841b22448b42297f0a14ae6118f6e69aa17ec73199086483f\"\n", + "}\n", + "\n", + "search = GoogleSearch(params)\n", + "\n", + "results = search.get_dict()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "9a254b4c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "error Invalid API key. Your API key should be here: https://serpapi.com/manage-api-key\n" + ] + } + ], + "source": [ + "for i in results:\n", + " print(i, results[i])" + ] + }, + { + "cell_type": "markdown", + "id": "adcabfdd", + "metadata": {}, + "source": [ + "# use video results\n", + "* people_also_watched it give recommendations\n", + "* channels_new_to_you this give suggestion and application" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "8ddbd3c8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 How to choose an embedding model\n", + "0 7111\n", + "--------------------\n", + "1 The Only Embedding Model You Need for RAG\n", + "1 17217\n", + "--------------------\n", + "2 What are Word Embeddings?\n", + "2 101295\n", + "--------------------\n", + "3 What is an embedding model?\n", + "3 352\n", + "--------------------\n", + "4 Vector Databases simply explained! (Embeddings & Indexes)\n", + "4 502115\n", + "--------------------\n", + "5 How to Choose an Embedding Model?\n", + "5 490\n", + "--------------------\n", + "6 7. Embeddings in Depth - Part of the Ollama Course\n", + "6 25677\n", + "--------------------\n", + "7 Machine Learning Crash Course: Embeddings\n", + "7 27496\n", + "--------------------\n", + "8 Build an AI Chat Service using OpenAI's Embedding Model\n", + "8 36\n", + "--------------------\n", + "9 A Beginner's Guide to Vector Embeddings\n", + "9 61266\n", + "--------------------\n", + "10 Embedding model evaluation & selection guide\n", + "10 680\n", + "--------------------\n", + "11 400x Faster Embeddings! - Static & Distilled Embedding Models\n", + "11 5008\n", + "--------------------\n", + "12 Best Embedding Model for RAG\n", + "12 296\n", + "--------------------\n", + "13 OpenAI Embeddings Explained in 5 Minutes\n", + "13 27132\n", + "--------------------\n", + "14 Word Embedding and Word2Vec, Clearly Explained!!!\n", + "14 448110\n", + "--------------------\n", + "15 What are text embeddings?\n", + "15 14085\n", + "--------------------\n", + "16 Vector databases are so hot right now. WTF are they?\n", + "16 1057587\n", + "--------------------\n", + "17 Training State-of-the-Art Sentence Embedding Models\n", + "17 8654\n", + "--------------------\n", + "18 OpenAI vs. Open-source Embedding Model Showdown\n", + "18 2982\n", + "--------------------\n", + "19 Text embeddings & semantic search\n", + "19 44642\n", + "--------------------\n" + ] + } + ], + "source": [ + "# len(results.get('video_results'))\n", + "for i in range(len(results.get('video_results'))):\n", + " print(i, results.get('video_results')[i].get('title'))\n", + " print(i, results.get('video_results')[i].get('views'))\n", + " print('-' * 20)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "69698387", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'channels_new_to_you': [{'channel': {'link': 'https://www.youtube.com/@fahdmirza',\n", + " 'name': 'Fahd Mirza',\n", + " 'thumbnail': 'https://yt3.ggpht.com/J3-Aidj_-1f90ZU53gXJOE06A6jXBp41FJX16pLEyY1lApFkSWIOilI--QVDoVZGIcHDFfJcVw=s68-c-k-c0x00ffffff-no-rj'},\n", + " 'description': 'This video installs and test new '\n", + " 'Qwen3 embedding models designed for '\n", + " 'text embedding and ranking tasks. '\n", + " 'Buy Me a Coffee to\\xa0...',\n", + " 'length': '11:22',\n", + " 'link': 'https://www.youtube.com/watch?v=hIx7OapeAtk',\n", + " 'position_on_page': 15,\n", + " 'published_date': '1 month ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=hIx7OapeAtk',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/hIx7OapeAtk/mqdefault_6s.webp?du=3000&sqp=COnsrsMG&rs=AOn4CLDUrjt9-ZxRz90-pUA8oV8teLPYgQ',\n", + " 'static': 'https://i.ytimg.com/vi/hIx7OapeAtk/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBjYMQLHMB71yWURGyTvaK3KZj90Q'},\n", + " 'title': 'Qwen3 Embedding Models: Install, Test and '\n", + " 'Compare Locally',\n", + " 'views': 1805},\n", + " {'channel': {'link': 'https://www.youtube.com/@cloudwolfaws',\n", + " 'name': 'CloudWolf AWS',\n", + " 'thumbnail': 'https://yt3.ggpht.com/l5t1pB57xNQNpmUeS3PMclktDmgDxgCaEiI0EsXv7eg4GlUyYpa7zH7FgUjUGYvqvRYN5CN6HOA=s68-c-k-c0x00ffffff-no-rj'},\n", + " 'description': \"Today we're discussing tokens and \"\n", + " 'embeddings for large language '\n", + " 'models. This AWS tutorial will help '\n", + " 'prepare you for the exam.',\n", + " 'length': '5:19',\n", + " 'link': 'https://www.youtube.com/watch?v=OiibD-l7bps',\n", + " 'position_on_page': 16,\n", + " 'published_date': '5 months ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=OiibD-l7bps',\n", + " 'thumbnail': {'static': 'https://i.ytimg.com/vi/OiibD-l7bps/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBbc08bavwGC6-vuFbgddHdwAR-UA'},\n", + " 'title': 'Large Language Models Tutorial: Tokens and '\n", + " 'Embeddings',\n", + " 'views': 191},\n", + " {'channel': {'link': 'https://www.youtube.com/@Datasciencedojo',\n", + " 'name': 'Data Science Dojo',\n", + " 'thumbnail': 'https://yt3.ggpht.com/f9i55fzk9F7jtmGeGCaIswwsirNwwEbonlPmy1jVlgZpsH4igJWBkfpCJKTyxFcRQ8Y9VmHB=s68-c-k-c0x00ffffff-no-rj'},\n", + " 'description': 'In this short video, we break down '\n", + " 'how to choose the best embedding '\n", + " \"model for your use caseβ€”whether it's \"\n", + " 'open source (like\\xa0...',\n", + " 'length': '7:58',\n", + " 'link': 'https://www.youtube.com/watch?v=WGV2tL3vwrY',\n", + " 'position_on_page': 'hidden',\n", + " 'published_date': '3 months ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=WGV2tL3vwrY',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/WGV2tL3vwrY/mqdefault_6s.webp?du=3000&sqp=CMrzrsMG&rs=AOn4CLBca0RAgzzYDCxvMqAzv1SegAL2Fw',\n", + " 'static': 'https://i.ytimg.com/vi/WGV2tL3vwrY/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCllQFE2Ar2kg_AKdW4FkDMJYyvLw'},\n", + " 'title': 'Using Open Source & Closed Source '\n", + " 'Embedding Model with Weaviate',\n", + " 'views': 169},\n", + " {'channel': {'link': 'https://www.youtube.com/@fahdmirza',\n", + " 'name': 'Fahd Mirza',\n", + " 'thumbnail': 'https://yt3.ggpht.com/J3-Aidj_-1f90ZU53gXJOE06A6jXBp41FJX16pLEyY1lApFkSWIOilI--QVDoVZGIcHDFfJcVw=s68-c-k-c0x00ffffff-no-rj'},\n", + " 'description': 'This video shows how to locally '\n", + " 'install Nomic Embed Text V2 Moe, '\n", + " 'which is first embedding model based '\n", + " 'on the\\xa0...',\n", + " 'length': '10:46',\n", + " 'link': 'https://www.youtube.com/watch?v=aA3KTK17B8A',\n", + " 'position_on_page': 'hidden',\n", + " 'published_date': '4 months ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=aA3KTK17B8A',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/aA3KTK17B8A/mqdefault_6s.webp?du=3000&sqp=CMDfrsMG&rs=AOn4CLDX1p4jVddmwt1oHezEaOsYfgLTug',\n", + " 'static': 'https://i.ytimg.com/vi/aA3KTK17B8A/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBY-sw3-DDks-_CzmU7mS1PiO0FoQ'},\n", + " 'title': 'Nomic Embed Text V2 - Multilingual, '\n", + " 'Mixture-of-Experts Embedding Model - '\n", + " 'Install Locally',\n", + " 'views': 1349}],\n", + " 'pagination': {'current': 'https://www.youtube.com/results?search_query=embedding+model',\n", + " 'next': 'https://www.youtube.com/results?search_query=embedding%20model&sp=SBSCAQtkanA0MjA1dEhHVYIBC3A3eVJMSWo5SXlRggELd2dmU0RycVlNSjSCAQswVTFTMFdTc1B1RYIBC2ROMGxzRjJjdm00ggELdFhrSGZicjFaelmCAQthR3diMUtMbXRvZ4IBC215NXdGTlFwRk8wggELN1RLaFlnbWRnMDiCAQtORXJlTzJ6bFhEa4IBCzlna1RfZGZhNXlBggELTGg0eGZqOVhuemuCAQt1WW5sTmx0NS1ZRYIBCzhrSlN0VFJ1TWNzggELdmlack9uSmNsWTCCAQt2bGNRVjRqMmtUb4IBC2tsVHZFd2czb0o0ggELUkhYWktVcjhxT1mCAQtWZ1NGVzMwNTVsc4IBC09BVENnUXROWDJvsgEGCgQIFhAC',\n", + " 'next_page_token': 'SBSCAQtkanA0MjA1dEhHVYIBC3A3eVJMSWo5SXlRggELd2dmU0RycVlNSjSCAQswVTFTMFdTc1B1RYIBC2ROMGxzRjJjdm00ggELdFhrSGZicjFaelmCAQthR3diMUtMbXRvZ4IBC215NXdGTlFwRk8wggELN1RLaFlnbWRnMDiCAQtORXJlTzJ6bFhEa4IBCzlna1RfZGZhNXlBggELTGg0eGZqOVhuemuCAQt1WW5sTmx0NS1ZRYIBCzhrSlN0VFJ1TWNzggELdmlack9uSmNsWTCCAQt2bGNRVjRqMmtUb4IBC2tsVHZFd2czb0o0ggELUkhYWktVcjhxT1mCAQtWZ1NGVzMwNTVsc4IBC09BVENnUXROWDJvsgEGCgQIFhAC'},\n", + " 'people_also_watched': [{'channel': {'link': 'https://www.youtube.com/@IBMTechnology',\n", + " 'name': 'IBM Technology',\n", + " 'thumbnail': 'https://yt3.ggpht.com/7qCmNHAsFvD6RSINuJ1WoGZYoKmm7TDnhORKFqLb8QoeOFh2qFXal8brkzoxNrwqmJTuvOLs=s68-c-k-c0x00ffffff-no-rj',\n", + " 'verified': True},\n", + " 'description': 'How can you find images with similar '\n", + " 'color palettes or landscapes? πŸ–ΌοΈ '\n", + " 'Martin Keen explains how vector '\n", + " 'databases use vector\\xa0...',\n", + " 'extensions': ['CC'],\n", + " 'length': '9:49',\n", + " 'link': 'https://www.youtube.com/watch?v=gl1r1XV0SLw',\n", + " 'position_on_page': 9,\n", + " 'published_date': '3 months ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=gl1r1XV0SLw',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/gl1r1XV0SLw/mqdefault_6s.webp?du=3000&sqp=CMjsrsMG&rs=AOn4CLAhzDvesUspNSXLaOsKuuDKa8U2jQ',\n", + " 'static': 'https://i.ytimg.com/vi/gl1r1XV0SLw/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLA5yIOdCZlMcUB22z5nVm5gkZTnpQ'},\n", + " 'title': 'What is a Vector Database? Powering '\n", + " 'Semantic Search & AI Applications',\n", + " 'views': 327869},\n", + " {'channel': {'link': 'https://www.youtube.com/@RasmusWiding',\n", + " 'name': 'Rasmus Widing',\n", + " 'thumbnail': 'https://yt3.ggpht.com/ytc/AIdro_lTsn_SEf4XL2vXIaaoqLUud5NQ9oq-8qVva6nli6qHfYKIw450e_k2BCXQMQ4EGZfOvw=s68-c-k-c0x00ffffff-no-rj'},\n", + " 'description': 'Https://coff.ee/wirasm Consider '\n", + " 'donating, this will allow me to '\n", + " 'produce more free content and '\n", + " 'resources! Workshop: Sign up for\\xa0'\n", + " '...',\n", + " 'extensions': ['New'],\n", + " 'length': '40:00',\n", + " 'link': 'https://www.youtube.com/watch?v=KVOZ9s1S9Gk',\n", + " 'position_on_page': 10,\n", + " 'published_date': '1 day ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=KVOZ9s1S9Gk',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/KVOZ9s1S9Gk/mqdefault_6s.webp?du=3000&sqp=CInjrsMG&rs=AOn4CLA0I9eIoNfXv2UjTlu53UQPg1gddg',\n", + " 'static': 'https://i.ytimg.com/vi/KVOZ9s1S9Gk/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDj1c9Pg3GfQLqA38FMwyWAMQrkZw'},\n", + " 'title': 'Cole Medin Featured My PRP Framework! '\n", + " 'Context Engineering | Get Structured with '\n", + " 'your AI coding Today',\n", + " 'views': 1962},\n", + " {'channel': {'link': 'https://www.youtube.com/@EngineeringExplained',\n", + " 'name': 'Engineering Explained',\n", + " 'thumbnail': 'https://yt3.ggpht.com/ytc/AIdro_mFX9zV-Eb4C0r8is3Gu7UpwPFm1vH1bbQT72Rv-rme7Po=s68-c-k-c0x00ffffff-no-rj',\n", + " 'verified': True},\n", + " 'description': \"There's a new track weapon out of \"\n", + " \"South Korea, Hyundai's Ioniq 6 N, \"\n", + " 'which finally will give the Tesla '\n", + " 'Model 3 Performance some\\xa0...',\n", + " 'extensions': ['New', '4K'],\n", + " 'length': '12:37',\n", + " 'link': 'https://www.youtube.com/watch?v=l4gmHZ9c6g4',\n", + " 'position_on_page': 'hidden',\n", + " 'published_date': '2 days ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=l4gmHZ9c6g4',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/l4gmHZ9c6g4/mqdefault_6s.webp?du=3000&sqp=COqCr8MG&rs=AOn4CLBJDcTKZxnVBR6Grc6dppQUvqoVaQ',\n", + " 'static': 'https://i.ytimg.com/vi/l4gmHZ9c6g4/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLD1u9eSzLFpuVF-b5Ib_8pyJt1JDA'},\n", + " 'title': 'Hyundai Ioniq 6 N - Finally, A Rival To '\n", + " 'Tesla’s Model 3 Performance',\n", + " 'views': 113668},\n", + " {'channel': {'link': 'https://www.youtube.com/@BehindAsia',\n", + " 'name': 'Behind Asia',\n", + " 'thumbnail': 'https://yt3.ggpht.com/MMYJ2h5kOUD3diQFw5DH4MgFZ161VVqD_CmgpxpCYzLYUHpwmlqoTWjq3w0WL-uMNbygN7DJ=s68-c-k-c0x00ffffff-no-rj',\n", + " 'verified': True},\n", + " 'description': 'With a US$10 billion incentive '\n", + " 'package, India is attempting to '\n", + " 'position itself as a credible, '\n", + " 'democratic alternative to China '\n", + " 'for\\xa0...',\n", + " 'extensions': ['New', 'CC'],\n", + " 'length': '34:33',\n", + " 'link': 'https://www.youtube.com/watch?v=qs0bA020qUM',\n", + " 'position_on_page': 'hidden',\n", + " 'published_date': '3 days ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=qs0bA020qUM',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/qs0bA020qUM/mqdefault_6s.webp?du=3000&sqp=CMv4rsMG&rs=AOn4CLBw8OOV0G2eJb4AFeRg5plENz47Rw',\n", + " 'static': 'https://i.ytimg.com/vi/qs0bA020qUM/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLD-vSqtdPq6otKIj4pvMfPLSdp2SQ'},\n", + " 'title': \"India's Trillion-Dollar Dream: Building a \"\n", + " 'Chip Industry From Scratch',\n", + " 'views': 85831},\n", + " {'channel': {'link': 'https://www.youtube.com/@code4AI',\n", + " 'name': 'Discover AI',\n", + " 'thumbnail': 'https://yt3.ggpht.com/EocTonB0FyVFCLmoHoSBsCBtS4LutlIUQC7FdjY6e5Rfm5vQGEBgLIWGj9WkD73qxA1UZeO577Q=s68-c-k-c0x00ffffff-no-rj'},\n", + " 'description': 'Explore a new AI architecture, that '\n", + " 'combines recurrent neural networks '\n", + " '(RNN) with Transformers (but not '\n", + " 'GPT). A new\\xa0...',\n", + " 'extensions': ['New', '4K'],\n", + " 'length': '31:35',\n", + " 'link': 'https://www.youtube.com/watch?v=QWD55guu0So',\n", + " 'position_on_page': 'hidden',\n", + " 'published_date': '4 days ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=QWD55guu0So',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/QWD55guu0So/mqdefault_6s.webp?du=3000&sqp=CKLorsMG&rs=AOn4CLC-fcwVBfmN3QcjbIXx-V2sz9T2xA',\n", + " 'static': 'https://i.ytimg.com/vi/QWD55guu0So/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAfjTLuuixyVD8pJJGNnwBqzMdUTg'},\n", + " 'title': 'NEW AI Models: Hierarchical Reasoning '\n", + " 'Models (HRM)',\n", + " 'views': 12724},\n", + " {'channel': {'link': 'https://www.youtube.com/@GalLahat',\n", + " 'name': 'Gal Lahat',\n", + " 'thumbnail': 'https://yt3.ggpht.com/Dsz0VcZEM02OaR2_-TN3LNa9pHqNiz6Nl2wz4B53FoPe1EHOWRDPe9sxukkbgyODRmWviJCCUg=s68-c-k-c0x00ffffff-no-rj'},\n", + " 'description': 'This video was sponsored by '\n", + " 'Brilliant The music is created by my '\n", + " 'partner (AI) and me, feel free to '\n", + " 'use it commercially for your own\\xa0'\n", + " '...',\n", + " 'extensions': ['New', '4K'],\n", + " 'length': '13:01',\n", + " 'link': 'https://www.youtube.com/watch?v=RNF0FvRjGZk',\n", + " 'position_on_page': 'hidden',\n", + " 'published_date': '6 days ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=RNF0FvRjGZk',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/RNF0FvRjGZk/mqdefault_6s.webp?du=3000&sqp=CNDwrsMG&rs=AOn4CLDrg2roqFGt5gsAiE7ahEHvxE0V6A',\n", + " 'static': 'https://i.ytimg.com/vi/RNF0FvRjGZk/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDvFK6Ayw-otUukUOga7nkHDrA6cg'},\n", + " 'title': 'I Visualised Attention in Transformers',\n", + " 'views': 24245},\n", + " {'channel': {'link': 'https://www.youtube.com/@tesla',\n", + " 'name': 'Tesla',\n", + " 'thumbnail': 'https://yt3.ggpht.com/YMRZbg8UjcGgiFOj4qxIWynUkxkV9h5KcpbQGO3yuJnU34Lga3UJeCEAP3T-t7MmNj8Yxsw-EQ=s68-c-k-c0x00ffffff-no-rj',\n", + " 'verified': True},\n", + " 'description': \"The world's first autonomous \"\n", + " 'delivery of a car! This Model Y '\n", + " 'drove itself from Gigafactory Texas '\n", + " \"to its new owner's home ~30min\\xa0\"\n", + " '...',\n", + " 'extensions': ['4K'],\n", + " 'length': '3:27',\n", + " 'link': 'https://www.youtube.com/watch?v=GU16hXSSGKs',\n", + " 'position_on_page': 'hidden',\n", + " 'published_date': '9 days ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=GU16hXSSGKs',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/GU16hXSSGKs/mqdefault_6s.webp?du=3000&sqp=CP_brsMG&rs=AOn4CLB23jZm-CKZFm2kTMZG0UE9PG7FCg',\n", + " 'static': 'https://i.ytimg.com/vi/GU16hXSSGKs/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCFRT5PtJ3TofBpU0BKRF2fquLZgg'},\n", + " 'title': 'Tesla Delivers Itself to New Owner',\n", + " 'views': 2042802},\n", + " {'channel': {'link': 'https://www.youtube.com/@stanfordengineering',\n", + " 'name': 'Stanford University School of '\n", + " 'Engineering',\n", + " 'thumbnail': 'https://yt3.ggpht.com/ytc/AIdro_n_ybKM9ATFjCc6IWXp_PyLcXVLZx99xvVdb1b1gAN2kjM=s68-c-k-c0x00ffffff-no-rj'},\n", + " 'description': 'Lecture 2 continues the discussion '\n", + " 'on the concept of representing words '\n", + " 'as numeric vectors and popular '\n", + " 'approaches to\\xa0...',\n", + " 'extensions': ['CC'],\n", + " 'length': '1:18:17',\n", + " 'link': 'https://www.youtube.com/watch?v=ERibwqs9p38',\n", + " 'position_on_page': 'hidden',\n", + " 'published_date': '8 years ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=ERibwqs9p38',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/ERibwqs9p38/mqdefault_6s.webp?du=3000&sqp=CMqGr8MG&rs=AOn4CLBa1qdV368_mdiVBfLs9SFO-GBjug',\n", + " 'static': 'https://i.ytimg.com/vi/ERibwqs9p38/hq720.jpg?sqp=-oaymwE2COgCEMoBSFXyq4qpAygIARUAAIhCGAFwAcABBvABAfgB_gmAAtAFigIMCAAQARhlIGUoZTAP&rs=AOn4CLDThI1NqoSkzydaqZhkb1z6e-5t8w'},\n", + " 'title': 'Lecture 2 | Word Vector Representations: '\n", + " 'word2vec',\n", + " 'views': 522546},\n", + " {'channel': {'link': 'https://www.youtube.com/@TechLead',\n", + " 'name': 'TechLead',\n", + " 'thumbnail': 'https://yt3.ggpht.com/BB2M49atCki1_n2UysXNtL04dmkdIVUMDUtj_MzltJuFbNqWShtpdAS-K-vT-VTo55jC6pKmABw=s68-c-k-c0x00ffffff-no-rj',\n", + " 'verified': True},\n", + " 'description': 'Disclaimer: This description may '\n", + " 'contain affiliate links. '\n", + " 'Cryptocurrencies are not investments '\n", + " 'and are subject to market '\n", + " 'volatility.',\n", + " 'length': '16:29',\n", + " 'link': 'https://www.youtube.com/watch?v=9AXP7tCI9PI',\n", + " 'position_on_page': 'hidden',\n", + " 'published_date': '2 years ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=9AXP7tCI9PI',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/9AXP7tCI9PI/mqdefault_6s.webp?du=3000&sqp=CNbtrsMG&rs=AOn4CLDoBlGwQJxoiY2NicRqQONK5gs1ow',\n", + " 'static': 'https://i.ytimg.com/vi/9AXP7tCI9PI/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAvLfSo2QLRm51zOutDbBCy99PI-Q'},\n", + " 'title': 'Using ChatGPT with YOUR OWN Data. This is '\n", + " 'magical. (LangChain OpenAI API)',\n", + " 'views': 1469194},\n", + " {'channel': {'link': 'https://www.youtube.com/@machinelearningconference5512',\n", + " 'name': 'MLCon | Machine Learning '\n", + " 'Conference',\n", + " 'thumbnail': 'https://yt3.ggpht.com/y05jsc9_i_0PfaCJehd8p2e7fLnP3KenEdfeK-88g-5MBS1MZ8ugBI4OxlGhW5ee1dgKZlASYDg=s68-c-k-c0x00ffffff-no-rj'},\n", + " 'description': 'Join Dr. Roman Grebennikov at MLcon '\n", + " 'Munich 2024 to explore fine-tuning '\n", + " 'large language models (LLMs) for '\n", + " 'semantic search.',\n", + " 'length': '48:30',\n", + " 'link': 'https://www.youtube.com/watch?v=M2mrxeDL-n4',\n", + " 'position_on_page': 'hidden',\n", + " 'published_date': '11 months ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=M2mrxeDL-n4',\n", + " 'thumbnail': {'static': 'https://i.ytimg.com/vi/M2mrxeDL-n4/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLD8imT_pK6l0TpssezpGM7fKNW16A'},\n", + " 'title': 'Practical LLM Fine Tuning For Semantic '\n", + " 'Search | Dr. Roman Grebennikov',\n", + " 'views': 1593}],\n", + " 'search_information': {'total_results': 10195861,\n", + " 'video_results_state': 'Results for exact spelling'},\n", + " 'search_metadata': {'created_at': '2025-07-07 12:58:20 UTC',\n", + " 'id': '686bc46cbc3f0be3442c27bd',\n", + " 'json_endpoint': 'https://serpapi.com/searches/75a05f903665e431/686bc46cbc3f0be3442c27bd.json',\n", + " 'processed_at': '2025-07-07 12:58:20 UTC',\n", + " 'raw_html_file': 'https://serpapi.com/searches/75a05f903665e431/686bc46cbc3f0be3442c27bd.html',\n", + " 'status': 'Success',\n", + " 'total_time_taken': 1.52,\n", + " 'youtube_url': 'https://www.youtube.com/results?search_query=embedding+model'},\n", + " 'search_parameters': {'engine': 'youtube', 'search_query': 'embedding model'},\n", + " 'serpapi_pagination': {'current': 'https://serpapi.com/search.json?engine=youtube&search_query=embedding+model',\n", + " 'next': 'https://serpapi.com/search.json?engine=youtube&search_query=embedding+model&sp=SBSCAQtkanA0MjA1dEhHVYIBC3A3eVJMSWo5SXlRggELd2dmU0RycVlNSjSCAQswVTFTMFdTc1B1RYIBC2ROMGxzRjJjdm00ggELdFhrSGZicjFaelmCAQthR3diMUtMbXRvZ4IBC215NXdGTlFwRk8wggELN1RLaFlnbWRnMDiCAQtORXJlTzJ6bFhEa4IBCzlna1RfZGZhNXlBggELTGg0eGZqOVhuemuCAQt1WW5sTmx0NS1ZRYIBCzhrSlN0VFJ1TWNzggELdmlack9uSmNsWTCCAQt2bGNRVjRqMmtUb4IBC2tsVHZFd2czb0o0ggELUkhYWktVcjhxT1mCAQtWZ1NGVzMwNTVsc4IBC09BVENnUXROWDJvsgEGCgQIFhAC',\n", + " 'next_page_token': 'SBSCAQtkanA0MjA1dEhHVYIBC3A3eVJMSWo5SXlRggELd2dmU0RycVlNSjSCAQswVTFTMFdTc1B1RYIBC2ROMGxzRjJjdm00ggELdFhrSGZicjFaelmCAQthR3diMUtMbXRvZ4IBC215NXdGTlFwRk8wggELN1RLaFlnbWRnMDiCAQtORXJlTzJ6bFhEa4IBCzlna1RfZGZhNXlBggELTGg0eGZqOVhuemuCAQt1WW5sTmx0NS1ZRYIBCzhrSlN0VFJ1TWNzggELdmlack9uSmNsWTCCAQt2bGNRVjRqMmtUb4IBC2tsVHZFd2czb0o0ggELUkhYWktVcjhxT1mCAQtWZ1NGVzMwNTVsc4IBC09BVENnUXROWDJvsgEGCgQIFhAC'},\n", + " 'video_results': [{'channel': {'link': 'https://www.youtube.com/@Weaviate',\n", + " 'name': 'Weaviate β€’ Vector Database',\n", + " 'thumbnail': 'https://yt3.ggpht.com/mJRcoQYNb1mzu6CHg89drxBHNi6xt6n-BFl-_p3QNYaK83Nijnpgef73QxSYpKXOdJXoQo55HqQ=s68-c-k-c0x00ffffff-no-rj'},\n", + " 'description': 'How do you chose the best embedding model '\n", + " 'for your use case? (and how do they even '\n", + " 'work, anyways?) - Learn more in this\\xa0'\n", + " '...',\n", + " 'length': '4:04',\n", + " 'link': 'https://www.youtube.com/watch?v=djp4205tHGU',\n", + " 'position_on_page': 1,\n", + " 'published_date': '5 months ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=djp4205tHGU',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/djp4205tHGU/mqdefault_6s.webp?du=3000&sqp=CIXvrsMG&rs=AOn4CLD6s0z5TNTnvsDPo-qW-nKkKVaa4Q',\n", + " 'static': 'https://i.ytimg.com/vi/djp4205tHGU/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLC_dtlRposbDit4Tor3SXixTGnzaw'},\n", + " 'title': 'How to choose an embedding model',\n", + " 'views': 7111},\n", + " {'channel': {'link': 'https://www.youtube.com/@engineerprompt',\n", + " 'name': 'Prompt Engineering',\n", + " 'thumbnail': 'https://yt3.ggpht.com/vhMNk7glc5VOkyiIjttJ1RcwHErqinDL4z1WmerIM8VU7WSCLAXJIB4lxe64MO3V3ZvVhbhJ6g=s68-c-k-c0x00ffffff-no-rj'},\n", + " 'description': 'I walk you through a single, multimodal '\n", + " 'embedding model that handles text, images, '\n", + " 'tables β€”and even code β€”inside one '\n", + " 'vector\\xa0...',\n", + " 'extensions': ['New', '4K'],\n", + " 'length': '13:52',\n", + " 'link': 'https://www.youtube.com/watch?v=p7yRLIj9IyQ',\n", + " 'position_on_page': 2,\n", + " 'published_date': '5 days ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=p7yRLIj9IyQ',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/p7yRLIj9IyQ/mqdefault_6s.webp?du=3000&sqp=CLjfrsMG&rs=AOn4CLC7sn5liut00sZVmu0MZbUIwfBdoA',\n", + " 'static': 'https://i9.ytimg.com/vi/p7yRLIj9IyQ/hq720_custom_2.jpg?sqp=CKSHr8MG-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCo0ocimAxOv5t2AiAOuUiM7Mu_yw'},\n", + " 'title': 'The Only Embedding Model You Need for RAG',\n", + " 'views': 17217},\n", + " {'channel': {'link': 'https://www.youtube.com/@IBMTechnology',\n", + " 'name': 'IBM Technology',\n", + " 'thumbnail': 'https://yt3.ggpht.com/7qCmNHAsFvD6RSINuJ1WoGZYoKmm7TDnhORKFqLb8QoeOFh2qFXal8brkzoxNrwqmJTuvOLs=s68-c-k-c0x00ffffff-no-rj',\n", + " 'verified': True},\n", + " 'description': 'Word Embeddings the means of turning '\n", + " 'natural language into numerical vectors '\n", + " 'for machine learning tasks. Martin Keen '\n", + " 'explains\\xa0...',\n", + " 'extensions': ['CC'],\n", + " 'length': '8:38',\n", + " 'link': 'https://www.youtube.com/watch?v=wgfSDrqYMJ4',\n", + " 'position_on_page': 3,\n", + " 'published_date': '10 months ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=wgfSDrqYMJ4',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/wgfSDrqYMJ4/mqdefault_6s.webp?du=3000&sqp=CNqBr8MG&rs=AOn4CLCYFzU5jPn1M-lmo_1i8B5U82KDbA',\n", + " 'static': 'https://i.ytimg.com/vi/wgfSDrqYMJ4/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDIjS3J0LgfaEesrCKAOaCkzTOnmA'},\n", + " 'title': 'What are Word Embeddings?',\n", + " 'views': 101295},\n", + " {'channel': {'link': 'https://www.youtube.com/@Redisinc',\n", + " 'name': 'Redis',\n", + " 'thumbnail': 'https://yt3.ggpht.com/cF0hSBjL4-ylVmroWSV9KERZzuJliavhGUKdgY4vVSV6aPPMhMqfZlNWFrw25CQ2ACV1chZ2FQ=s68-c-k-c0x00ffffff-no-rj'},\n", + " 'description': \"Everyone's talking about embedding models \"\n", + " 'latelyβ€”but what do they actually do, and '\n", + " 'why does it matter? In this video\\xa0...',\n", + " 'extensions': ['4K'],\n", + " 'length': '4:17',\n", + " 'link': 'https://www.youtube.com/watch?v=0U1S0WSsPuE',\n", + " 'position_on_page': 4,\n", + " 'published_date': '2 weeks ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=0U1S0WSsPuE',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/0U1S0WSsPuE/mqdefault_6s.webp?du=3000&sqp=CMXIrsMG&rs=AOn4CLB1TDfsuwEtnpL8I4rxkCuB2I8vtQ',\n", + " 'static': 'https://i9.ytimg.com/vi/0U1S0WSsPuE/hq720_custom_2.jpg?sqp=CKSHr8MG-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBx9TtSGd5bPbb9CLSEt540PgTvCA'},\n", + " 'title': 'What is an embedding model?',\n", + " 'views': 352},\n", + " {'channel': {'link': 'https://www.youtube.com/@AssemblyAI',\n", + " 'name': 'AssemblyAI',\n", + " 'thumbnail': 'https://yt3.ggpht.com/5z_-jPDKLrUlaxA0Ow7BRdIAwbh6YQYrqU3pd8Cm6okahuJ3BaawiEPpdWUhwE98v_j3ugUAbA=s68-c-k-c0x00ffffff-no-rj'},\n", + " 'description': 'Vector Databases simply explained. Learn '\n", + " 'what vector databases and vector '\n", + " 'embeddings are and how they work. Then '\n", + " \"I'll go\\xa0...\",\n", + " 'extensions': ['4K'],\n", + " 'length': '4:23',\n", + " 'link': 'https://www.youtube.com/watch?v=dN0lsF2cvm4',\n", + " 'position_on_page': 5,\n", + " 'published_date': '2 years ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=dN0lsF2cvm4',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/dN0lsF2cvm4/mqdefault_6s.webp?du=3000&sqp=CM7OrsMG&rs=AOn4CLA2gZg4Q5rtoZtm2vEreUFtI8Mi3Q',\n", + " 'static': 'https://i.ytimg.com/vi/dN0lsF2cvm4/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLC0xz-care7zAqdFblmBo27KLwSvA'},\n", + " 'title': 'Vector Databases simply explained! (Embeddings & '\n", + " 'Indexes)',\n", + " 'views': 502115},\n", + " {'channel': {'link': 'https://www.youtube.com/@Datasciencedojo',\n", + " 'name': 'Data Science Dojo',\n", + " 'thumbnail': 'https://yt3.ggpht.com/f9i55fzk9F7jtmGeGCaIswwsirNwwEbonlPmy1jVlgZpsH4igJWBkfpCJKTyxFcRQ8Y9VmHB=s68-c-k-c0x00ffffff-no-rj'},\n", + " 'description': 'With so many embedding models '\n", + " 'availableβ€”OpenAI, Cohere, open-source '\n", + " 'optionsβ€”it can be overwhelming to pick the '\n", + " 'right one.',\n", + " 'length': '3:08',\n", + " 'link': 'https://www.youtube.com/watch?v=tXkHfbr1ZzY',\n", + " 'position_on_page': 6,\n", + " 'published_date': '3 months ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=tXkHfbr1ZzY',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/tXkHfbr1ZzY/mqdefault_6s.webp?du=3000&sqp=CNLsrsMG&rs=AOn4CLBzQKxG3s0jc6R_so-V5gwVYhb0Mw',\n", + " 'static': 'https://i.ytimg.com/vi/tXkHfbr1ZzY/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLC5k7TEDPhFMriY6Izq5qPZDUQq5Q'},\n", + " 'title': 'How to Choose an Embedding Model?',\n", + " 'views': 490},\n", + " {'channel': {'link': 'https://www.youtube.com/@technovangelist',\n", + " 'name': 'Matt Williams',\n", + " 'thumbnail': 'https://yt3.ggpht.com/ytc/AIdro_mXVAnytfFhGKFrCuySGeQMMXlclfEdex9Yxx3PVKV9rMI=s68-c-k-c0x00ffffff-no-rj'},\n", + " 'description': \"... and use embeddings with Ollama's API - \"\n", + " 'A practical comparison of different '\n", + " 'embedding models, including: - '\n", + " 'nomic-embed-text\\xa0...',\n", + " 'extensions': ['4K'],\n", + " 'length': '10:27',\n", + " 'link': 'https://www.youtube.com/watch?v=aGwb1KLmtog',\n", + " 'position_on_page': 7,\n", + " 'published_date': '10 months ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=aGwb1KLmtog',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/aGwb1KLmtog/mqdefault_6s.webp?du=3000&sqp=CMjrrsMG&rs=AOn4CLCgHnbcWhpDiFPErV8yPUp8Ge57vw',\n", + " 'static': 'https://i.ytimg.com/vi/aGwb1KLmtog/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBZdtCTjDb9P1nnx4WdjLx5iJqtng'},\n", + " 'title': '7. Embeddings in Depth - Part of the Ollama '\n", + " 'Course',\n", + " 'views': 25677},\n", + " {'channel': {'link': 'https://www.youtube.com/@GoogleDevelopers',\n", + " 'name': 'Google for Developers',\n", + " 'thumbnail': 'https://yt3.ggpht.com/2eI1TjX447QZFDe6R32K0V2mjbVMKT5mIfQR-wK5bAsxttS_7qzUDS1ojoSKeSP0NuWd6sl7qQ=s68-c-k-c0x00ffffff-no-rj',\n", + " 'verified': True},\n", + " 'description': 'An embedding translates large feature '\n", + " 'vectors into a lower-dimensional space '\n", + " 'that encodes meaningful relationships '\n", + " 'between\\xa0...',\n", + " 'extensions': ['4K', 'CC'],\n", + " 'length': '2:37',\n", + " 'link': 'https://www.youtube.com/watch?v=my5wFNQpFO0',\n", + " 'position_on_page': 8,\n", + " 'published_date': '9 months ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=my5wFNQpFO0',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/my5wFNQpFO0/mqdefault_6s.webp?du=3000&sqp=CIyEr8MG&rs=AOn4CLAu32nYFi4McmrIV8b2D9u1N7gPCg',\n", + " 'static': 'https://i.ytimg.com/vi/my5wFNQpFO0/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBewRyA6M18wSbJo0A0Qn8VL8xy1A'},\n", + " 'title': 'Machine Learning Crash Course: Embeddings',\n", + " 'views': 27496},\n", + " {'channel': {'link': 'https://www.youtube.com/@SPS_Tech',\n", + " 'name': 'SPS Tech | Learn Java | Crack '\n", + " 'Interviews',\n", + " 'thumbnail': 'https://yt3.ggpht.com/qNiHXFERRUSU0DsTRacG-OV8KQVKeyePidvDYDZ5n_-quX9e2dXHIqHfifQccjEHCTz_cxusFQ=s68-c-k-c0x00ffffff-no-rj'},\n", + " 'description': 'I created this channel to increase '\n", + " 'knowledge awareness about Java-related '\n", + " 'topics and explain those topics that every '\n", + " 'Java\\xa0...',\n", + " 'extensions': ['New'],\n", + " 'length': '19:07',\n", + " 'link': 'https://www.youtube.com/watch?v=7TKhYgmdg08',\n", + " 'position_on_page': 11,\n", + " 'published_date': '1 day ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=7TKhYgmdg08',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/7TKhYgmdg08/mqdefault_6s.webp?du=3000&sqp=COrHrsMG&rs=AOn4CLD2tbeCaigPs9WPuUXGv8t-go5hkQ',\n", + " 'static': 'https://i.ytimg.com/vi/7TKhYgmdg08/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDCf2KpJItk0QNdxG-iPSqcJ1RBag'},\n", + " 'title': \"Build an AI Chat Service using OpenAI's \"\n", + " 'Embedding Model',\n", + " 'views': 36},\n", + " {'channel': {'link': 'https://www.youtube.com/@colintalkstech',\n", + " 'name': 'Colin Talks Tech',\n", + " 'thumbnail': 'https://yt3.ggpht.com/QnCGvCIHjkkU8QsxIfNpBedRSKtk15RkeGkEi6KpRTacKrIC574SEtZB4i7qzujPFcQKC263PPw=s68-c-k-c0x00ffffff-no-rj'},\n", + " 'description': 'A high level primer on vectors, vector '\n", + " 'embeddings and vector databases. '\n", + " 'References covered in this video: What are '\n", + " 'Vector\\xa0...',\n", + " 'extensions': ['4K'],\n", + " 'length': '8:29',\n", + " 'link': 'https://www.youtube.com/watch?v=NEreO2zlXDk',\n", + " 'position_on_page': 12,\n", + " 'published_date': '2 years ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=NEreO2zlXDk',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/NEreO2zlXDk/mqdefault_6s.webp?du=3000&sqp=CMmFr8MG&rs=AOn4CLCK9VA-CNefPZYYaVLvCqStqjJUiQ',\n", + " 'static': 'https://i.ytimg.com/vi/NEreO2zlXDk/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDeEGUHdkRZTzQ4BrUyYkp7mmKAMg'},\n", + " 'title': \"A Beginner's Guide to Vector Embeddings\",\n", + " 'views': 61266},\n", + " {'channel': {'link': 'https://www.youtube.com/@Weaviate',\n", + " 'name': 'Weaviate β€’ Vector Database',\n", + " 'thumbnail': 'https://yt3.ggpht.com/mJRcoQYNb1mzu6CHg89drxBHNi6xt6n-BFl-_p3QNYaK83Nijnpgef73QxSYpKXOdJXoQo55HqQ=s68-c-k-c0x00ffffff-no-rj'},\n", + " 'description': 'Selecting the right embedding model can '\n", + " \"make or break your AI application's \"\n", + " 'performance. In this guide, JP from '\n", + " 'Weaviate walks\\xa0...',\n", + " 'length': '6:29',\n", + " 'link': 'https://www.youtube.com/watch?v=9gkT_dfa5yA',\n", + " 'position_on_page': 13,\n", + " 'published_date': '1 month ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=9gkT_dfa5yA',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/9gkT_dfa5yA/mqdefault_6s.webp?du=3000&sqp=CICEr8MG&rs=AOn4CLBxqunG17VsyIMzBRvCJB9FMai44g',\n", + " 'static': 'https://i.ytimg.com/vi/9gkT_dfa5yA/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCAJaxzPIZDrv47OinFbsn9mX4s6g'},\n", + " 'title': 'Embedding model evaluation & selection guide',\n", + " 'views': 680},\n", + " {'channel': {'link': 'https://www.youtube.com/@AdamLucek',\n", + " 'name': 'Adam Lucek',\n", + " 'thumbnail': 'https://yt3.ggpht.com/fhph_T2BohvHq3ERpbZAPlkVHtTkOWpn_jZSudlJ7tHXgXZo_yi_4tVpVXXEtkRQNVWREdy-REE=s68-c-k-c0x00ffffff-no-rj'},\n", + " 'description': 'Chapters: 00:00 - Background Embeddings '\n", + " '03:01 - Brilliant! 04:22 - What are Static '\n", + " 'Embeddings 06:47 - W2V Training Example\\xa0'\n", + " '...',\n", + " 'length': '36:33',\n", + " 'link': 'https://www.youtube.com/watch?v=Lh4xfj9Xnzk',\n", + " 'position_on_page': 14,\n", + " 'published_date': '1 month ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=Lh4xfj9Xnzk',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/Lh4xfj9Xnzk/mqdefault_6s.webp?du=3000&sqp=CITSrsMG&rs=AOn4CLBRAYPTLnQ16QsOEkdibBleUpGL7Q',\n", + " 'static': 'https://i.ytimg.com/vi/Lh4xfj9Xnzk/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDIuY1PZhSpDwCPaRgn_M0kwS4xTQ'},\n", + " 'title': '400x Faster Embeddings! - Static & Distilled '\n", + " 'Embedding Models',\n", + " 'views': 5008},\n", + " {'channel': {'link': 'https://www.youtube.com/@SridharKumarKannam',\n", + " 'name': 'Hands-on AI ',\n", + " 'thumbnail': 'https://yt3.ggpht.com/pHs3qiODkmdk1FmzQBXO8noLQFyqoVvBJGHErmwdR0UIAG0-N92E_CJYb0Z-AmKnLgrnSAAYtA=s68-c-k-c0x00ffffff-no-rj'},\n", + " 'description': '#datascience #machinelearning '\n", + " '#deeplearning #datanalytics '\n", + " '#predictiveanalytics '\n", + " '#artificialintelligence #generativeai\\xa0'\n", + " '...',\n", + " 'length': '26:11',\n", + " 'link': 'https://www.youtube.com/watch?v=uYnlNlt5-YE',\n", + " 'position_on_page': 17,\n", + " 'published_date': '11 days ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=uYnlNlt5-YE',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/uYnlNlt5-YE/mqdefault_6s.webp?du=3000&sqp=CKaIr8MG&rs=AOn4CLBdQe5Dwq-4pDITlH_NU1PAxavQow',\n", + " 'static': 'https://i.ytimg.com/vi/uYnlNlt5-YE/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAda-YmRqwgnSrLqscOvofTgbDsAA'},\n", + " 'title': 'Best Embedding Model for RAG',\n", + " 'views': 296},\n", + " {'channel': {'link': 'https://www.youtube.com/@CooperCodes',\n", + " 'name': 'Cooper Codes',\n", + " 'thumbnail': 'https://yt3.ggpht.com/ytc/AIdro_l118oNoOFMD4Fm4hRpXCHGS-hyUbD6tN_wruJO7vzvKw=s68-c-k-c0x00ffffff-no-rj'},\n", + " 'description': 'Embeddings are a massively important part '\n", + " 'of AI ecosystems. In this video we cover '\n", + " 'the basics of OpenAI Embeddings, and '\n", + " 'how\\xa0...',\n", + " 'length': '5:12',\n", + " 'link': 'https://www.youtube.com/watch?v=8kJStTRuMcs',\n", + " 'position_on_page': 18,\n", + " 'published_date': '1 year ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=8kJStTRuMcs',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/8kJStTRuMcs/mqdefault_6s.webp?du=3000&sqp=CKzErsMG&rs=AOn4CLAbtMQx1qyxluzBjDJZkgaAVzvM3g',\n", + " 'static': 'https://i.ytimg.com/vi/8kJStTRuMcs/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDF6mJd9HpVNChrE-9z_AJEG-6TQg'},\n", + " 'title': 'OpenAI Embeddings Explained in 5 Minutes',\n", + " 'views': 27132},\n", + " {'channel': {'link': 'https://www.youtube.com/@statquest',\n", + " 'name': 'StatQuest with Josh Starmer',\n", + " 'thumbnail': 'https://yt3.ggpht.com/Lzc9YzCKTkcA1My5A5pbsqaEtOoGc0ncWpCJiOQs2-0win3Tjf5XxmDFEYUiVM9jOTuhMjGs=s68-c-k-c0x00ffffff-no-rj',\n", + " 'verified': True},\n", + " 'description': 'Words are great, but if we want to use '\n", + " 'them as input to a neural network, we have '\n", + " 'to convert them to numbers. One of the '\n", + " 'most\\xa0...',\n", + " 'extensions': ['CC'],\n", + " 'length': '16:12',\n", + " 'link': 'https://www.youtube.com/watch?v=viZrOnJclY0',\n", + " 'position_on_page': 19,\n", + " 'published_date': '2 years ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=viZrOnJclY0',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/viZrOnJclY0/mqdefault_6s.webp?du=3000&sqp=CIr8rsMG&rs=AOn4CLDaQZK30UFxw7REvoIfE5C-SIlbGg',\n", + " 'static': 'https://i.ytimg.com/vi/viZrOnJclY0/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDWNz1QzHi5AIlS4WJd68BIs_slQw'},\n", + " 'title': 'Word Embedding and Word2Vec, Clearly '\n", + " 'Explained!!!',\n", + " 'views': 448110},\n", + " {'channel': {'link': 'https://www.youtube.com/@googlecloudtech',\n", + " 'name': 'Google Cloud Tech',\n", + " 'thumbnail': 'https://yt3.ggpht.com/1a3sAZKZj0xtTNPJCta6_MQ1wWfsjK2URS1S5bL_NJdDcYxLotkg4tWmlC1lz-K8kULyz2zHlUA=s68-c-k-c0x00ffffff-no-rj',\n", + " 'verified': True},\n", + " 'description': 'This video explores the world of '\n", + " 'embeddings in machine learning, explaining '\n", + " 'how they transform complex data like text '\n", + " 'and\\xa0...',\n", + " 'extensions': ['CC'],\n", + " 'length': '4:48',\n", + " 'link': 'https://www.youtube.com/watch?v=vlcQV4j2kTo',\n", + " 'position_on_page': 20,\n", + " 'published_date': '8 months ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=vlcQV4j2kTo',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/vlcQV4j2kTo/mqdefault_6s.webp?du=3000&sqp=CLj2rsMG&rs=AOn4CLCuNTvDdbsk-dEWo3STS6QoxVu0IA',\n", + " 'static': 'https://i.ytimg.com/vi/vlcQV4j2kTo/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDTVbKRBc4Vj0nBSBGWslzZDyB0hw'},\n", + " 'title': 'What are text embeddings?',\n", + " 'views': 14085},\n", + " {'channel': {'link': 'https://www.youtube.com/@Fireship',\n", + " 'name': 'Fireship',\n", + " 'thumbnail': 'https://yt3.ggpht.com/ytc/AIdro_mKzklyPPhghBJQH5H3HpZ108YcE618DBRLAvRUD1AjKNw=s68-c-k-c0x00ffffff-no-rj',\n", + " 'verified': True},\n", + " 'description': 'Vector databases are rapidly growing in '\n", + " 'popularity as a way to add long-term '\n", + " 'memory to LLMs like GPT-4, LLaMDA, and '\n", + " 'LLaMA.',\n", + " 'extensions': ['4K'],\n", + " 'length': '3:22',\n", + " 'link': 'https://www.youtube.com/watch?v=klTvEwg3oJ4',\n", + " 'position_on_page': 21,\n", + " 'published_date': '2 years ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=klTvEwg3oJ4',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/klTvEwg3oJ4/mqdefault_6s.webp?du=3000&sqp=CPjjrsMG&rs=AOn4CLDEVHYEPFapLG-WvBhhsKjhEp4bjA',\n", + " 'static': 'https://i.ytimg.com/vi/klTvEwg3oJ4/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCReOqLo_KnE3ATMnLgblZFLsUmJw'},\n", + " 'title': 'Vector databases are so hot right now. WTF are '\n", + " 'they?',\n", + " 'views': 1057587},\n", + " {'channel': {'link': 'https://www.youtube.com/@NilsReimersTalks',\n", + " 'name': 'Nils Reimers',\n", + " 'thumbnail': 'https://yt3.ggpht.com/J9-ds_cv0u_-urwe2uhRZhyRiwaKsxyJ87A0yVeeSOrh68-CoAFiab1jy4yQxsnx7_MctY43=s68-c-k-c0x00ffffff-no-rj'},\n", + " 'description': 'Detailed talk about how to train '\n", + " 'state-of-the-art sentence embedding '\n", + " 'models. The talks does a deep-dive on '\n", + " 'the\\xa0...',\n", + " 'length': '43:43',\n", + " 'link': 'https://www.youtube.com/watch?v=RHXZKUr8qOY',\n", + " 'position_on_page': 22,\n", + " 'published_date': '4 years ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=RHXZKUr8qOY',\n", + " 'thumbnail': {'static': 'https://i.ytimg.com/vi/RHXZKUr8qOY/hqdefault.jpg?sqp=-oaymwEcCOADEI4CSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDD1PFXLUDzciPaqjMO7coRqCyW7A'},\n", + " 'title': 'Training State-of-the-Art Sentence Embedding '\n", + " 'Models',\n", + " 'views': 8654},\n", + " {'channel': {'link': 'https://www.youtube.com/@TimescaleDB',\n", + " 'name': 'Timescale',\n", + " 'thumbnail': 'https://yt3.ggpht.com/eiu9-Tp9KTzpzJuH-tsSVHhNzIYIz1cP5Wn96VXiitsXD1SvtZzpRhDJgRP7hpNl4LPJVbyz7g=s68-c-k-c0x00ffffff-no-rj'},\n", + " 'description': \"In today's video, Jacky Liang, developer \"\n", + " 'advocate at Timescale, deep dives into the '\n", + " 'complex world of embedding models for '\n", + " 'AI\\xa0...',\n", + " 'extensions': ['4K'],\n", + " 'length': '10:48',\n", + " 'link': 'https://www.youtube.com/watch?v=VgSFW3055ls',\n", + " 'position_on_page': 23,\n", + " 'published_date': '6 months ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=VgSFW3055ls',\n", + " 'thumbnail': {'rich': 'https://i.ytimg.com/an_webp/VgSFW3055ls/mqdefault_6s.webp?du=3000&sqp=CLLcrsMG&rs=AOn4CLD5b6XdWahh92LQ3a_F7AFlTGGPUw',\n", + " 'static': 'https://i.ytimg.com/vi/VgSFW3055ls/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBDQbHOZip8ejGHQC8_RivcQ-AEzg'},\n", + " 'title': 'OpenAI vs. Open-source Embedding Model Showdown',\n", + " 'views': 2982},\n", + " {'channel': {'link': 'https://www.youtube.com/@HuggingFace',\n", + " 'name': 'HuggingFace',\n", + " 'thumbnail': 'https://yt3.ggpht.com/ytc/AIdro_mNrquVCKsXMFEQe0YqYV7cTkQ6TsEIVq-kWruQJoOH7g=s68-c-k-c0x00ffffff-no-rj'},\n", + " 'description': 'Learn how Transformer models can be used '\n", + " 'to represent documents and queries as '\n", + " 'vectors called embeddings. In this video, '\n", + " 'we\\xa0...',\n", + " 'extensions': ['CC'],\n", + " 'length': '3:30',\n", + " 'link': 'https://www.youtube.com/watch?v=OATCgQtNX2o',\n", + " 'position_on_page': 24,\n", + " 'published_date': '3 years ago',\n", + " 'serpapi_link': 'https://serpapi.com/search.json?engine=youtube_video&v=OATCgQtNX2o',\n", + " 'thumbnail': {'static': 'https://i.ytimg.com/vi/OATCgQtNX2o/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLD-cWESrZMxB8Wmsz5tnuOXKTHfUw'},\n", + " 'title': 'Text embeddings & semantic search',\n", + " 'views': 44642}]}\n" + ] + } + ], + "source": [ + "from pprint import pprint\n", + "pprint(results)\n", + "# print(results)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f69c5a7b", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/simple_ai_agents/Career_Guidence/requirements.txt b/simple_ai_agents/Career_Guidence/requirements.txt new file mode 100644 index 00000000..460b0056 --- /dev/null +++ b/simple_ai_agents/Career_Guidence/requirements.txt @@ -0,0 +1,13 @@ +streamlit +pandas +numpy +plotly +langchain +langchain-openai +langchain-community +crewai +langchain-groq +langchain-google-genai +python-dotenv +faiss-cpu +google-search-results \ No newline at end of file diff --git a/simple_ai_agents/Career_Guidence/temp.py b/simple_ai_agents/Career_Guidence/temp.py new file mode 100644 index 00000000..6422769e --- /dev/null +++ b/simple_ai_agents/Career_Guidence/temp.py @@ -0,0 +1,427 @@ +import streamlit as st +from langchain_groq import ChatGroq +from langchain.chains import LLMChain +from langchain.prompts import PromptTemplate +from langchain.agents import load_tools, initialize_agent, AgentType +from langchain_community.utilities import SerpAPIWrapper +from datetime import datetime +import os +import time +import uuid + +# Streamlit page configuration +st.set_page_config(page_title="Career Guidance System", layout="wide") + +class CareerGuidanceSystem: + def __init__(self, groq_api_key=None, serpapi_key=None): + """Initialize the career guidance system""" + self.groq_api_key = groq_api_key + self.serpapi_key = serpapi_key + + # Set environment variable for Groq API key + if groq_api_key: + os.environ["GROQ_API_KEY"] = groq_api_key + + # Set environment variable for SerpAPI key + if serpapi_key: + os.environ["SERPAPI_API_KEY"] = serpapi_key + + # Initialize the language model + if groq_api_key: + self.llm = ChatGroq( + model_name="gemma2-9b-it", + groq_api_key=groq_api_key + ) + + # Initialize search tools if SerpAPI key is provided + if serpapi_key: + self.search = SerpAPIWrapper(serpapi_api_key=serpapi_key) + self.tools = load_tools(["serpapi"], llm=self.llm) + self.search_agent = initialize_agent( + self.tools, + self.llm, + agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, + verbose=False, + handle_parsing_errors=True, + max_iterations=6 + ) + else: + self.search = None + self.search_agent = None + else: + self.llm = None + self.search = None + self.search_agent = None + + # Career data storage with caching + self.career_data = {} + self.search_cache = {} + self.user_profile = {} + + # Fallback career options + self.fallback_career_options = { + "Technology": [ + "Software Engineering", "Data Science", "Cybersecurity", + "AI/ML Engineering", "DevOps", "Cloud Architecture", + "Mobile Development" + ], + "Healthcare": [ + "Medicine", "Nursing", "Pharmacy", "Biomedical Engineering", + "Healthcare Administration", "Physical Therapy" + ], + "Business": [ + "Finance", "Marketing", "Management", "Human Resources", + "Entrepreneurship", "Business Analysis" + ], + "Creative": [ + "Graphic Design", "UX/UI Design", "Content Creation", + "Digital Marketing", "Animation", "Film Production" + ] + } + + def search_with_cache(self, query, cache_key, ttl_hours=24, max_retries=3): + """Perform a search with caching""" + if cache_key in self.search_cache: + timestamp = self.search_cache[cache_key]['timestamp'] + age_hours = (datetime.now() - timestamp).total_seconds() / 3600 + if age_hours < ttl_hours: + return self.search_cache[cache_key]['data'] + + if self.search_agent: + retry_count = 0 + last_error = None + + while retry_count < max_retries: + try: + result = self.search_agent.run(query) + self.search_cache[cache_key] = { + 'data': result, + 'timestamp': datetime.now() + } + time.sleep(1) + return result + except Exception as e: + last_error = str(e) + retry_count += 1 + time.sleep(2) + + try: + prompt = PromptTemplate( + input_variables=["query"], + template=""" + Please provide information on the following: {query} + Structure your response clearly with headings and bullet points. + """ + ) + chain = LLMChain(llm=self.llm, prompt=prompt) + result = chain.run(query=query) + self.search_cache[cache_key] = { + 'data': result, + 'timestamp': datetime.now() + } + return result + except: + return f"Search failed after {max_retries} attempts. Last error: {last_error}" + else: + return "Search unavailable. Please provide a SerpAPI key." + + def format_search_results(self, results, title): + """Format search results into markdown""" + formatted = f"# {title}\n\n" + if isinstance(results, str): + lines = results.split('\n') + clean_lines = [] + for line in lines: + if "I'll search for" not in line and "I need to search for" not in line: + if not line.startswith("Action:") and not line.startswith("Observation:"): + clean_lines.append(line) + formatted += "\n".join(clean_lines) + else: + formatted += "No results available." + return formatted + + def get_career_options(self): + """Return all available career categories and options""" + return self.fallback_career_options + + def comprehensive_career_analysis(self, career_name, user_profile=None): + """Run a comprehensive analysis of a career""" + if career_name in self.career_data: + return self.career_data[career_name] + + if self.search_agent and self.serpapi_key: + overview_query = ( + f"Create a detailed overview of the {career_name} career with the following structure:\n" + f"1. Role Overview: What do {career_name} professionals do?\n" + f"2. Key Responsibilities: List the main tasks and responsibilities\n" + f"3. Required Technical Skills: List the technical skills needed\n" + f"4. Required Soft Skills: List the soft skills needed\n" + f"5. Educational Background: What education is typically required?" + ) + overview_result = self.search_with_cache(overview_query, f"{career_name}_overview") + research = self.format_search_results(overview_result, f"{career_name} Career Analysis") + + market_query = ( + f"Analyze the job market for {career_name} professionals with the following structure:\n" + f"1. Job Growth Projections: How is job growth trending?\n" + f"2. Salary Ranges: What are salary ranges by experience level?\n" + f"3. Top Industries: Which industries hire the most {career_name} professionals?\n" + f"4. Geographic Hotspots: Which locations have the most opportunities?\n" + f"5. Emerging Trends: What new trends are affecting this field?" + ) + market_result = self.search_with_cache(market_query, f"{career_name}_market") + market_analysis = self.format_search_results(market_result, f"{career_name} Market Analysis") + + experience_level = "beginner" + if user_profile and "experience" in user_profile: + exp = user_profile["experience"] + if "5-10" in exp or "10+" in exp: + experience_level = "advanced" + elif "3-5" in exp: + experience_level = "intermediate" + + roadmap_query = ( + f"Create a learning roadmap for becoming a {career_name} professional at the {experience_level} level with this structure:\n" + f"1. Skills to Develop: What skills should they focus on?\n" + f"2. Education Requirements: What degrees or certifications are needed?\n" + f"3. Recommended Courses: What specific courses or training programs work best?\n" + f"4. Learning Resources: What books, websites, or tools are helpful?\n" + f"5. Timeline: Provide a realistic timeline for skill acquisition" + ) + roadmap_result = self.search_with_cache(roadmap_query, f"{career_name}_roadmap_{experience_level}") + learning_roadmap = self.format_search_results(roadmap_result, f"{career_name} Learning Roadmap") + + insights_query = ( + f"Provide industry insights for {career_name} professionals with this structure:\n" + f"1. Workplace Culture: What is the typical work environment like?\n" + f"2. Day-to-Day Activities: What does a typical workday include?\n" + f"3. Career Progression: What career advancement paths exist?\n" + f"4. Work-Life Balance: How is the work-life balance in this field?\n" + f"5. Success Strategies: What tips help professionals succeed in this field?" + ) + insights_result = self.search_with_cache(insights_query, f"{career_name}_insights") + industry_insights = self.format_search_results(insights_result, f"{career_name} Industry Insights") + + results1 = { + "career_name": career_name, + "research": research, + "market_analysis": market_analysis, + "learning_roadmap": learning_roadmap, + "industry_insights": industry_insights, + "timestamp": datetime.now().isoformat() + } + self.career_data[career_name] = results1 + return results1 + + elif self.llm: + career_prompt = PromptTemplate( + input_variables=["career"], + template=""" + Provide a comprehensive analysis of the {career} career path. + Include role overview, key responsibilities, required technical and soft skills, + and educational background or alternative paths into the field. + Format the response in markdown with clear headings and bullet points. + """ + ) + market_prompt = PromptTemplate( + input_variables=["career"], + template=""" + Analyze the current job market for {career} professionals. + Include information on job growth projections, salary ranges by experience level, + top industries hiring, geographic hotspots, and emerging trends affecting the field. + Format the response in markdown with clear headings. + """ + ) + roadmap_prompt = PromptTemplate( + input_variables=["career", "experience_level"], + template=""" + Create a detailed learning roadmap for someone pursuing a {career} career path. + The person is at a {experience_level} level. + Include essential skills to develop, specific education requirements, recommended courses and resources, + and a timeline for skill acquisition. Structure the response with clear sections and markdown formatting. + """ + ) + insights_prompt = PromptTemplate( + input_variables=["career"], + template=""" + Provide detailed insider insights about working as a {career} professional. + Include information on workplace culture, day-to-day activities, career progression paths, + work-life balance considerations, and success strategies. + Format the response in markdown with clear headings. + """ + ) + career_chain = LLMChain(llm=self.llm, prompt=career_prompt) + market_chain = LLMChain(llm=self.llm, prompt=market_prompt) + roadmap_chain = LLMChain(llm=self.llm, prompt=roadmap_prompt) + insights_chain = LLMChain(llm=self.llm, prompt=insights_prompt) + + experience_level = "beginner" + if user_profile and "experience" in user_profile: + exp = user_profile["experience"] + if "5-10" in exp or "10+" in exp: + experience_level = "advanced" + elif "3-5" in exp: + experience_level = "intermediate" + + research = career_chain.run(career=career_name) + market_analysis = market_chain.run(career=career_name) + learning_roadmap = roadmap_chain.run(career=career_name, experience_level=experience_level) + industry_insights = insights_chain.run(career=career_name) + + results2 = { + "career_name": career_name, + "research": research, + "market_analysis": market_analysis, + "learning_roadmap": learning_roadmap, + "industry_insights": industry_insights, + "timestamp": datetime.now().isoformat() + } + self.career_data[career_name] = results2 + return results2 + + return { + "career_name": career_name, + "research": f"Career analysis for {career_name} unavailable. Please provide API keys.", + "market_analysis": "Market analysis unavailable. Please provide API keys.", + "learning_roadmap": "Learning roadmap unavailable. Please provide API keys.", + "industry_insights": "Industry insights unavailable. Please provide API keys." + } + + def chat_with_assistant(self, question, career_data=None): + """Engage in conversation about career questions""" + if not self.llm: + return "Career assistant is not available. Please provide a Groq API key." + + try: + context = "" + if career_data and isinstance(career_data, dict): + career_name = career_data.get("career_name", "the selected career") + context = f"The user has selected the {career_name} career path. " + + if any(kw in question.lower() for kw in ["skill", "learn", "study", "education", "degree"]): + context += f"Here's information about the career: {career_data.get('research', '')} " + context += f"Here's learning roadmap information: {career_data.get('learning_roadmap', '')} " + + if any(kw in question.lower() for kw in ["market", "job", "salary", "pay", "demand", "trend"]): + context += f"Here's market analysis information: {career_data.get('market_analysis', '')} " + + if any(kw in question.lower() for kw in ["work", "day", "culture", "balance", "advance"]): + context += f"Here's industry insights information: {career_data.get('industry_insights', '')} " + + prompt = PromptTemplate( + input_variables=["context", "question"], + template=""" + You are a career guidance assistant helping a user with their career questions. + + Context about the user's selected career: + {context} + + User question: {question} + + Provide a helpful, informative response that directly addresses the user's question. + Be conversational but concise. Include specific advice or information when possible. + Format your response in markdown with bullet points and headings where appropriate. + """ + ) + + chain = LLMChain(llm=self.llm, prompt=prompt) + response = chain.run(context=context, question=question) + return response + + except Exception as e: + return f"I encountered an error while processing your question: {str(e)}" + +# Initialize session state +# if 'career_system' not @staticmethod +# ...existing code... +# Initialize session state +if 'career_system' not in st.session_state: + st.session_state.career_system = None + +def generate_uuid(): + return str(uuid.uuid4()) + +# Initialize session state +if 'career_system' not in st.session_state: + st.session_state.career_system = None +if 'career_data' not in st.session_state: + st.session_state.career_data = None +if 'chat_history' not in st.session_state: + st.session_state.chat_history = [] + +# Main Streamlit UI +def main(): + st.title("Career Guidance System") + st.markdown("Explore career paths, get market insights, and create personalized learning roadmaps.") + + # Sidebar for API keys and user profile + with st.sidebar: + st.header("Configuration") + groq_api_key = st.text_input("Groq API Key", type="password") + serpapi_key = st.text_input("SerpAPI Key", type="password") + + if st.button("Initialize System"): + st.session_state.career_system = CareerGuidanceSystem(groq_api_key, serpapi_key) + st.success("System initialized!") + + st.header("User Profile") + education = st.selectbox("Education Level", ["High School", "Bachelor's", "Master's", "PhD", "Other"]) + experience = st.selectbox("Experience Level", ["0-2 years", "3-5 years", "5-10 years", "10+ years"]) + skills = st.text_area("Skills (comma-separated)") + if st.button("Save Profile"): + st.session_state.career_system.user_profile = { + "education": education, + "experience": experience, + "skills": {skill.strip(): 5 for skill in skills.split(",") if skill.strip()} + } + st.success("Profile saved!") + + # Main content + if st.session_state.career_system: + career_system = st.session_state.career_system + + # Career selection + st.header("Select a Career") + career_categories = career_system.get_career_options() + category = st.selectbox("Career Category", list(career_categories.keys())) + career = st.selectbox("Specific Career", career_categories[category]) + + if st.button("Analyze Career"): + with st.spinner("Analyzing career..."): + st.session_state.career_data = career_system.comprehensive_career_analysis( + career, career_system.user_profile + ) + + # Display career analysis + if st.session_state.career_data: + tabs = st.tabs(["Overview", "Market Analysis", "Learning Roadmap", "Industry Insights"]) + + with tabs[0]: + st.markdown(st.session_state.career_data["research"]) + + with tabs[1]: + st.markdown(st.session_state.career_data["market_analysis"]) + + with tabs[2]: + st.markdown(st.session_state.career_data["learning_roadmap"]) + + with tabs[3]: + st.markdown(st.session_state.career_data["industry_insights"]) + + # Chat interface + st.header("Career Advisor Chat") + user_query = st.text_input("Ask a career question:") + if st.button("Submit Question"): + if user_query: + response = career_system.chat_with_assistant(user_query, st.session_state.career_data) + st.session_state.chat_history.append({"question": user_query, "response": response}) + + # Display chat history + for chat in st.session_state.chat_history: + st.markdown(f"**Q: {chat['question']}**") + st.markdown(chat['response']) + else: + st.warning("Please initialize the system with API keys.") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/simple_ai_agents/Recruitify/.gitignore b/simple_ai_agents/Recruitify/.gitignore new file mode 100644 index 00000000..65c966d7 --- /dev/null +++ b/simple_ai_agents/Recruitify/.gitignore @@ -0,0 +1,7 @@ +.env +./env +.\env +.__pycache__ +./__pycache__ +.venv +./venv \ No newline at end of file diff --git a/simple_ai_agents/Recruitify/README.md b/simple_ai_agents/Recruitify/README.md new file mode 100644 index 00000000..346ce34b --- /dev/null +++ b/simple_ai_agents/Recruitify/README.md @@ -0,0 +1,179 @@ + +# πŸš€ Recruitify + +An intelligent recruitment and interview assistant built with **Streamlit**, **LangChain**, **Google Gemini API**, **Groq API**, and **Deepgram API**. +This app helps recruiters, candidates, and career coaches by: + +* πŸ“„ **Analyzing Resumes** against job requirements or custom job descriptions +* ❓ **Q\&A on Resumes** using Retrieval-Augmented Generation (RAG) +* 🎯 **Generating Personalized Interview Questions** +* πŸ› οΈ **Providing Resume Improvement Suggestions** with before/after examples +* πŸ“ **Creating Optimized Improved Resumes** tailored for a target role +* πŸŽ™οΈ **Simulating Real-Time Interviews** with voice input, AI-generated feedback, and performance scoring + +--- + +## ✨ Features + +### πŸ” Resume Requirement Analyst + +* Upload a **resume (PDF)** and analyze it against: + + * Predefined **role-based skill sets** (e.g., Data Scientist, AI/ML Engineer, DevOps Engineer) + * Or a **custom job description (PDF/TXT)** +* Get: + + * **Overall Score & Shortlisting Status** + * **Strengths & Weaknesses** + * **Detailed Skill Gap Analysis** + * πŸ“Š Downloadable **Resume Analysis Report** + +### πŸ€– Resume Q\&A + +* Ask **any question** about the uploaded resume +* Example: *β€œWhat is the candidate’s most recent role?”* or *β€œDoes the candidate have cloud experience?”* + +### πŸ“ Interview Question Generator + +* Generate **personalized interview questions** based on resume & skills +* Supports multiple question types: *Basic, Technical, Scenario, Coding, Behavioral* +* Difficulty levels: *Easy, Medium, Hard* +* Download generated questions in **Markdown format** + +### πŸ› οΈ Resume Improvement + +* AI-powered suggestions for improvement in: + + * **Content** + * **Skills Highlighting** + * **Experience & Projects** + * **Format & Structure** +* Includes **Before vs. After** examples for better clarity + +### πŸ“‘ Improved Resume Generator + +* Automatically rewrite and optimize resumes for: + + * A **specific target role** + * Or a **given job description** +* Highlights missing & key skills + +### πŸŽ™οΈ AI Interview Assistant + +* Upload **interview question files (Markdown/TXT)** +* **Voice-based interview simulation**: + + * Questions are read aloud using **Groq TTS** + * Candidate answers via **microphone input** + * Transcribed using **Deepgram STT** +* AI evaluates responses with: + + * βœ… Strengths & Weaknesses + * πŸ“Œ Actionable Improvement Suggestions + * πŸ“Š Confidence & Accuracy Scoring (1–10) +* πŸ“‰ Performance Report with **visual graphs (Plotly)** + +--- + +## πŸ› οΈ Tech Stack + +* **Frontend/UI**: [Streamlit](https://streamlit.io/) +* **AI & NLP**: [LangChain](https://www.langchain.com/), [Google Gemini API](https://ai.google.dev/) +* **LLMs**: Groq (LLaMA 3.3-70B), Google Gemini +* **Vector Database**: FAISS +* **Speech-to-Text**: [Deepgram](https://deepgram.com/) +* **Text-to-Speech**: Groq Audio API +* **Charts & Visualization**: Plotly, Matplotlib + +--- + +## πŸš€ Installation + +### 1️⃣ Clone the Repository + +```bash +git clone https://github.com/yourusername/recruitment-agent.git +cd recruitment-agent +``` + +### 2️⃣ Create a Virtual Environment + +```bash +python -m venv venv +source venv/bin/activate # On Windows: venv\Scripts\activate +``` + +### 3️⃣ Install Dependencies + +```bash +pip install -r requirements.txt +``` + +### 4️⃣ Set Environment Variables + +Create a `.env` file in the project root: + +```ini +GOOGLE_API_KEY=your_google_api_key +GROQ_API_KEY=your_groq_api_key +DEEPGRAM_API_KEY=your_deepgram_api_key +``` + +--- + +## ▢️ Usage + +Run the app with: + +```bash +streamlit run app.py +``` + +### Modes: + +* **Requirement Analyst** β†’ Resume analysis, Q\&A, improvements, and interview question generation +* **Interview Assistant** β†’ Voice-enabled AI-powered interview simulation + +--- + +## πŸ“‚ Project Structure + +``` +. +β”œβ”€β”€ app.py # Main Streamlit app entry point +β”œβ”€β”€ agents.py # Resume analysis agent (LLM, RAG, scoring, improvements) +β”œβ”€β”€ interview.py # AI-powered voice interview assistant +β”œβ”€β”€ ui.py # Streamlit UI components and styling +β”œβ”€β”€ requirements.txt # Dependencies +└── README.md # Project documentation +``` + +--- + +## πŸ“Š Example Screenshots + +image +image +image + + +--- + +## 🀝 Contributing + +Contributions are welcome! + +* Fork the repo +* Create a feature branch +* Commit your changes +* Open a Pull Request + +--- + +## πŸ“œ License + +This project is licensed under the **MIT License**. + +--- + +Would you like me to also **generate a `requirements.txt`** file for you based on the imports in all four files? That way your repo will be fully ready to run. diff --git a/simple_ai_agents/Recruitify/agents.py b/simple_ai_agents/Recruitify/agents.py new file mode 100644 index 00000000..ec3f2557 --- /dev/null +++ b/simple_ai_agents/Recruitify/agents.py @@ -0,0 +1,675 @@ +import re +import PyPDF2 +import io +from langchain_community.vectorstores import FAISS +from langchain.chains import RetrievalQA +from langchain.text_splitter import RecursiveCharacterTextSplitter +from concurrent.futures import ThreadPoolExecutor +import tempfile +import os +import json +from langchain_google_genai import ChatGoogleGenerativeAI,GoogleGenerativeAIEmbeddings +import asyncio + + +try: + asyncio.get_running_loop() +except RuntimeError: + asyncio.set_event_loop(asyncio.new_event_loop()) + + +class ResumeAnalysisAgent: + def __init__(self, api_key, cutoff_score=75): + self.api_key = api_key + self.cutoff_score = cutoff_score + self.resume_text = None + self.rag_vectorstore = None + self.analysis_result = None + self.jd_text = None + self.extracted_skills = None + self.resume_weaknesses = [] + self.resume_strengths = [] + self.improvement_suggestions = {} + + def extract_text_from_pdf(self, pdf_file): + """Extract text from a PDF file""" + try: + if hasattr(pdf_file, 'getvalue'): + pdf_data = pdf_file.getvalue() + pdf_file_like = io.BytesIO(pdf_data) + reader = PyPDF2.PdfReader(pdf_file_like) + else: + reader = PyPDF2.PdfReader(pdf_file) + + text = "" + for page in reader.pages: + text += page.extract_text() + + return text + except Exception as e: + print(f"Error extracting text from PDF: {e}") + return "" + + def extract_text_from_txt(self, txt_file): + """Extract text from a text file""" + try: + if hasattr(txt_file, 'getvalue'): + return txt_file.getvalue().decode('utf-8') + else: + with open(txt_file, 'r', encoding='utf-8') as f: + return f.read() + except Exception as e: + print(f"Error extracting text from text file: {e}") + return "" + + def extract_text_from_file(self, file): + """Extract text from a file (PDF or TXT)""" + if hasattr(file, 'name'): + file_extension = file.name.split('.')[-1].lower() + else: + file_extension = file.split('.')[-1].lower() + + if file_extension == 'pdf': + return self.extract_text_from_pdf(file) + elif file_extension == 'txt': + return self.extract_text_from_txt(file) + else: + print(f"Unsupported file extension: {file_extension}") + return "" + + def create_rag_vector_store(self, text): + """Create a vector store for RAG""" + + text_splitter = RecursiveCharacterTextSplitter( + chunk_size=1000, + chunk_overlap=200, + length_function=len, + ) + chunks = text_splitter.split_text(text) + + embeddings= GoogleGenerativeAIEmbeddings(model="gemini-embedding-001",google_api_key=self.api_key) + vectorstore = FAISS.from_texts(chunks, embeddings) + return vectorstore + + def create_vector_store(self, text): + """Create a simpler vector store for skill analysis""" + embeddings = GoogleGenerativeAIEmbeddings(model="gemini-embedding-001",google_api_key=self.api_key) + vectorstore = FAISS.from_texts([text], embeddings) + return vectorstore + + def analyze_skill(self, qa_chain, skill): + """Analyze a skill in the resume""" + query = f"On a scale of 0-10, how clearly does the candidate mention proficiency in {skill}? Provide a numeric rating first, followed by reasoning." + response = qa_chain.run(query) + match = re.search(r"(\d{1,2})", response) + score = int(match.group(1)) if match else 0 + + + reasoning = response.split('.', 1)[1].strip() if '.' in response and len(response.split('.')) > 1 else "" + + + return skill, min(score, 10), reasoning + + def analyze_resume_weaknesses(self): + """Analyze specific weaknesses in the resume based on missing skills""" + if not self.resume_text or not self.extracted_skills or not self.analysis_result: + return [] + + weaknesses = [] + + for skill in self.analysis_result.get("missing_skills", []): + + llm = ChatGoogleGenerativeAI(model='gemini-2.0-flash',google_api_key=self.api_key) + prompt = f""" + Analyze why the resume is weak in demonstrating proficiency in "{skill}". + + For your analysis, consider: + 1. What's missing from the resume regarding this skill? + 2. How could it be improved with specific examples? + 3. What specific action items would make this skill stand out? + + Resume Content: + {self.resume_text[:3000]}... + + Provide your response in this JSON format: + {{ + "weakness": "A concise description of what's missing or problematic (1-2 sentences)", + "improvement_suggestions": [ + "Specific suggestion 1", + "Specific suggestion 2", + "Specific suggestion 3" + ], + "example_addition": "A specific bullet point that could be added to showcase this skill" + }} + + Return only valid JSON, no other text. + """ + + response = llm.invoke(prompt) + weakness_content = response.content.strip() + + + try: + weakness_data = json.loads(weakness_content) + + weakness_detail = { + "skill": skill, + "score": self.analysis_result.get("skill_scores", {}).get(skill, 0), + "detail": weakness_data.get("weakness", "No specific details provided."), + "suggestions": weakness_data.get("improvement_suggestions", []), + "example": weakness_data.get("example_addition", "") + } + + weaknesses.append(weakness_detail) + + self.improvement_suggestions[skill] = { + "suggestions": weakness_data.get("improvement_suggestions", []), + "example": weakness_data.get("example_addition", "") + } + except json.JSONDecodeError: + + weaknesses.append({ + "skill": skill, + "score": self.analysis_result.get("skill_scores", {}).get(skill, 0), + "detail": weakness_content[:200] # Truncate if it's not proper JSON + }) + + self.resume_weaknesses = weaknesses + return weaknesses + + def extract_skills_from_jd(self, jd_text): + """Extract skills from a job description""" + try: + llm = ChatGoogleGenerativeAI(model='gemini-2.0-flash',google_api_key=self.api_key) + prompt = f""" + Extract a comprehensive list of technical skills, technologies, and competencies required from this job description. + Format the output as a Python list of strings. Only include the list, nothing else. + + Job Description: + {jd_text} + """ + + response = llm.invoke(prompt) + skills_text = response.content + + + match = re.search(r'\[(.*?)\]', skills_text, re.DOTALL) + if match: + skills_text = match.group(0) + + + try: + skills_list = eval(skills_text) + if isinstance(skills_list, list): + return skills_list + except: + pass + + + skills = [] + for line in skills_text.split('\n'): + line = line.strip() + if line.startswith('- ') or line.startswith('* '): + skill = line[2:].strip() + if skill: + skills.append(skill) + elif line.startswith('"') and line.endswith('"'): + skill = line.strip('"') + if skill: + skills.append(skill) + + return skills + except Exception as e: + print(f"Error extracting skills from job description: {e}") + return [] + + def semantic_skill_analysis(self, resume_text, skills): + """Analyze skills semantically""" + vectorstore = self.create_vector_store(resume_text) + retriever = vectorstore.as_retriever() + qa_chain = RetrievalQA.from_chain_type( + llm=ChatGoogleGenerativeAI(model='gemini-2.0-flash',google_api_key=self.api_key), + retriever=retriever, + return_source_documents=False + ) + + skill_scores = {} + skill_reasoning = {} + missing_skills = [] + total_score = 0 + + with ThreadPoolExecutor(max_workers=5) as executor: + results = list(executor.map(lambda skill: self.analyze_skill(qa_chain, skill), skills)) + + for skill, score, reasoning in results: + skill_scores[skill] = score + skill_reasoning[skill] = reasoning + total_score += score + if score <= 5: + missing_skills.append(skill) + + overall_score = int((total_score / (10 * len(skills))) * 100) + selected = overall_score >= self.cutoff_score + + reasoning = "Candidate evaluated based on explicit resume content using semantic similarity and clear numeric scoring." + strengths = [skill for skill, score in skill_scores.items() if score >= 5] + improvement_areas = missing_skills if not selected else [] + + + self.resume_strengths = strengths + + return { + "overall_score": overall_score, + "skill_scores": skill_scores, + "skill_reasoning": skill_reasoning, + "selected": selected, + "reasoning": reasoning, + "missing_skills": missing_skills, + "strengths": strengths, + "improvement_areas": improvement_areas + } + + def analyze_resume(self, resume_file, role_requirements=None, custom_jd=None): + """Analyze a resume against role requirements or a custom JD""" + self.resume_text = self.extract_text_from_file(resume_file) + + + with tempfile.NamedTemporaryFile(delete=False, suffix='.txt', mode='w', encoding='utf-8') as tmp: + tmp.write(self.resume_text) + self.resume_file_path = tmp.name + + self.rag_vectorstore = self.create_rag_vector_store(self.resume_text) + + + if custom_jd: + self.jd_text = self.extract_text_from_file(custom_jd) + self.extracted_skills = self.extract_skills_from_jd(self.jd_text) + + + self.analysis_result = self.semantic_skill_analysis(self.resume_text, self.extracted_skills) + + elif role_requirements: + self.extracted_skills = role_requirements + + + self.analysis_result = self.semantic_skill_analysis(self.resume_text, role_requirements) + + + if self.analysis_result and "missing_skills" in self.analysis_result and self.analysis_result["missing_skills"]: + self.analyze_resume_weaknesses() + + self.analysis_result["detailed_weaknesses"] = self.resume_weaknesses + + return self.analysis_result + + def ask_question(self, question): + """Ask a question about the resume""" + if not self.rag_vectorstore or not self.resume_text: + return "Please analyze a resume first." + + retriever = self.rag_vectorstore.as_retriever( + search_kwargs={"k": 3} + ) + + qa_chain = RetrievalQA.from_chain_type( + llm=ChatGoogleGenerativeAI(model='gemini-2.0-flash',google_api_key=self.api_key), + chain_type="stuff", + retriever=retriever, + return_source_documents=False, + ) + + response = qa_chain.run(question) + return response + + def generate_interview_questions(self, question_types, difficulty, num_questions): + """Generate interview questions based on the resume""" + if not self.resume_text or not self.extracted_skills: + return [] + + try: + llm = ChatGoogleGenerativeAI(model='gemini-2.0-flash',google_api_key=self.api_key) + + + context = f""" + Resume Content: + {self.resume_text[:2000]}... + + Skills to focus on: {', '.join(self.extracted_skills)} + + Strengths: {', '.join(self.analysis_result.get('strengths', []))} + + Areas for improvement: {', '.join(self.analysis_result.get('missing_skills', []))} + """ + + prompt = f""" + Generate {num_questions} personalized {difficulty.lower()} level interview questions for this candidate + based on their resume and skills. Include only the following question types: {', '.join(question_types)}. + + For each question: + 1. Clearly label the question type + 2. Make the question specific to their background and skills + 3. For coding questions, include a clear problem statement + + {context} + + Format the response as a list of tuples with the question type and the question itself. + Each tuple should be in the format: ("Question Type", "Full Question Text") + """ + + response = llm.invoke(prompt) + questions_text = response.content + + + questions = [] + pattern = r'[("]([^"]+)[",)\s]+[(",\s]+([^"]+)[")\s]+' + matches = re.findall(pattern, questions_text, re.DOTALL) + + for match in matches: + if len(match) >= 2: + question_type = match[0].strip() + question = match[1].strip() + + + for requested_type in question_types: + if requested_type.lower() in question_type.lower(): + questions.append((requested_type, question)) + break + + + if not questions: + lines = questions_text.split('\n') + current_type = None + current_question = "" + + for line in lines: + line = line.strip() + if any(t.lower() in line.lower() for t in question_types) and not current_question: + current_type = next((t for t in question_types if t.lower() in line.lower()), None) + if ":" in line: + current_question = line.split(":", 1)[1].strip() + elif current_type and line: + current_question += " " + line + elif current_type and current_question: + questions.append((current_type, current_question)) + current_type = None + current_question = "" + + questions = questions[:num_questions] + + return questions + + except Exception as e: + print(f"Error generating interview questions: {e}") + return [] + + def improve_resume(self, improvement_areas, target_role=""): + """Generate suggestions to improve the resume""" + if not self.resume_text: + return {} + + try: + + improvements = {} + + + for area in improvement_areas: + + if area == "Skills Highlighting" and self.resume_weaknesses: + skill_improvements = { + "description": "Your resume needs to better highlight key skills that are important for the role.", + "specific": [] + } + + before_after_examples = {} + + for weakness in self.resume_weaknesses: + skill_name = weakness.get("skill", "") + if "suggestions" in weakness and weakness["suggestions"]: + for suggestion in weakness["suggestions"]: + skill_improvements["specific"].append(f"**{skill_name}**: {suggestion}") + + if "example" in weakness and weakness["example"]: + + resume_chunks = self.resume_text.split('\n\n') + relevant_chunk = "" + + + for chunk in resume_chunks: + if skill_name.lower() in chunk.lower() or "experience" in chunk.lower(): + relevant_chunk = chunk + break + + if relevant_chunk: + before_after_examples = { + "before": relevant_chunk.strip(), + "after": relevant_chunk.strip() + "\nβ€’ " + weakness["example"] + } + + if before_after_examples: + skill_improvements["before_after"] = before_after_examples + + improvements["Skills Highlighting"] = skill_improvements + + remaining_areas = [area for area in improvement_areas if area not in improvements] + + if remaining_areas: + llm = ChatGoogleGenerativeAI(model='gemini-2.0-flash',google_api_key=self.api_key) + + # Create a context with resume analysis and weaknesses + weaknesses_text = "" + if self.resume_weaknesses: + weaknesses_text = "Resume Weaknesses:\n" + for i, weakness in enumerate(self.resume_weaknesses): + weaknesses_text += f"{i+1}. {weakness['skill']}: {weakness['detail']}\n" + if "suggestions" in weakness: + for j, sugg in enumerate(weakness["suggestions"]): + weaknesses_text += f" - {sugg}\n" + + context = f""" + Resume Content: + {self.resume_text} + + Skills to focus on: {', '.join(self.extracted_skills)} + + Strengths: {', '.join(self.analysis_result.get('strengths', []))} + + Areas for improvement: {', '.join(self.analysis_result.get('missing_skills', []))} + + {weaknesses_text} + + Target role: {target_role if target_role else "Not specified"} + """ + + prompt = f""" + Provide detailed suggestions to improve this resume in the following areas: {', '.join(remaining_areas)}. + + {context} + + For each improvement area, provide: + 1. A general description of what needs improvement + 2. 3-5 specific actionable suggestions + 3. Where relevant, provide a before/after example + + Format the response as a JSON object with improvement areas as keys, each containing: + - "description": general description + - "specific": list of specific suggestions + - "before_after": (where applicable) a dict with "before" and "after" examples + + Only include the requested improvement areas that aren't already covered. + Focus particularly on addressing the resume weaknesses identified. + """ + + response = llm.invoke(prompt) + + # Try to parse JSON from the response + ai_improvements = {} + + # Extract from markdown code blocks if present + json_match = re.search(r'```(?:json)?\s*([\s\S]+?)\s*```', response.content) + if json_match: + try: + ai_improvements = json.loads(json_match.group(1)) + # Merge with existing improvements + improvements.update(ai_improvements) + except json.JSONDecodeError: + pass + + # If JSON parsing failed, create structured output manually + if not ai_improvements: + sections = response.content.split("##") + + for section in sections: + if not section.strip(): + continue + + lines = section.strip().split("\n") + area = None + + for line in lines: + if not area and line.strip(): + area = line.strip() + improvements[area] = { + "description": "", + "specific": [] + } + elif area and "specific" in improvements[area]: + if line.strip().startswith("- "): + improvements[area]["specific"].append(line.strip()[2:]) + elif not improvements[area]["description"]: + improvements[area]["description"] += line.strip() + + # Ensure all requested areas are included + for area in improvement_areas: + if area not in improvements: + improvements[area] = { + "description": f"Improvements needed in {area}", + "specific": ["Review and enhance this section"] + } + + return improvements + + except Exception as e: + print(f"Error generating resume improvements: {e}") + return {area: {"description": "Error generating suggestions", "specific": []} for area in improvement_areas} + + def get_improved_resume(self, target_role="", highlight_skills=""): + """Generate an improved version of the resume optimized for the job description""" + if not self.resume_text: + return "Please upload and analyze a resume first." + + try: + # Parse highlight skills if provided + skills_to_highlight = [] + if highlight_skills: + + if len(highlight_skills) > 100: + self.jd_text = highlight_skills + try: + parsed_skills = self.extract_skills_from_jd(highlight_skills) + if parsed_skills: + skills_to_highlight = parsed_skills + else: + + skills_to_highlight = [s.strip() for s in highlight_skills.split(",") if s.strip()] + except: + + skills_to_highlight = [s.strip() for s in highlight_skills.split(",") if s.strip()] + else: + skills_to_highlight = [s.strip() for s in highlight_skills.split(",") if s.strip()] + + if not skills_to_highlight and self.analysis_result: + + skills_to_highlight = self.analysis_result.get('missing_skills', []) + + skills_to_highlight.extend([ + skill for skill in self.analysis_result.get('strengths', []) + if skill not in skills_to_highlight + ]) + + if self.extracted_skills: + skills_to_highlight.extend([ + skill for skill in self.extracted_skills + if skill not in skills_to_highlight + ]) + + + weakness_context = "" + improvement_examples = "" + + if self.resume_weaknesses: + weakness_context = "Address these specific weaknesses:\n" + + for weakness in self.resume_weaknesses: + skill_name = weakness.get('skill', '') + weakness_context += f"- {skill_name}: {weakness.get('detail', '')}\n" + + + if 'suggestions' in weakness and weakness['suggestions']: + weakness_context += " Suggested improvements:\n" + for suggestion in weakness['suggestions']: + weakness_context += f" * {suggestion}\n" + + if 'example' in weakness and weakness['example']: + improvement_examples += f"For {skill_name}: {weakness['example']}\n\n" + + + llm = ChatGoogleGenerativeAI(model='gemini-2.0-flash',google_api_key=self.api_key) + + + jd_context = "" + if self.jd_text: + jd_context = f"Job Description:\n{self.jd_text}\n\n" + elif target_role: + jd_context = f"Target Role: {target_role}\n\n" + + prompt = f""" + Rewrite and improve this resume to make it highly optimized for the target job. + + {jd_context} + Original Resume: + {self.resume_text} + + Skills to highlight (in order of priority): {', '.join(skills_to_highlight)} + + {weakness_context} + + Here are specific examples of content to add: + {improvement_examples} + + Please improve the resume by: + 1. Adding strong, quantifiable achievements + 2. Highlighting the specified skills strategically for ATS scanning + 3. Addressing all the weakness areas identified with the specific suggestions provided + 4. Incorporating the example improvements provided above + 5. Structuring information in a clear, professional format + 6. Using industry-standard terminology + 7. Ensuring all relevant experience is properly emphasized + 8. Adding measurable outcomes and achievements + + Return only the improved resume text without any additional explanations. + Format the resume in a modern, clean style with clear section headings. + """ + + response = llm.invoke(prompt) + improved_resume = response.content.strip() + + with tempfile.NamedTemporaryFile(delete=False, suffix='.txt', mode='w', encoding='utf-8') as tmp: + tmp.write(improved_resume) + self.improved_resume_path = tmp.name + + return improved_resume + + except Exception as e: + print(f"Error generating improved resume: {e}") + return "Error generating improved resume. Please try again." + + def cleanup(self): + """Clean up temporary files""" + try: + if hasattr(self, 'resume_file_path') and os.path.exists(self.resume_file_path): + os.unlink(self.resume_file_path) + + if hasattr(self, 'improved_resume_path') and os.path.exists(self.improved_resume_path): + os.unlink(self.improved_resume_path) + except Exception as e: + print(f"Error cleaning up temporary files: {e}") \ No newline at end of file diff --git a/simple_ai_agents/Recruitify/app.py b/simple_ai_agents/Recruitify/app.py new file mode 100644 index 00000000..269e4704 --- /dev/null +++ b/simple_ai_agents/Recruitify/app.py @@ -0,0 +1,257 @@ +import patch_sqlite +import streamlit as st +import asyncio +import nest_asyncio +from interview import Interview +nest_asyncio.apply() + +try: + asyncio.get_running_loop() +except RuntimeError: + asyncio.set_event_loop(asyncio.new_event_loop()) + +# This MUST be the first Streamlit command +st.set_page_config( + page_title="Recruitify", + page_icon="πŸš€", + layout="wide" +) + +import ui +from agents import ResumeAnalysisAgent +import atexit + + + +# # Role requirements dictionary + +ROLE_REQUIREMENTS = { + "AI/ML Engineer": [ + "Python", "Machine Learning", "Deep Learning", "PyTorch", "TensorFlow", + "MLOps", "Scikit-Learn", "NLP", "Computer Vision", "Reinforcement Learning", + "Hugging Face", "Data Engineering", "Feature Engineering", "AutoML" + ], + "Frontend Engineer": [ + "JavaScript", "TypeScript", "React", "Vue", "Angular", + "HTML5", "CSS3", "Next.js", "Svelte", "Tailwind CSS", + "GraphQL", "Redux", "WebAssembly", "Three.js", "Performance Optimization" + ], + "Backend Engineer": [ + "Python or Java or Node.js", "REST APIs", "Cloud Services", + "Kubernetes", "Docker", "GraphQL", "Microservices", "gRPC", + "Spring Boot or Flask or FastAPI", "SQL & NoSQL Databases", + "Redis", "RabbitMQ", "CI/CD" + ], + "Data Engineer": [ + "Python or Scala", "SQL", "Apache Spark", "Hadoop", "Kafka", + "ETL Pipelines", "Airflow", "BigQuery", "Redshift", "Data Warehousing", + "Snowflake", "Azure Data Factory", "GCP", "AWS Glue", "DBT" + ], + "DevOps Engineer": [ + "Kubernetes", "Docker", "Terraform", "CI/CD", "AWS or Azure or GCP", + "Jenkins", "Ansible", "Prometheus", "Grafana", "Helm", + "Linux Administration", "Networking", "Site Reliability Engineering (SRE)" + ], + "Full Stack Developer": [ + "JavaScript or TypeScript", "React", "Node.js", "Express", + "MongoDB", "SQL", "RESTful APIs", "Git", "CI/CD", + "Cloud Services", "Responsive Design", "Authentication & Authorization" + ], + "Product Manager": [ + "Product Strategy", "User Research", "Agile Methodologies", "Roadmapping", + "Market Analysis", "Stakeholder Management", "Data Analysis", "User Stories", + "Product Lifecycle", "A/B Testing", "KPI Definition", "Prioritization", + "Competitive Analysis", "Customer Journey Mapping" + ], + "Data Scientist": [ + "Python or R", "SQL", "Machine Learning", "Statistics", + "Data Visualization", "Pandas", "NumPy", "Scikit-learn", + "Jupyter", "Hypothesis Testing", "Feature Engineering", "Model Evaluation" + ] +} + + +# Initialize session state variables +if 'resume_agent' not in st.session_state: + st.session_state.resume_agent = None + +if 'resume_analyzed' not in st.session_state: + st.session_state.resume_analyzed = False + +if 'analysis_result' not in st.session_state: + st.session_state.analysis_result = None + + +# Important part to check +def setup_agent(config): + """Set up the resume analysis agent with the provided configuration""" + if not config["google_api_key"]: + st.error("⚠️ Please enter your Google API Key in the sidebar.") + return None + + # Initialize or update the agent with the API key + if st.session_state.resume_agent is None: + st.session_state.resume_agent = ResumeAnalysisAgent(api_key=config["google_api_key"]) + else: + st.session_state.resume_agent.api_key = config["google_api_key"] + + return st.session_state.resume_agent + +def analyze_resume(agent, resume_file, role, custom_jd): + """Analyze the resume with the agent""" + if not resume_file: + st.error("⚠️ Please upload a resume.") + return None + + try: + with st.spinner("πŸ” Analyzing resume... This may take a minute."): + if custom_jd: + result = agent.analyze_resume(resume_file, custom_jd=custom_jd) + else: + result = agent.analyze_resume(resume_file, role_requirements=ROLE_REQUIREMENTS[role]) + + st.session_state.resume_analyzed = True + st.session_state.analysis_result = result + return result + except Exception as e: + st.error(f"⚠️ Error analyzing resume: {e}") + return None + +def ask_question(agent, question): + """Ask a question about the resume""" + try: + with st.spinner("Generating response..."): + response = agent.ask_question(question) + return response + except Exception as e: + return f"Error: {e}" + +def generate_interview_questions(agent, question_types, difficulty, num_questions): + """Generate interview questions based on the resume""" + try: + with st.spinner("Generating personalized interview questions..."): + questions = agent.generate_interview_questions(question_types, difficulty, num_questions) + return questions + except Exception as e: + st.error(f"⚠️ Error generating questions: {e}") + return [] + +def improve_resume(agent, improvement_areas, target_role): + """Generate resume improvement suggestions""" + try: + with st.spinner("Analyzing and generating improvements..."): + return agent.improve_resume(improvement_areas, target_role) + except Exception as e: + st.error(f"⚠️ Error generating improvements: {e}") + return {} + +def get_improved_resume(agent, target_role, highlight_skills): + """Get an improved version of the resume""" + try: + with st.spinner("Creating improved resume..."): + return agent.get_improved_resume(target_role, highlight_skills) + except Exception as e: + st.error(f"⚠️ Error creating improved resume: {e}") + return "Error generating improved resume." + +def cleanup(): + """Clean up resources when the app exits""" + if st.session_state.resume_agent: + st.session_state.resume_agent.cleanup() + +# Register cleanup function +atexit.register(cleanup) + +def main(): + # Setup page UI + options=st.sidebar.selectbox( + "Select a option",['Requirement Analyst','Interview Assistant'] + ) + + if options=="Requirement Analyst": + ui.setup_page() + ui.display_header() + + # Set up sidebar and get configuration + config = ui.setup_sidebar() + + # Set up the agent + agent = setup_agent(config) + + # Create tabs for different functionalities + tabs = ui.create_tabs() + # Tab 1: Resume Analysis + with tabs[0]: + role, custom_jd = ui.role_selection_section(ROLE_REQUIREMENTS) + uploaded_resume = ui.resume_upload_section() + + col1, col2, col3 = st.columns([1, 1, 1]) + with col2: + if st.button("πŸ” Analyze Resume", type="primary"): + if agent and uploaded_resume: + # Just store the result, don't display it here + analyze_resume(agent, uploaded_resume, role, custom_jd) + + # Display analysis result (only once) + if st.session_state.analysis_result: + ui.display_analysis_results(st.session_state.analysis_result) + + # Tab 2: Resume Q&A + with tabs[1]: + # We need to ensure the agent and resume are available + if st.session_state.resume_analyzed and st.session_state.resume_agent: + ui.resume_qa_section( + has_resume=True, # Explicitly set to True since we checked above + ask_question_func=lambda q: ask_question(st.session_state.resume_agent, q) + ) + else: + st.warning("Please upload and analyze a resume first in the 'Resume Analysis' tab.") + + # Tab 3: Interview Questions + with tabs[2]: + # We need to ensure the agent and resume are available + if st.session_state.resume_analyzed and st.session_state.resume_agent: + ui.interview_questions_section( + has_resume=True, # Explicitly set to True since we checked above + generate_questions_func=lambda types, diff, num: generate_interview_questions(st.session_state.resume_agent, types, diff, num) + ) + else: + st.warning("Please upload and analyze a resume first in the 'Resume Analysis' tab.") + + # Tab 4: Resume Improvement + with tabs[3]: + if st.session_state.resume_analyzed and st.session_state.resume_agent: + ui.resume_improvement_section( + has_resume=True, + improve_resume_func=lambda areas, role: improve_resume(st.session_state.resume_agent, areas, role) + ) + else: + st.warning("Please upload and analyze a resume first in the 'Resume Analysis' tab.") + + # Tab 5: Improved Resume + with tabs[4]: + if st.session_state.resume_analyzed and st.session_state.resume_agent: + ui.improved_resume_section( + has_resume=True, + get_improved_resume_func=lambda role, skills: get_improved_resume(st.session_state.resume_agent, role, skills) + ) + else: + st.warning("Please upload and analyze a resume first in the 'Resume Analysis' tab.") + + else: + with st.sidebar: + groq_api_key=st.text_input("Enter your Groq Api Kyey Here",type='password') + deepgram_api_key=st.text_input("Enter your Deepgram Api Key Here",type='password') + if not groq_api_key: + if not deepgram_api_key: + st.warning("please Enter your Groq api key !") + st.warning("please Enter your Groq And Deepgram api key !") + + # st.title("AI Interview Assistant") + if groq_api_key and deepgram_api_key: + interviewAssistant=Interview(groq_api_key,deepgram_api_key) + interviewAssistant.run() + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/simple_ai_agents/Recruitify/interview.py b/simple_ai_agents/Recruitify/interview.py new file mode 100644 index 00000000..1bfd4ac0 --- /dev/null +++ b/simple_ai_agents/Recruitify/interview.py @@ -0,0 +1,486 @@ +import re +import streamlit as st +from groq import Groq +import os +import tempfile +import base64 +import plotly.graph_objects as go +from streamlit_mic_recorder import mic_recorder +from deepgram import DeepgramClient, PrerecordedOptions, FileSource +import time +from datetime import datetime, timedelta +from langchain_groq import ChatGroq + + +def apply_custom_css(accent_color="#d32f2f"): + st.markdown(f""" + + """, unsafe_allow_html=True) + +class Interview: + def __init__(self, groq_api_key, deepgram_api_key): + self.GROQ_API_KEY = groq_api_key + self.DEEPGRAM_API_KEY = deepgram_api_key + self.client = Groq(api_key=self.GROQ_API_KEY) + self.llm = ChatGroq(model="llama-3.3-70b-versatile", temperature=0, api_key=self.GROQ_API_KEY) + if "current_q" not in st.session_state: + st.session_state.current_q = 0 + if "questions" not in st.session_state: + st.session_state.questions = [] + if "responses" not in st.session_state: + st.session_state.responses = [] + if "feedback" not in st.session_state: + st.session_state.feedback = [] + if "audio_volume" not in st.session_state: + st.session_state.audio_volume = 1.0 + if "recorder_key" not in st.session_state: + st.session_state.recorder_key = f"recorder_{st.session_state.current_q}" + if "answer_times" not in st.session_state: + st.session_state.answer_times = [] + if "start_time" not in st.session_state: + st.session_state.start_time = None + + def sidebar_upload_and_summary(self): + with st.sidebar: + st.markdown("### πŸ“€ Upload Questions") + file_uploaded = st.file_uploader("Upload the interview questions (.md file)", type=["md", "txt"]) + if file_uploaded and not st.session_state.questions: + try: + content = file_uploaded.read().decode("utf-8") + except UnicodeDecodeError: + st.error("Failed to decode file. Ensure it’s a valid .md or .txt file with UTF-8 encoding.") + st.stop() + questions = [] + try: + question_sections = re.split(r"## \d+[.\-]\s*", content)[1:] + for section in question_sections: + lines = section.strip().split("\n") + if len(lines) < 2: + st.error("Invalid question format in file.") + st.stop() + question_type = lines[0].split(" Question")[0].strip() + question_text = " ".join(lines[1:]).strip() + if question_type and question_text: + questions.append({"type": question_type, "question": question_text}) + except Exception as e: + st.error(f"Error parsing file: {str(e)}") + st.stop() + if not questions: + st.error("No valid questions found in the file.") + st.stop() + st.session_state.current_q = 0 + st.session_state.questions = questions + st.session_state.responses = ["" for _ in questions] + st.session_state.feedback = ["" for _ in questions] + st.session_state.answer_times = [None for _ in questions] + st.session_state.recorder_key = f"recorder_{st.session_state.current_q}" + st.success(f"Loaded {len(questions)} questions.") + with st.sidebar: + st.markdown("### 🎚️ Audio Settings") + st.session_state.audio_volume = st.slider("Audio volume", 0.0, 1.0, st.session_state.audio_volume, 0.1) + st.markdown("### πŸ“Š Interview Summary") + for i, q in enumerate(st.session_state.questions): + with st.expander(f"Question {i+1}"): + st.write(f"**Type:** {q['type']}") + st.markdown(f"**Question:** {q['question']}") + st.write(f"**Response:** {st.session_state.responses[i] if st.session_state.responses[i] else 'Not answered'}") + st.markdown(f"**Feedback:** {st.session_state.feedback[i] if st.session_state.feedback[i] else 'No feedback yet'}") + if st.session_state.answer_times[i] is not None: + st.write(f"**Time Taken:** {timedelta(seconds=int(st.session_state.answer_times[i]))}") + + def run(self): + apply_custom_css() # Apply the custom CSS + st.markdown('

πŸŽ™οΈInterview assistant

', unsafe_allow_html=True) + self.sidebar_upload_and_summary() + if not st.session_state.questions: + st.info("Please upload a question file to start the interview.") + return + if st.session_state.current_q < 0 or st.session_state.current_q >= len(st.session_state.questions): + st.error(f"Invalid question index: {st.session_state.current_q}. Resetting to 0.") + st.session_state.current_q = 0 + st.session_state.recorder_key = f"recorder_{st.session_state.current_q}" + st.session_state.start_time = None + st.rerun() + + q = st.session_state.questions[st.session_state.current_q] + with st.container(): + st.markdown( + f'

Question {st.session_state.current_q+1} of {len(st.session_state.questions)}

' + f'

{q["question"]}

', + unsafe_allow_html=True + ) + with st.sidebar: + st.markdown("### πŸ“ˆ Progress") + progress = (st.session_state.current_q + 1) / len(st.session_state.questions) + st.progress(progress) + st.markdown(f"**{st.session_state.current_q + 1}/{len(st.session_state.questions)}** questions completed") + + col1, col2, col3 = st.columns([1, 1, 1]) + with col2: + if st.button("πŸ”Š Play Question"): + voice = "Deedee-PlayAI" + model = "playai-tts" + response_format = "mp3" + with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_file: + temp_path = tmp_file.name + try: + with st.spinner("Just a Second..."): + response = self.client.audio.speech.create( + model=model, + voice=voice, + input=f"This is a {q['type']} question: {q['question']}", + response_format=response_format + ) + response.write_to_file(temp_path) + with open(temp_path, "rb") as audio_file: + audio_bytes = audio_file.read() + audio_base64 = base64.b64encode(audio_bytes).decode("utf-8") + audio_html = f""" + + + """ + st.markdown(audio_html, unsafe_allow_html=True) + except Exception as e: + st.error(f"Failed to generate or play audio: {str(e)}") + finally: + if os.path.exists(temp_path): + os.remove(temp_path) + + st.markdown('

🎀 Your Interviewer

', unsafe_allow_html=True) + with st.container(): + st.markdown('
', unsafe_allow_html=True) + audio = mic_recorder(start_prompt="Start Interview", stop_prompt="Stop Interview", key=st.session_state.recorder_key) + st.markdown('
', unsafe_allow_html=True) + if audio: + st.audio(audio['bytes']) + if st.session_state.start_time is None: + st.session_state.start_time = time.time() + with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmpfile: + tmpfile.write(audio['bytes']) + tmpfile_path = tmpfile.name + deepgram = DeepgramClient(api_key=self.DEEPGRAM_API_KEY) + with open(tmpfile_path, "rb") as file: + buffer_data = file.read() + payload: FileSource = {"buffer": buffer_data} + options = PrerecordedOptions(model="nova-3", smart_format=True) + response = deepgram.listen.rest.v("1").transcribe_file(payload, options) + text = response.to_dict()["results"]["channels"][0]["alternatives"][0]["paragraphs"]["transcript"] + st.session_state.responses[st.session_state.current_q] = text + if st.session_state.start_time is not None: + time_taken = time.time() - st.session_state.start_time + st.session_state.answer_times[st.session_state.current_q] = time_taken + st.session_state.start_time = None + st.markdown("**The Response:**") + st.write(text) + + type_q = q['type'] + if type_q == 'Basic': + type_q = "HR Recruiter" + elif type_q == 'Technical': + type_q = "Technical Expert" + else: + type_q = "Manager or Team Lead" + + prompt = f""" + You are an experienced {type_q} interviewer assessing a candidate's answer. + + **Question:** {q['question']} + **Candidate Response:** {text} + + ⚠️ Important: Do not be biased in your evaluation. Focus only on the quality, clarity, and correctness of the response. + + Your task: + 1. Evaluate the overall quality of the response. + 2. Highlight strengths and positive aspects. + 3. Identify specific weaknesses or missing points. + 4. Suggest clear, actionable improvements. + 5. Provide a **Confidence Score** (1-10) for how confidently the response was delivered. + 6. Provide an **Accuracy Score** (1-10) for how factually correct the response is, based on the question. + + Format your response exactly as follows: + + Evaluation: + [Your evaluation text here] + + Strengths: + - [Strength 1] + - [Strength 2] + + Weaknesses: + - [Weakness 1] + - [Weakness 2] + + Suggestions for Improvement: + - [Suggestion 1] + - [Suggestion 2] + + Confidence Score: X/10 + Accuracy Score: X/10 + """ + + try: + ai_response = self.llm.invoke(prompt).content + st.session_state.feedback[st.session_state.current_q] = ai_response + except Exception as e: + st.error(f"Failed to get AI feedback: {str(e)}") + if st.session_state.feedback[st.session_state.current_q]: + st.markdown(f'

πŸ€– AI Feedback

{st.session_state.feedback[st.session_state.current_q]}
', unsafe_allow_html=True) + col1, col2 = st.columns(2) + with col1: + if st.button("⬅️ Previous Question", disabled=st.session_state.current_q == 0): + st.session_state.current_q -= 1 + st.session_state.recorder_key = f"recorder_{st.session_state.current_q}" + st.session_state.start_time = None + st.rerun() + with col2: + if st.button("➑️ Next Question", disabled=st.session_state.current_q >= len(st.session_state.questions) - 1): + st.session_state.current_q += 1 + st.session_state.recorder_key = f"recorder_{st.session_state.current_q}" + st.session_state.start_time = None + st.rerun() + if st.button("βœ… Finish Interview"): + self.finish_report() + + def finish_report(self): + all_responded = all(r.strip() for r in st.session_state.responses) + st.success("Interview completed!") + if not all_responded: + st.warning("Some questions have no responses. The report may be incomplete.") + scores = [] + for feedback in st.session_state.feedback: + if feedback: + match = re.search(r"Confidence Score: (\d+)/10.*Accuracy Score: (\d+)/10", feedback, re.DOTALL) + if match: + scores.append((int(match.group(1)), int(match.group(2)))) + with st.container(): + st.markdown('
', unsafe_allow_html=True) + if scores: + avg_confidence = sum(s[0] for s in scores) / len(scores) + avg_accuracy = sum(s[1] for s in scores) / len(scores) + st.markdown(f"

Average Scores

Confidence: {avg_confidence:.1f}/10

Accuracy: {avg_accuracy:.1f}/10

", unsafe_allow_html=True) + labels = [f"Q{i+1}" for i in range(len(scores))] + confidence_data = [s[0] for s in scores] + accuracy_data = [s[1] for s in scores] + fig = go.Figure(data=[ + go.Bar(name="Confidence", x=labels, y=confidence_data, marker_color="#2563eb", text=confidence_data, textposition="auto"), + go.Bar(name="Accuracy", x=labels, y=accuracy_data, marker_color="#dc2626", text=accuracy_data, textposition="auto") + ]) + fig.update_layout( + barmode="group", + yaxis=dict(range=[0, 10], title="Score", gridcolor="#e5e7eb"), + xaxis=dict(title="Questions"), + title=dict(text="Interview Performance Scores", x=0.5, xanchor="center"), + template="plotly_white", + height=400, + margin=dict(t=50, b=50, l=50, r=50), + showlegend=True, + legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="center", x=0.5) + ) + st.plotly_chart(fig, use_container_width=True) + else: + st.warning("No scores available to display the graph. Please ensure all questions have feedback.") + st.markdown('
', unsafe_allow_html=True) + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + markdown_report = "# Interview Report\n\n" + for i, q in enumerate(st.session_state.questions): + markdown_report += f"## Question {i+1}\n" + markdown_report += f"**Type:** {q['type']}\n\n" + markdown_report += f"**Question:** {q['question']}\n\n" + markdown_report += f"**User Response:** {st.session_state.responses[i] if st.session_state.responses[i] else 'Not answered'}\n\n" + markdown_report += f"**AI Feedback:**\n{st.session_state.feedback[i] if st.session_state.feedback[i] else 'No feedback'}\n\n" + time_taken = st.session_state.answer_times[i] + markdown_report += f"**Time Taken:** {timedelta(seconds=int(time_taken)) if time_taken else 'Not recorded'}\n\n" + markdown_report += "---\n\n" + if scores: + markdown_report += f"**Average Confidence Score:** {avg_confidence:.1f}/10\n\n" + markdown_report += f"**Average Accuracy Score:** {avg_accuracy:.1f}/10\n\n" + st.download_button( + label="πŸ“₯ Download Feedback Report", + data=markdown_report.encode("utf-8"), + file_name=f"interview_feedback_Requirement_Agent😎.md", + mime="text/markdown", + key="download_button", + help="Download the interview report as a Markdown file" + ) \ No newline at end of file diff --git a/simple_ai_agents/Recruitify/patch_sqlite.py b/simple_ai_agents/Recruitify/patch_sqlite.py new file mode 100644 index 00000000..51e2fa2e --- /dev/null +++ b/simple_ai_agents/Recruitify/patch_sqlite.py @@ -0,0 +1,5 @@ +import sys +import os + +__import__('pysqlite3') +sys.modules['sqlite3'] = sys.modules.pop('pysqlite3') \ No newline at end of file diff --git a/simple_ai_agents/Recruitify/requirements.txt b/simple_ai_agents/Recruitify/requirements.txt new file mode 100644 index 00000000..a06622eb --- /dev/null +++ b/simple_ai_agents/Recruitify/requirements.txt @@ -0,0 +1,17 @@ +python-dotenv +streamlit +langchain +langchain-groq +pandas +matplotlib +faiss-cpu +pypdf2 +langchain-community +langchain-google-genai +google-search-results +groq +plotly +streamlit-mic-recorder +deepgram-sdk +nest-asyncio +pysqlite3-binary \ No newline at end of file diff --git a/simple_ai_agents/Recruitify/temp/practise.py b/simple_ai_agents/Recruitify/temp/practise.py new file mode 100644 index 00000000..028c0a96 --- /dev/null +++ b/simple_ai_agents/Recruitify/temp/practise.py @@ -0,0 +1,619 @@ + +# """import os + +# from deepgram import ( +# DeepgramClient, +# PrerecordedOptions, +# FileSource, +# ) +# import tempfile +# import streamlit as st +# from streamlit_mic_recorder import mic_recorder + +# audio = mic_recorder( +# start_prompt="⏺️", +# stop_prompt="⏹️", +# key='recorder' +# ) + +# if audio: +# st.audio(audio['bytes']) # playback + +# # Step 1: Save as a temporary MP3 file +# with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmpfile: +# tmpfile.write(audio['bytes']) +# tmpfile_path = tmpfile.name + + +# # Path to the audio file +# AUDIO_FILE = tmpfile_path + +# # STEP 1 Create a Deepgram client using the API key +# # deepgram = DeepgramClient(a) +# deepgram = DeepgramClient(api_key="37c16f3a101aad2918c257d802f21f1843a9f683") + + +# with open(AUDIO_FILE, "rb") as file: +# buffer_data = file.read() + +# payload: FileSource = { +# "buffer": buffer_data, +# } + +# #STEP 2: Configure Deepgram options for audio analysis +# options = PrerecordedOptions( +# model="nova-3", +# smart_format=True, +# ) + +# # STEP 3: Call the transcribe_file method with the text payload and options +# response = deepgram.listen.rest.v("1").transcribe_file(payload, options) + + +# text=response.to_dict().get("results").get("channels")[0].get("alternatives")[0].get("paragraphs").get('transcript') +# print(tp) +# st.write(tp) +# """ + + + + +# import re +# import streamlit as st +# from groq import Groq +# import os +# import tempfile +# import pandas as pd +# from streamlit_mic_recorder import speech_to_text +# from langchain_groq import ChatGroq +# from dotenv import load_dotenv +# import base64 + +# load_dotenv() + +# # ---- Groq Config ---- +# GROQ_API_KEY = os.getenv("GROQ_API_KEY") +# if not GROQ_API_KEY: +# st.error("GROQ_API_KEY environment variable not set. Please configure it.") +# st.stop() +# client = Groq(api_key=GROQ_API_KEY) + +# # ---- State Init ---- +# if "current_q" not in st.session_state: +# st.session_state.current_q = 0 +# if "questions" not in st.session_state: +# st.session_state.questions = [] +# if "responses" not in st.session_state: +# st.session_state.responses = [] +# if "feedback" not in st.session_state: +# st.session_state.feedback = [] +# if "audio_volume" not in st.session_state: +# st.session_state.audio_volume = 1.0 # Default volume + +# # ---- LLM ---- +# llm = ChatGroq(model='llama-3.3-70b-versatile', temperature=0.7, api_key=GROQ_API_KEY) + +# # ---- File Upload ---- +# file_uploaded = st.file_uploader("Upload the interview questions (.md file)", type=["md", "txt"]) +# if file_uploaded and not st.session_state.questions: +# try: +# content = file_uploaded.read().decode("utf-8") +# except UnicodeDecodeError: +# st.error("Failed to decode file. Ensure it’s a valid .md or .txt file with UTF-8 encoding.") +# st.stop() + +# try: +# questions = [] +# question_sections = re.split(r"## \d+[.\-]\s*", content)[1:] +# for section in question_sections: +# lines = section.strip().split("\n") +# if len(lines) < 2: +# st.error("Invalid question format in file.") +# st.stop() +# question_type = lines[0].split(" Question")[0].strip() +# question_text = " ".join(lines[1:]).strip() +# if question_type and question_text: +# questions.append({"type": question_type, "question": question_text}) +# except Exception as e: +# st.error(f"Error parsing file: {str(e)}") +# st.stop() + +# if not questions: +# st.error("No valid questions found in the file.") +# st.stop() + +# st.session_state.current_q = 0 +# st.session_state.questions = questions +# st.session_state.responses = ["" for _ in questions] +# st.session_state.feedback = ["" for _ in questions] +# st.success(f"Loaded {len(questions)} questions.") + +# # ---- Interview Flow ---- +# if st.session_state.questions: +# # State validation +# if st.session_state.current_q < 0 or st.session_state.current_q >= len(st.session_state.questions): +# st.error(f"Invalid question index: {st.session_state.current_q}. Resetting to 0.") +# st.session_state.current_q = 0 +# st.rerun() + +# q = st.session_state.questions[st.session_state.current_q] +# st.write(f"Progress: Question {st.session_state.current_q+1} of {len(st.session_state.questions)}") +# st.subheader(f"Question {st.session_state.current_q+1}") +# st.write(f"**Type:** {q['type']}") +# st.markdown(q['question']) +# st.write(f"Debug: Current question index: {st.session_state.current_q}") + +# voice = "Deedee-PlayAI" +# model = "playai-tts" +# response_format = "mp3" + +# # Volume control +# st.session_state.audio_volume = st.slider("Audio volume", 0.0, 1.0, st.session_state.audio_volume, 0.1) + +# if st.button("πŸ”Š Play Question"): +# with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_file: +# temp_path = tmp_file.name +# try: +# with st.spinner("Generating speech..."): +# response = client.audio.speech.create( +# model=model, +# voice=voice, +# input=f"This is a {q['type']} question: {q['question']}", +# response_format=response_format +# ) +# response.write_to_file(temp_path) + +# with open(temp_path, "rb") as audio_file: +# audio_bytes = audio_file.read() +# audio_base64 = base64.b64encode(audio_bytes).decode("utf-8") + +# audio_html = f""" +# +# +# """ +# st.markdown(audio_html, unsafe_allow_html=True) +# except Exception as e: +# st.error(f"Failed to generate or play audio: {str(e)}") +# finally: +# if os.path.exists(temp_path): +# os.remove(temp_path) + +# # Record Answer +# text = speech_to_text(language='en', use_container_width=True, just_once=True, key=f'STT_{st.session_state.current_q}') +# if not text: +# text = st.text_area("Type your response (if speech fails):", key=f'text_input_{st.session_state.current_q}') +# if text: +# st.write(f"πŸ—£ **Your Response:** {text}") +# st.session_state.responses[st.session_state.current_q] = text + +# # AI Feedback +# prompt = f""" +# You are an experienced technical interviewer assessing a candidate's answer. + +# **Question Type:** {q['type']} +# **Question:** {q['question']} +# **Candidate Response:** {text} + +# Your task: +# 1. Evaluate the overall quality of the response. +# 2. Highlight strengths and positive aspects. +# 3. Identify specific weaknesses or missing points. +# 4. Suggest clear, actionable improvements. +# 5. Provide a **Confidence Score** (1-10) for your evaluation. +# 6. Provide an **Accuracy Score** (1-10) for how factually correct the answer is. + +# Format your response exactly as follows: + +# Evaluation: +# [Your evaluation text here] + +# Strengths: +# - [Strength 1] +# - [Strength 2] + +# Weaknesses: +# - [Weakness 1] +# - [Weakness 2] + +# Suggestions for Improvement: +# - [Suggestion 1] +# - [Suggestion 2] + +# Confidence Score: X/10 +# Accuracy Score: X/10 +# """ +# try: +# ai_response = llm.invoke(prompt).content +# st.session_state.feedback[st.session_state.current_q] = ai_response +# st.markdown(f"πŸ€– **AI Feedback:** {ai_response}") +# except Exception as e: +# st.error(f"Failed to get AI feedback: {str(e)}") + +# # Navigation +# col1, col2 = st.columns(2) +# with col1: +# prev_disabled = st.session_state.current_q == 0 +# if st.button("⬅️ Previous Question", key="prev_btn", disabled=prev_disabled): +# st.write(f"Debug: Moving to previous question. Current index: {st.session_state.current_q}") +# st.session_state.current_q -= 1 +# st.write(f"Debug: New index: {st.session_state.current_q}") +# st.rerun() +# with col2: +# next_disabled = st.session_state.current_q >= len(st.session_state.questions) - 1 +# if st.button("➑️ Next Question", key="next_btn", disabled=next_disabled): +# st.write(f"Debug: Moving to next question. Current index: {st.session_state.current_q}") +# st.session_state.current_q += 1 +# st.write(f"Debug: New index: {st.session_state.current_q}") +# st.rerun() + +# all_responded = all(response.strip() for response in st.session_state.responses) +# if st.button("βœ… Finish Interview", disabled=not all_responded): +# st.success("Interview completed!") +# if not all_responded: +# st.warning("Some questions have no responses. The report may be incomplete.") + +# # Extract scores for chart +# scores = [] +# for feedback in st.session_state.feedback: +# if feedback: +# match = re.search(r"Confidence Score: (\d+)/10.*Accuracy Score: (\d+)/10", feedback, re.DOTALL) +# if match: +# scores.append((int(match.group(1)), int(match.group(2)))) +# import plotly.graph_objects as go + +# if scores: +# avg_confidence = sum(s[0] for s in scores) / len(scores) +# avg_accuracy = sum(s[1] for s in scores) / len(scores) + +# st.write(f"Average Confidence Score: {avg_confidence:.1f}/10") +# st.write(f"Average Accuracy Score: {avg_accuracy:.1f}/10") + +# labels = [f"Q{i+1}" for i in range(len(scores))] +# confidence_data = [s[0] for s in scores] +# accuracy_data = [s[1] for s in scores] + +# fig = go.Figure(data=[ +# go.Bar(name='Confidence', x=labels, y=confidence_data, marker_color='rgba(75, 192, 192, 0.7)'), +# go.Bar(name='Accuracy', x=labels, y=accuracy_data, marker_color='rgba(255, 99, 132, 0.7)') +# ]) + +# fig.update_layout( +# barmode='group', +# yaxis=dict(range=[0, 10], title="Score"), +# xaxis=dict(title="Questions"), +# title="Interview Performance Scores", +# legend=dict(title="Metrics") +# ) + +# st.plotly_chart(fig, use_container_width=True) + + + +# temp +"""""# import re +# import streamlit as st +# from groq import Groq +# import os +# import tempfile +# import base64 +# import plotly.graph_objects as go +# from streamlit_mic_recorder import mic_recorder +# from deepgram import DeepgramClient, PrerecordedOptions, FileSource +# import time +# from datetime import datetime, timedelta +# from dotenv import load_dotenv +# from langchain_groq import ChatGroq + +# load_dotenv() + +# ---- API Keys ---- +# GROQ_API_KEY = os.getenv("GROQ_API_KEY") +# DATAGRAM_API_KEY = os.getenv("DATAGRAM_API_KEY") + +# class Interview: +# def __init__(self,groq_api_key,datagram_api_key): +# self.GROQ_API_KEY=groq_api_key +# self.DATAGRAM_API_KEY=datagram_api_key + +# self.client = Groq(api_key=self.GROQ_API_KEY) +# self.llm = ChatGroq(model="llama-3.3-70b-versatile", temperature=0.7, api_key=self.GROQ_API_KEY) +# # ---- Session State ---- +# if "current_q" not in st.session_state: +# st.session_state.current_q = 0 +# if "questions" not in st.session_state: +# st.session_state.questions = [] +# if "responses" not in st.session_state: +# st.session_state.responses = [] +# if "feedback" not in st.session_state: +# st.session_state.feedback = [] +# if "audio_volume" not in st.session_state: +# st.session_state.audio_volume = 1.0 +# if "recorder_key" not in st.session_state: +# st.session_state.recorder_key = f"recorder_{st.session_state.current_q}" +# if "answer_times" not in st.session_state: +# st.session_state.answer_times = [] +# if "start_time" not in st.session_state: +# st.session_state.start_time = None + +# def sidebar_upload_and_summary(self): +# """Handles file upload and shows sidebar summary""" +# with st.sidebar: +# st.markdown("### πŸ“€ Upload Questions") + +# file_uploaded = st.file_uploader("Upload the interview questions (.md file)", type=["md", "txt"]) +# if file_uploaded and not st.session_state.questions: +# try: +# content = file_uploaded.read().decode("utf-8") +# except UnicodeDecodeError: +# st.error("Failed to decode file. Ensure it’s a valid .md or .txt file with UTF-8 encoding.") +# st.stop() + +# questions = [] +# try: +# question_sections = re.split(r"## \d+[.\-]\s*", content)[1:] +# for section in question_sections: +# lines = section.strip().split("\n") +# if len(lines) < 2: +# st.error("Invalid question format in file.") +# st.stop() +# question_type = lines[0].split(" Question")[0].strip() +# question_text = " ".join(lines[1:]).strip() +# if question_type and question_text: +# questions.append({"type": question_type, "question": question_text}) +# except Exception as e: +# st.error(f"Error parsing file: {str(e)}") +# st.stop() + +# if not questions: +# st.error("No valid questions found in the file.") +# st.stop() + +# st.session_state.current_q = 0 +# st.session_state.questions = questions +# st.session_state.responses = ["" for _ in questions] +# st.session_state.feedback = ["" for _ in questions] +# st.session_state.answer_times = [None for _ in questions] +# st.session_state.recorder_key = f"recorder_{st.session_state.current_q}" +# st.success(f"Loaded {len(questions)} questions.") + +# # Sidebar UI +# with st.sidebar: +# st.session_state.audio_volume = st.slider("🎚️ Audio volume", 0.0, 1.0, st.session_state.audio_volume, 0.1) +# st.header("Interview Summary") +# for i, q in enumerate(st.session_state.questions): +# with st.expander(f"Question {i+1}"): +# st.write(f"**Type:** {q['type']}") +# st.markdown(f"**Question:** {q['question']}") +# st.write(f"**Response:** {st.session_state.responses[i] if st.session_state.responses[i] else 'Not answered'}") +# st.markdown(f"**Feedback:** {st.session_state.feedback[i] if st.session_state.feedback[i] else 'No feedback yet'}") +# if st.session_state.answer_times[i] is not None: +# st.write(f"**Time Taken:** {timedelta(seconds=int(st.session_state.answer_times[i]))}") + +# def run(self): +# """Main interview workflow""" +# st.title("πŸŽ™οΈ AI-Powered Interview Practice") + +# self.sidebar_upload_and_summary() + +# if not st.session_state.questions: +# return + +# # Validate state +# if st.session_state.current_q < 0 or st.session_state.current_q >= len(st.session_state.questions): +# st.error(f"Invalid question index: {st.session_state.current_q}. Resetting to 0.") +# st.session_state.current_q = 0 +# st.session_state.recorder_key = f"recorder_{st.session_state.current_q}" +# st.session_state.start_time = None +# st.rerun() + +# q = st.session_state.questions[st.session_state.current_q] +# st.subheader(f"Question {st.session_state.current_q+1}") +# st.write(f"**Type:** {q['type']}") +# st.markdown(q['question']) + +# # Progress Bar +# with st.sidebar: +# st.markdown('# Progress') +# progress = (st.session_state.current_q + 1) / len(st.session_state.questions) +# st.progress(progress) + +# # Play Question +# if st.button("πŸ”Š Play Question"): +# voice = "Deedee-PlayAI" +# model = "playai-tts" +# response_format = "mp3" + +# with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_file: +# temp_path = tmp_file.name +# try: +# with st.spinner("Generating speech..."): +# response = self.client.audio.speech.create( +# model=model, +# voice=voice, +# input=f"This is a {q['type']} question: {q['question']}", +# response_format=response_format +# ) +# response.write_to_file(temp_path) + +# with open(temp_path, "rb") as audio_file: +# audio_bytes = audio_file.read() +# audio_base64 = base64.b64encode(audio_bytes).decode("utf-8") + +# audio_html = f""" +# +# +# """ +# st.markdown(audio_html, unsafe_allow_html=True) +# except Exception as e: +# st.error(f"Failed to generate or play audio: {str(e)}") +# finally: +# if os.path.exists(temp_path): +# os.remove(temp_path) + +# # Record Answer +# audio = mic_recorder(start_prompt="Start Interview", stop_prompt="Stop Interview", key=st.session_state.recorder_key) +# if audio: +# st.audio(audio['bytes']) +# if st.session_state.start_time is None: +# st.session_state.start_time = time.time() + +# with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmpfile: +# tmpfile.write(audio['bytes']) +# tmpfile_path = tmpfile.name + +# deepgram = DeepgramClient(api_key=self.DATAGRAM_API_KEY) +# with open(tmpfile_path, "rb") as file: +# buffer_data = file.read() + +# payload: FileSource = {"buffer": buffer_data} +# options = PrerecordedOptions(model="nova-3", smart_format=True) +# response = deepgram.listen.rest.v("1").transcribe_file(payload, options) + +# text = response.to_dict()["results"]["channels"][0]["alternatives"][0]["paragraphs"]["transcript"] +# st.session_state.responses[st.session_state.current_q] = text + +# # Track time taken +# if st.session_state.start_time is not None: +# time_taken = time.time() - st.session_state.start_time +# st.session_state.answer_times[st.session_state.current_q] = time_taken +# st.session_state.start_time = None + +# st.markdown("**The Response:**") +# st.write(text) + +# # AI Feedback +# prompt = f""" +# You are an experienced technical interviewer assessing a candidate's answer. + +# **Question Type:** {q['type']} +# **Question:** {q['question']} +# **Candidate Response:** {text} + +# Your task: +# 1. Evaluate the overall quality of the response. +# 2. Highlight strengths and positive aspects. +# 3. Identify specific weaknesses or missing points. +# 4. Suggest clear, actionable improvements. +# 5. Provide a **Confidence Score** (1-10) for your evaluation. +# 6. Provide an **Accuracy Score** (1-10) for how factually correct the answer is. + +# Format your response exactly as follows: + +# Evaluation: +# [Your evaluation text here] + +# Strengths: +# - [Strength 1] +# - [Strength 2] + +# Weaknesses: +# - [Weakness 1] +# - [Weakness 2] + +# Suggestions for Improvement: +# - [Suggestion 1] +# - [Suggestion 2] + +# Confidence Score: X/10 +# Accuracy Score: X/10 +# """ +# try: +# ai_response = self.llm.invoke(prompt).content +# st.session_state.feedback[st.session_state.current_q] = ai_response +# except Exception as e: +# st.error(f"Failed to get AI feedback: {str(e)}") + +# # Feedback +# if st.session_state.feedback[st.session_state.current_q]: +# st.markdown(f"πŸ€– **AI Feedback:** {st.session_state.feedback[st.session_state.current_q]}") + +# # Navigation +# col1, col2 = st.columns(2) +# with col1: +# if st.button("⬅️ Previous Question", disabled=st.session_state.current_q == 0): +# st.session_state.current_q -= 1 +# st.session_state.recorder_key = f"recorder_{st.session_state.current_q}" +# st.session_state.start_time = None +# st.rerun() +# with col2: +# if st.button("➑️ Next Question", disabled=st.session_state.current_q >= len(st.session_state.questions) - 1): +# st.session_state.current_q += 1 +# st.session_state.recorder_key = f"recorder_{st.session_state.current_q}" +# st.session_state.start_time = None +# st.rerun() + +# # Finish Interview +# if st.button("βœ… Finish Interview"): +# self.finish_report() + +# def finish_report(self): +# """Generates final interview report with charts and download option""" +# all_responded = all(r.strip() for r in st.session_state.responses) +# st.success("Interview completed!") +# if not all_responded: +# st.warning("Some questions have no responses. The report may be incomplete.") + +# scores = [] +# for feedback in st.session_state.feedback: +# if feedback: +# match = re.search(r"Confidence Score: (\d+)/10.*Accuracy Score: (\d+)/10", feedback, re.DOTALL) +# if match: +# scores.append((int(match.group(1)), int(match.group(2)))) + +# if scores: +# avg_confidence = sum(s[0] for s in scores) / len(scores) +# avg_accuracy = sum(s[1] for s in scores) / len(scores) + +# st.write(f"Average Confidence Score: {avg_confidence:.1f}/10") +# st.write(f"Average Accuracy Score: {avg_accuracy:.1f}/10") + +# labels = [f"Q{i+1}" for i in range(len(scores))] +# confidence_data = [s[0] for s in scores] +# accuracy_data = [s[1] for s in scores] + +# fig = go.Figure(data=[ +# go.Bar(name="Confidence", x=labels, y=confidence_data, marker_color="rgba(75, 192, 192, 0.7)"), +# go.Bar(name="Accuracy", x=labels, y=accuracy_data, marker_color="rgba(255, 99, 132, 0.7)") +# ]) +# fig.update_layout(barmode="group", yaxis=dict(range=[0, 10], title="Score"), xaxis=dict(title="Questions"), title="Interview Performance Scores") +# st.plotly_chart(fig, use_container_width=True) + +# # Markdown Report +# timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") +# markdown_report = "# Interview Report\n\n" +# for i, q in enumerate(st.session_state.questions): +# markdown_report += f"## Question {i+1}\n" +# markdown_report += f"**Type:** {q['type']}\n\n" +# markdown_report += f"**Question:** {q['question']}\n\n" +# markdown_report += f"**User Response:** {st.session_state.responses[i] if st.session_state.responses[i] else 'Not answered'}\n\n" +# markdown_report += f"**AI Feedback:**\n{st.session_state.feedback[i] if st.session_state.feedback[i] else 'No feedback'}\n\n" +# time_taken = st.session_state.answer_times[i] +# markdown_report += f"**Time Taken:** {timedelta(seconds=int(time_taken)) if time_taken else 'Not recorded'}\n\n" +# markdown_report += "---\n\n" + +# if scores: +# markdown_report += f"**Average Confidence Score:** {avg_confidence:.1f}/10\n\n" +# markdown_report += f"**Average Accuracy Score:** {avg_accuracy:.1f}/10\n\n" + +# st.download_button( +# label="πŸ“₯ Download Feedback Report (Markdown)", +# data=markdown_report.encode("utf-8"), +# file_name=f"interview_feedback_{timestamp}.md", +# mime="text/markdown" +# ) + +""" \ No newline at end of file diff --git a/simple_ai_agents/Recruitify/temp/stt.py b/simple_ai_agents/Recruitify/temp/stt.py new file mode 100644 index 00000000..088c6c37 --- /dev/null +++ b/simple_ai_agents/Recruitify/temp/stt.py @@ -0,0 +1,76 @@ +# import streamlit as st +# import os +# from streamlit_mic_recorder import mic_recorder, speech_to_text + +# state = st.session_state + +# if 'text_received' not in state: +# state.text_received = [] + +# c1, c2 = st.columns(2) +# with c1: +# st.write("Convert speech to text:") +# with c2: +# text = speech_to_text(language='en', use_container_width=True, just_once=True, key='STT') + +# if text: +# state.text_received.append(text) + +# for text in state.text_received: +# st.text(text) + +# st.write("Record your voice, and play the recorded audio:") +# audio = mic_recorder(start_prompt="⏺️", stop_prompt="⏹️", key='recorder') + +# if audio: +# st.audio(audio['bytes']) + + +# text = speech_to_text(language='en', use_container_width=True, just_once=True, key='STT') +# if text: +# st.write(text) + + +# import streamlit as st +# import tempfile +# import base64 +# from deepgram import DeepgramClient, SpeakOptions + +# # Deepgram API Key (replace with env variable for security) +# DEEPGRAM_API_KEY = "37c16f3a101aad2918c257d802f21f1843a9f683" + +# st.title("🎀 Deepgram TTS Demo") + +# # User text input +# user_text = st.text_area("Enter text to synthesize:", "Hello world! My name is Ankush") + +# if st.button("Generate Speech"): +# try: +# # Create client +# deepgram = DeepgramClient(api_key=DEEPGRAM_API_KEY) + +# # Speak options +# options = SpeakOptions( +# model="aura-2-aries-en", # Choose model +# encoding="mp3", # Ensure mp3 encoding +# ) + +# # Temp file for audio +# with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_file: +# temp_path = tmp_file.name + +# # Generate speech and save to file +# response = deepgram.speak.rest.v("1").save(temp_path, {"text": user_text}, options) + +# # Play audio in Streamlit +# st.success("βœ… Speech generated successfully!") +# st.audio(temp_path, format="audio/mp3") + +# # Optionally: Base64 for embedding elsewhere +# with open(temp_path, "rb") as audio_file: +# audio_bytes = audio_file.read() +# audio_base64 = base64.b64encode(audio_bytes).decode("utf-8") +# # st.text_area("Base64 Audio (for API use):", audio_base64[:200] + "...") + +# except Exception as e: +# st.error(f"❌ Error: {e}") diff --git a/simple_ai_agents/Recruitify/ui.py b/simple_ai_agents/Recruitify/ui.py new file mode 100644 index 00000000..484170cc --- /dev/null +++ b/simple_ai_agents/Recruitify/ui.py @@ -0,0 +1,673 @@ + +import streamlit as st +import pandas as pd +import base64 +import io +import matplotlib.pyplot as plt + +def setup_page(): + """Apply custom CSS and setup page (without setting page config)""" + # Apply custom CSS only + apply_custom_css() + + # Add local logo handling via JavaScript + st.markdown(""" + + """, unsafe_allow_html=True) + +def display_header(): + + st.header("πŸš€ Recruitment Agent") + + + +def apply_custom_css(accent_color="#d32f2f"): + st.markdown(f""" + + """, unsafe_allow_html=True) + + + + +def setup_sidebar(): + with st.sidebar: + st.header("Configuration") + + st.subheader("API Keys") + google_api_key = st.text_input("Google API Key", type="password") + + st.markdown("---") + + return { + "google_api_key": google_api_key, + # "theme_color": theme_color + } + + + +def role_selection_section(role_requirements): + st.markdown('
', unsafe_allow_html=True) + + col1, col2 = st.columns([2, 1]) + + with col1: + role = st.selectbox("Select the role you're applying for:", list(role_requirements.keys())) + + with col2: + upload_jd = st.checkbox("custom job description instead") + + custom_jd = None + if upload_jd: + custom_jd_file = st.file_uploader("Upload job description (PDF or TXT)", type=["pdf", "txt"]) + if custom_jd_file: + st.success("Custom job description uploaded!") + custom_jd = custom_jd_file + + if not upload_jd: + st.info(f"Required skills: {', '.join(role_requirements[role])}") + st.markdown(f"

Cutoff Score for selection: {75}/100

", unsafe_allow_html=True) + + st.markdown('
', unsafe_allow_html=True) + + return role, custom_jd + + + +def resume_upload_section(): + st.markdown(""" +
+

πŸ“„ Upload Your Resume

+

Supported format: PDF

+
+ """, unsafe_allow_html=True) + + uploaded_resume = st.file_uploader("Upload The Resume", type=["pdf"], label_visibility="collapsed") + + return uploaded_resume + + + +def create_score_pie_chart(score): + """Create a professional pie chart for the score visualization""" + fig, ax = plt.subplots(figsize=(4, 4), facecolor='#111111') + + # Data + sizes = [score, 100 - score] + labels = ['', ''] # We'll use annotation instead + colors = ["#d32f2f", "#333333"] + explode = (0.05, 0) # explode the 1st slice (Score) + + # Plot + wedges, texts = ax.pie( + sizes, + labels=labels, + colors=colors, + explode=explode, + startangle=90, + wedgeprops={'width': 0.5, 'edgecolor': 'black', 'linewidth': 1} + ) + + # Draw a circle in the center to make it a donut chart + centre_circle = plt.Circle((0, 0), 0.25, fc='#111111') + ax.add_artist(centre_circle) + + # Equal aspect ratio ensures that pie is drawn as a circle + ax.set_aspect('equal') + + # Add score text in the center + ax.text(0, 0, f"{score}%", + ha='center', va='center', + fontsize=24, fontweight='bold', + color='white') + + # Add pass/fail indicator + status = "PASS" if score >= 75 else "FAIL" + status_color = "#4CAF50" if score >= 75 else "#d32f2f" + ax.text(0, -0.15, status, + ha='center', va='center', + fontsize=14, fontweight='bold', + color=status_color) + + # Set the background color + ax.set_facecolor('#111111') + + return fig + + + + +def display_analysis_results(analysis_result): + if not analysis_result: + return + + overall_score = analysis_result.get('overall_score', 0) + selected = analysis_result.get("selected", False) + skill_scores = analysis_result.get("skill_scores", {}) + detailed_weaknesses = analysis_result.get("detailed_weaknesses", []) + + st.markdown('
', unsafe_allow_html=True) + + + + col1, col2 = st.columns([1, 2]) + + with col1: + st.metric("Overall Score", f"{overall_score}/100") + fig = create_score_pie_chart(overall_score) + st.pyplot(fig) + + with col2: + if selected: + st.markdown("

βœ… Congratulations! You have been shortlisted.

", unsafe_allow_html=True) + else: + st.markdown("

❌ Unfortunately, you were not selected.

", unsafe_allow_html=True) + st.write(analysis_result.get('reasoning', '')) + + st.markdown('
', unsafe_allow_html=True) + + st.markdown('
', unsafe_allow_html=True) + + # Strengths + st.markdown('
', unsafe_allow_html=True) + st.subheader("🌟 Strengths") + strengths = analysis_result.get("strengths", []) + if strengths: + for skill in strengths: + st.markdown(f'
{skill} ({skill_scores.get(skill, "N/A")}/10)
', unsafe_allow_html=True) + else: + st.write("No notable strengths identified.") + st.markdown('
', unsafe_allow_html=True) + + # Weaknesses + + st.markdown('
', unsafe_allow_html=True) + st.subheader("🚩 Areas for Improvement") + missing_skills = analysis_result.get("missing_skills", []) + if missing_skills: + for skill in missing_skills: + st.markdown(f'
{skill} ({skill_scores.get(skill, "N/A")}/10)
', unsafe_allow_html=True) + else: + st.write("No significant areas for improvement.") + st.markdown('
', unsafe_allow_html=True) + + st.markdown('
', unsafe_allow_html=True) + + # Detailed weaknesses section + if detailed_weaknesses: + st.markdown('
', unsafe_allow_html=True) + st.subheader("πŸ“Š Detailed Weakness Analysis") + + for weakness in detailed_weaknesses: + skill_name = weakness.get('skill', '') + score = weakness.get('score', 0) + + with st.expander(f"{skill_name} (Score: {score}/10)"): + # Clean detail display + detail = weakness.get('detail', 'No specific details provided.') + # Clean JSON formatting if it appears in the text + if detail.startswith('```json') or '{' in detail: + detail = "The resume lacks examples of this skill." + + st.markdown(f'
Issue: {detail}
', + unsafe_allow_html=True) + + # Display improvement suggestions if available + if 'suggestions' in weakness and weakness['suggestions']: + st.markdown("How to improve:", unsafe_allow_html=True) + for i, suggestion in enumerate(weakness['suggestions']): + st.markdown(f'
{i+1}. {suggestion}
', + unsafe_allow_html=True) + + # Display example if available + if 'example' in weakness and weakness['example']: + st.markdown("Example addition:", unsafe_allow_html=True) + st.markdown(f'
{weakness["example"]}
', + unsafe_allow_html=True) + + st.markdown("---") + col1, col2, col3 = st.columns([1, 2, 1]) + with col2: + report_content = f""" +# Resume Analysis Report + +## Overall Score: {overall_score}/100 + +Status: {"βœ… Shortlisted" if selected else "❌ Not Selected"} + +## Analysis Reasoning +{analysis_result.get('reasoning', 'No reasoning provided.')} + +## Strengths +{", ".join(strengths if strengths else ["None identified"])} + +## Areas for Improvement +{", ".join(missing_skills if missing_skills else ["None identified"])} + +## Detailed Weakness Analysis +""" + # Add detailed weaknesses to report + for weakness in detailed_weaknesses: + skill_name = weakness.get('skill', '') + score = weakness.get('score', 0) + detail = weakness.get('detail', 'No specific details provided.') + + # Clean JSON formatting if it appears in the text + if detail.startswith('```json') or '{' in detail: + detail = "The resume lacks examples of this skill." + + report_content += f"\n### {skill_name} (Score: {score}/10)\n" + report_content += f"Issue: {detail}\n" + + # Add suggestions to report + if 'suggestions' in weakness and weakness['suggestions']: + report_content += "\nImprovement suggestions:\n" + for i, sugg in enumerate(weakness['suggestions']): + report_content += f"- {sugg}\n" + + # Add example to report + if 'example' in weakness and weakness['example']: + report_content += f"\nExample: {weakness['example']}\n" + + report_content += "\n---\nAnalysis provided by Recruitment Agent" + + report_b64 = base64.b64encode(report_content.encode()).decode() + href = f'πŸ“Š Download Analysis Report' + st.markdown(href, unsafe_allow_html=True) + + st.markdown('
', unsafe_allow_html=True) + + + + + +def resume_qa_section(has_resume, ask_question_func=None): + if not has_resume: + st.warning("Please upload and analyze a resume first.") + return + + st.markdown('
', unsafe_allow_html=True) + + st.subheader("Ask Questions About the Resume") + user_question = st.text_input("Enter your question about the resume:", placeholder="What is the candidate's most recent experience?") + + if user_question and ask_question_func: + with st.spinner("Searching resume and generating response..."): + response = ask_question_func(user_question) + + st.markdown('
', unsafe_allow_html=True) + st.write(response) + st.markdown('
', unsafe_allow_html=True) + + # Add example questions + with st.expander("Example Questions"): + example_questions = [ + "What is the candidate's most recent role?", + "How many years of experience does the candidate have with Python?", + "What educational qualifications does the candidate have?", + "What are the candidate's key achievements?", + "Has the candidate managed teams before?", + "What projects has the candidate worked on?", + "Does the candidate have experience with cloud technologies?" + ] + + for question in example_questions: + if st.button(question, key=f"q_{question}"): + st.session_state.current_question = question + st.experimental_rerun() + + st.markdown('
', unsafe_allow_html=True) + +def interview_questions_section(has_resume, generate_questions_func=None): + if not has_resume: + st.warning("Please upload and analyze a resume first.") + return + + st.markdown('
', unsafe_allow_html=True) + + col1, col2 = st.columns(2) + + with col1: + question_types = st.multiselect( + "Select question types:", + ["Basic", "Technical", "Experience", "Scenario", "Coding", "Behavioral"], + default=["Basic", "Technical"] + ) + + with col2: + difficulty = st.select_slider( + "Question difficulty:", + options=["Easy", "Medium", "Hard"], + value="Medium" + ) + + num_questions = st.slider("Number of questions:", 3, 15, 5) + + if st.button("Generate Interview Questions"): + if generate_questions_func: + with st.spinner("Generating personalized interview questions..."): + questions = generate_questions_func(question_types, difficulty, num_questions) + + # Create content for download + download_content = f"# Recruitment Agent - Interview Questions\n\n" + download_content += f"Difficulty: {difficulty}\n" + download_content += f"Types: {', '.join(question_types)}\n\n" + + for i, (q_type, question) in enumerate(questions): + with st.expander(f"{q_type}: {question[:50]}..."): + st.write(question) + + # For coding questions, add code editor + if q_type == "Coding": + st.code("# Write your solution here", language="python") + + # Add to download content + download_content += f"## {i+1}. {q_type} Question\n\n" + download_content += f"{question}\n\n" + if q_type == "Coding": + download_content += "```python\n# Write your solution here\n```\n\n" + + # download content + download_content += "\n---\nQuestions generated by Recruitment Agent" + + # Add download button + if questions: + st.markdown("---") + questions_bytes = download_content.encode() + b64 = base64.b64encode(questions_bytes).decode() + href = f'πŸ“ Download All Questions' + st.markdown(href, unsafe_allow_html=True) + + st.markdown('
', unsafe_allow_html=True) + +def resume_improvement_section(has_resume, improve_resume_func=None): + if not has_resume: + st.warning("Please upload and analyze a resume first.") + return + + st.markdown('
', unsafe_allow_html=True) + + improvement_areas = st.multiselect( + "Select areas to improve:", + ["Content", "Format", "Skills Highlighting", "Experience Description", "Education", "Projects", "Achievements", "Overall Structure"], + default=["Content", "Skills Highlighting"] + ) + + target_role = st.text_input("Target role (optional):", placeholder="e.g., Senior Data Scientist at Google") + + if st.button("Generate Resume Improvements"): + if improve_resume_func: + with st.spinner("Analyzing and generating improvements..."): + improvements = improve_resume_func(improvement_areas, target_role) + + # Create content for download + download_content = f"# Resume Improvement Suggestions\n\nTarget Role: {target_role if target_role else 'Not specified'}\n\n" + + for area, suggestions in improvements.items(): + with st.expander(f"Improvements for {area}", expanded=True): + st.markdown(f"

{suggestions['description']}

", unsafe_allow_html=True) + + st.subheader("Specific Suggestions") + for i, suggestion in enumerate(suggestions["specific"]): + st.markdown(f'
{i+1}. {suggestion}
', unsafe_allow_html=True) + + if "before_after" in suggestions: + st.markdown('
', unsafe_allow_html=True) + + st.markdown('
', unsafe_allow_html=True) + st.markdown("Before:", unsafe_allow_html=True) + st.markdown(f"
{suggestions['before_after']['before']}
", unsafe_allow_html=True) + st.markdown('
', unsafe_allow_html=True) + + st.markdown('
', unsafe_allow_html=True) + st.markdown("After:", unsafe_allow_html=True) + st.markdown(f"
{suggestions['before_after']['after']}
", unsafe_allow_html=True) + st.markdown('
', unsafe_allow_html=True) + + st.markdown('
', unsafe_allow_html=True) + + # Add to download content + download_content += f"## Improvements for {area}\n\n" + download_content += f"{suggestions['description']}\n\n" + download_content += "### Specific Suggestions\n\n" + for i, suggestion in enumerate(suggestions["specific"]): + download_content += f"{i+1}. {suggestion}\n" + download_content += "\n" + + if "before_after" in suggestions: + download_content += "### Before\n\n" + download_content += f"```\n{suggestions['before_after']['before']}\n```\n\n" + download_content += "### After\n\n" + download_content += f"```\n{suggestions['before_after']['after']}\n```\n\n" + + # download content + download_content += "\n---\nProvided by Recruitment Agent" + + # Add download button + st.markdown("---") + report_bytes = download_content.encode() + b64 = base64.b64encode(report_bytes).decode() + href = f'πŸ“ Download All Suggestions' + st.markdown(href, unsafe_allow_html=True) + + st.markdown('
', unsafe_allow_html=True) + +def improved_resume_section(has_resume, get_improved_resume_func=None): + if not has_resume: + st.warning("Please upload and analyze a resume first.") + return + + st.markdown('
', unsafe_allow_html=True) + + target_role = st.text_input("Target role:", placeholder="e.g., Senior Software Engineer") + highlight_skills = st.text_area("Paste your JD to get updated Resume", placeholder="e.g., Python, React, Cloud Architecture") + + if st.button("Generate Improved Resume"): + if get_improved_resume_func: + with st.spinner("Creating improved resume..."): + improved_resume = get_improved_resume_func(target_role, highlight_skills) + + st.subheader("Improved Resume") + st.text_area("", improved_resume, height=400) + + # Download buttons + col1, col2 = st.columns(2) + + with col1: + # Text file download + resume_bytes = improved_resume.encode() + b64 = base64.b64encode(resume_bytes).decode() + href = f'πŸ“„ Download as TXT' + st.markdown(href, unsafe_allow_html=True) + + with col2: + # Markdown file download + md_content = f"""# {target_role if target_role else 'Professional'} Resume + +{improved_resume} + +--- +Resume enhanced by Recruitment Agent +""" + md_bytes = md_content.encode() + md_b64 = base64.b64encode(md_bytes).decode() + md_href = f'πŸ“ Download as Markdown' + st.markdown(md_href, unsafe_allow_html=True) + + st.markdown('
', unsafe_allow_html=True) + +def create_tabs(): + return st.tabs([ + "Resume Analysis", + "Resume Q&A", + "Interview Questions", + "Resume Improvement", + "Improved Resume" + ]) \ No newline at end of file