Merge remote-tracking branch 'upstream/main'

This commit is contained in:
Sri Charan Thoutam 2025-02-16 14:18:56 +05:30
commit ec15fe3a12
19 changed files with 1364 additions and 5 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@

View file

@ -17,7 +17,7 @@
# 🌟 Awesome LLM Apps
A curated collection of awesome LLM apps built with RAG and AI agents. This repository features LLM apps that use models from OpenAI, Anthropic, Google, and even open-source models like LLaMA that you can run locally on your computer.
A curated collection of awesome LLM apps built with RAG and AI agents. This repository features LLM apps that use models from OpenAI, Anthropic, Google, and open-source models like DeepSeek, Qwen or Llama that you can run locally on your computer.
<p align="center">
<a href="https://trendshift.io/repositories/9876" target="_blank">
@ -51,12 +51,14 @@ A curated collection of awesome LLM apps built with RAG and AI agents. This repo
- [👨‍🏫 AI Teaching Agent Team](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/ai_teaching_agent_team)
- [🛫 AI Travel Agent](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/ai_travel_agent)
- [🎬 AI Movie Production Agent](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/ai_movie_production_agent)
-[💻 Multimodal AI Coding Agent Team with o3-mini and Gemini](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/ai_coding_agent_o3-mini)
- [📰 Multi-Agent AI Researcher](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/multi_agent_researcher)
- [💻 Multimodal AI Coding Agent Team with o3-mini and Gemini](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/ai_coding_agent_o3-mini)
- [📑 AI Meeting Agent](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/ai_meeting_agent)
- [♜ AI Chess Agent Game](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/ai_chess_agent)
- [🏠 AI Real Estate Agent](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/ai_real_estate_agent)
- [🌐 Local News Agent OpenAI Swarm](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/local_news_agent_openai_swarm)
- [📊 AI Finance Agent with xAI Grok](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/xai_finance_agent)
- [🎮 AI 3D PyGame Visualizer with DeepSeek R1](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/ai_3dpygame_r1)
- [🧠 AI Reasoning Agent](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/ai_reasoning_agent)
- [🧬 Multimodal AI Agent](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/multimodal_ai_agent)
@ -64,6 +66,7 @@ A curated collection of awesome LLM apps built with RAG and AI agents. This repo
- [🔍 Autonomous RAG](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/rag_tutorials/autonomous_rag)
- [🔗 Agentic RAG](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/rag_tutorials/agentic_rag)
- [🤔 Agentic RAG with Gemini Flash Thinking](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/rag_tutorials/gemini_agentic_rag)
- [🐋 Deepseek Local RAG Reasoning Agent](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/rag_tutorials/deepseek_local_rag_agent)
- [🔄 Llama3.1 Local RAG](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/rag_tutorials/llama3.1_local_rag)
- [🧩 RAG-as-a-Service](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/rag_tutorials/rag-as-a-service)
- [🦙 Local RAG Agent](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/rag_tutorials/local_rag_agent)

View file

@ -7,6 +7,17 @@ import os
gpt4_model = None
def create_article_crew(topic):
"""Creates a team of agents to research, write, and edit an article on a given topic.
This function sets up a crew consisting of three agents: a researcher, a writer, and an editor.
Each agent is assigned a specific task to ensure the production of a well-researched,
well-written, and polished article. The article is formatted using markdown standards.
Args:
topic (str): The subject matter on which the article will be based.
Returns:
Crew: A crew object that contains the agents and tasks necessary to complete the article."""
# Create agents
researcher = Agent(
role='Researcher',

View file

@ -13,6 +13,18 @@ if 'SERPAPI_API_KEY' not in os.environ:
st.stop()
def get_assistant(tools):
"""Creates and returns a configured assistant agent.
This function initializes an assistant agent with a specific model and toolset.
The assistant is capable of accessing tools selected by the user and includes
additional features such as showing tool call details, running in debug mode,
and appending the current datetime to its instructions.
Args:
tools (list): A list of tools that the assistant can access.
Returns:
Agent: A configured assistant agent with specified capabilities and settings."""
return Agent(
name="llama3_assistant",
model=Ollama(id="llama3.1:8b"),

View file

@ -0,0 +1,50 @@
# 🎮 AI 3D PyGame Visualizer with DeepSeek R1
This Project demonstrates R1's code capabilities with a PyGame code generator and visualizer with browser use. The system uses DeepSeek for reasoning, OpenAI for code extraction, and browser automation agents to visualize the code on Trinket.io.
### Features
- Generates PyGame code from natural language descriptions
- Uses DeepSeek Reasoner for code logic and explanation
- Extracts clean code using OpenAI GPT-4o
- Automates code visualization on Trinket.io using browser agents
- Provides a streamlined Streamlit interface
- Multi-agent system for handling different tasks (navigation, coding, execution, viewing)
### How to get Started?
1. Clone the GitHub repository
```bash
git clone https://github.com/Shubhamsaboo/awesome-llm-apps.git
cd awesome-llm-apps/ai_agent_tutorials/ai_3dpygame_r1
```
2. Install the required dependencies:
```bash
pip install -r requirements.txt
```
3. Get your API Keys
- Sign up for [DeepSeek](https://platform.deepseek.com/) and obtain your API key
- Sign up for [OpenAI](https://platform.openai.com/) and obtain your API key
4. Run the AI PyGame Visualizer
```bash
streamlit run ai_3dpygame_r1.py
```
5. Browser use automatically opens your web browser and navigate to the URL provided in the console output to interact with the PyGame generator.
### How it works?
1. **Query Processing:** User enters a natural language description of the desired PyGame visualization.
2. **Code Generation:**
- DeepSeek Reasoner analyzes the query and provides detailed reasoning with code
- OpenAI agent extracts clean, executable code from the reasoning
3. **Visualization:**
- Browser agents automate the process of running code on Trinket.io
- Multiple specialized agents handle different tasks:
- Navigation to Trinket.io
- Code input
- Execution
- Visualization viewing
4. **User Interface:** Streamlit provides an intuitive interface for entering queries, viewing code, and managing the visualization process.

View file

@ -0,0 +1,173 @@
import streamlit as st
from openai import OpenAI
from agno.agent import Agent as AgnoAgent
from agno.models.openai import OpenAIChat as AgnoOpenAIChat
from langchain_openai import ChatOpenAI
import asyncio
from browser_use import Browser
st.set_page_config(page_title="PyGame Code Generator", layout="wide")
# Initialize session state
if "api_keys" not in st.session_state:
st.session_state.api_keys = {
"deepseek": "",
"openai": ""
}
# Streamlit sidebar for API keys
with st.sidebar:
st.title("API Keys Configuration")
st.session_state.api_keys["deepseek"] = st.text_input(
"DeepSeek API Key",
type="password",
value=st.session_state.api_keys["deepseek"]
)
st.session_state.api_keys["openai"] = st.text_input(
"OpenAI API Key",
type="password",
value=st.session_state.api_keys["openai"]
)
st.markdown("---")
st.info("""
📝 How to use:
1. Enter your API keys above
2. Write your PyGame visualization query
3. Click 'Generate Code' to get the code
4. Click 'Generate Visualization' to:
- Open Trinket.io PyGame editor
- Copy and paste the generated code
- Watch it run automatically
""")
# Main UI
st.title("🎮 AI 3D Visualizer with DeepSeek R1")
example_query = "Create a particle system simulation where 100 particles emit from the mouse position and respond to keyboard-controlled wind forces"
query = st.text_area(
"Enter your PyGame query:",
height=70,
placeholder=f"e.g.: {example_query}"
)
# Split the buttons into columns
col1, col2 = st.columns(2)
generate_code_btn = col1.button("Generate Code")
generate_vis_btn = col2.button("Generate Visualization")
if generate_code_btn and query:
if not st.session_state.api_keys["deepseek"] or not st.session_state.api_keys["openai"]:
st.error("Please provide both API keys in the sidebar")
st.stop()
# Initialize Deepseek client
deepseek_client = OpenAI(
api_key=st.session_state.api_keys["deepseek"],
base_url="https://api.deepseek.com"
)
system_prompt = """You are a Pygame and Python Expert that specializes in making games and visualisation through pygame and python programming.
During your reasoning and thinking, include clear, concise, and well-formatted Python code in your reasoning.
Always include explanations for the code you provide."""
try:
# Get reasoning from Deepseek
with st.spinner("Generating solution..."):
deepseek_response = deepseek_client.chat.completions.create(
model="deepseek-reasoner",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": query}
],
max_tokens=1
)
reasoning_content = deepseek_response.choices[0].message.reasoning_content
print("\nDeepseek Reasoning:\n", reasoning_content)
with st.expander("R1's Reasoning"):
st.write(reasoning_content)
# Initialize Claude agent (using PhiAgent)
openai_agent = AgnoAgent(
model=AgnoOpenAIChat(
id="gpt-4o",
api_key=st.session_state.api_keys["openai"]
),
show_tool_calls=True,
markdown=True
)
# Extract code
extraction_prompt = f"""Extract ONLY the Python code from the following content which is reasoning of a particular query to make a pygame script.
Return nothing but the raw code without any explanations, or markdown backticks:
{reasoning_content}"""
with st.spinner("Extracting code..."):
code_response = openai_agent.run(extraction_prompt)
extracted_code = code_response.content
# Store the generated code in session state
st.session_state.generated_code = extracted_code
# Display the code
with st.expander("Generated PyGame Code", expanded=True):
st.code(extracted_code, language="python")
st.success("Code generated successfully! Click 'Generate Visualization' to run it.")
except Exception as e:
st.error(f"An error occurred: {str(e)}")
elif generate_vis_btn:
if "generated_code" not in st.session_state:
st.warning("Please generate code first before visualization")
else:
async def run_pygame_on_trinket(code: str) -> None:
browser = Browser()
from browser_use import Agent
async with await browser.new_context() as context:
model = ChatOpenAI(
model="gpt-4o",
api_key=st.session_state.api_keys["openai"]
)
agent1 = Agent(
task='Go to https://trinket.io/features/pygame, thats your only job.',
llm=model,
browser_context=context,
)
executor = Agent(
task='Executor. Execute the code written by the User by clicking on the run button on the right. ',
llm=model,
browser_context=context
)
coder = Agent(
task='Coder. Your job is to wait for the user for 10 seconds to write the code in the code editor.',
llm=model,
browser_context=context
)
viewer = Agent(
task='Viewer. Your job is to just view the pygame window for 10 seconds.',
llm=model,
browser_context=context,
)
with st.spinner("Running code on Trinket..."):
try:
await agent1.run()
await coder.run()
await executor.run()
await viewer.run()
st.success("Code is running on Trinket!")
except Exception as e:
st.error(f"Error running code on Trinket: {str(e)}")
st.info("You can still copy the code above and run it manually on Trinket")
# Run the async function with the stored code
asyncio.run(run_pygame_on_trinket(st.session_state.generated_code))
elif generate_code_btn and not query:
st.warning("Please enter a query before generating code")

View file

@ -0,0 +1,4 @@
agno
langchain-openai
browser-use
streamlit

View file

@ -21,7 +21,7 @@ An AI Powered Streamlit application that serves as your personal coding assistan
- 30-second execution timeout protection
#### Multi-Agent Architecture
- Vision Agent (Gemini-exp-1206) for image processing
- Vision Agent (Gemini-2.0-flash) for image processing
- Coding Agent (OpenAI- o3-mini) for solution generation
- Execution Agent (OpenAI) for code running and result analysis
- E2B Sandbox for secure code execution

View file

@ -34,7 +34,7 @@ def setup_sidebar() -> None:
def create_agents() -> tuple[Agent, Agent, Agent]:
vision_agent = Agent(
model=Gemini(id="gemini-exp-1206", api_key=st.session_state.gemini_key),
model=Gemini(id="gemini-2.0-flash", api_key=st.session_state.gemini_key),
markdown=True,
)

View file

@ -0,0 +1,61 @@
## 🏠 AI Real Estate Agent - Powered by Firecrawl's Extract Endpoint
The AI Real Estate Agent automates property search and market analysis using Firecrawl's Extract endpoint and Agno AI Agent's insights. It helps users find properties matching their criteria while providing detailed location trends and investment recommendations. This agent streamlines the property search process by combining data from multiple real estate websites and offering intelligent analysis.
### Features
- **Smart Property Search**: Uses Firecrawl's Extract endpoint to find properties across multiple real estate websites
- **Multi-Source Integration**: Aggregates data from 99acres, Housing.com, Square Yards, Nobroker, and MagicBricks
- **Location Analysis**: Provides detailed price trends and investment insights for different localities
- **AI-Powered Recommendations**: Uses GPT models to analyze properties and provide structured recommendations
- **User-Friendly Interface**: Clean Streamlit UI for easy property search and results viewing
- **Customizable Search**: Filter by city, property type, category, and budget
### How to Get Started
1. **Clone the repository**:
```bash
git clone https://github.com/Shubhamsaboo/awesome-llm-apps.git
cd ai_agent_tutorials/ai_real_estate_agent
```
2. **Install the required packages**:
```bash
pip install -r requirements.txt
```
3. **Set up your API keys**:
- Get your Firecrawl API key from [Firecrawl's website](https://www.firecrawl.dev/app/api-keys)
- Get your OpenAI API key from [OpenAI's website](https://platform.openai.com/api-keys)
4. **Run the application**:
```bash
streamlit run ai_real_estate_agent.py
```
### Using the Agent
1. **Enter API Keys**:
- Input your Firecrawl and OpenAI API keys in the sidebar
- Keys are securely stored in the session state
2. **Set Search Criteria**:
- Enter the city name
- Select property category (Residential/Commercial)
- Choose property type (Flat/Individual House)
- Set maximum budget in Crores
3. **View Results**:
- Property recommendations with detailed analysis
- Location trends with investment insights
- Expandable sections for easy reading
### Features in Detail
- **Property Finding**:
- Searches across multiple real estate websites
- Returns 3-6 properties matching criteria
- Provides detailed property information and analysis
- **Location Analysis**:
- Price trends for different localities
- Rental yield analysis
- Investment potential assessment
- Top performing areas identification

View file

@ -0,0 +1,321 @@
from typing import Dict, List
from pydantic import BaseModel, Field
from agno.agent import Agent
from agno.models.openai import OpenAIChat
from firecrawl import FirecrawlApp
import streamlit as st
class PropertyData(BaseModel):
"""Schema for property data extraction"""
building_name: str = Field(description="Name of the building/property", alias="Building_name")
property_type: str = Field(description="Type of property (commercial, residential, etc)", alias="Property_type")
location_address: str = Field(description="Complete address of the property")
price: str = Field(description="Price of the property", alias="Price")
description: str = Field(description="Detailed description of the property", alias="Description")
class PropertiesResponse(BaseModel):
"""Schema for multiple properties response"""
properties: List[PropertyData] = Field(description="List of property details")
class LocationData(BaseModel):
"""Schema for location price trends"""
location: str
price_per_sqft: float
percent_increase: float
rental_yield: float
class LocationsResponse(BaseModel):
"""Schema for multiple locations response"""
locations: List[LocationData] = Field(description="List of location data points")
class FirecrawlResponse(BaseModel):
"""Schema for Firecrawl API response"""
success: bool
data: Dict
status: str
expiresAt: str
class PropertyFindingAgent:
"""Agent responsible for finding properties and providing recommendations"""
def __init__(self, firecrawl_api_key: str, openai_api_key: str, model_id: str = "o3-mini"):
self.agent = Agent(
model=OpenAIChat(id=model_id, api_key=openai_api_key),
markdown=True,
description="I am a real estate expert who helps find and analyze properties based on user preferences."
)
self.firecrawl = FirecrawlApp(api_key=firecrawl_api_key)
def find_properties(
self,
city: str,
max_price: float,
property_category: str = "Residential",
property_type: str = "Flat"
) -> str:
"""Find and analyze properties based on user preferences"""
formatted_location = city.lower()
urls = [
f"https://www.squareyards.com/sale/property-for-sale-in-{formatted_location}/*",
f"https://www.99acres.com/property-in-{formatted_location}-ffid/*",
f"https://housing.com/in/buy/{formatted_location}/{formatted_location}",
# f"https://www.nobroker.in/property/sale/{city}/{formatted_location}",
]
property_type_prompt = "Flats" if property_type == "Flat" else "Individual Houses"
raw_response = self.firecrawl.extract(
urls=urls,
params={
'prompt': f"""Extract ONLY 10 OR LESS different {property_category} {property_type_prompt} from {city} that cost less than {max_price} crores.
Requirements:
- Property Category: {property_category} properties only
- Property Type: {property_type_prompt} only
- Location: {city}
- Maximum Price: {max_price} crores
- Include complete property details with exact location
- IMPORTANT: Return data for at least 3 different properties. MAXIMUM 10.
- Format as a list of properties with their respective details
""",
'schema': PropertiesResponse.model_json_schema()
}
)
print("Raw Property Response:", raw_response)
if isinstance(raw_response, dict) and raw_response.get('success'):
properties = raw_response['data'].get('properties', [])
else:
properties = []
print("Processed Properties:", properties)
analysis = self.agent.run(
f"""As a real estate expert, analyze these properties and market trends:
Properties Found in json format:
{properties}
**IMPORTANT INSTRUCTIONS:**
1. ONLY analyze properties from the above JSON data that match the user's requirements:
- Property Category: {property_category}
- Property Type: {property_type}
- Maximum Price: {max_price} crores
2. DO NOT create new categories or property types
3. From the matching properties, select 5-6 properties with prices closest to {max_price} crores
Please provide your analysis in this format:
🏠 SELECTED PROPERTIES
List only 5-6 best matching properties with prices closest to {max_price} crores
For each property include:
- Name and Location
- Price (with value analysis)
- Key Features
- Pros and Cons
💰 BEST VALUE ANALYSIS
Compare the selected properties based on:
- Price per sq ft
- Location advantage
- Amenities offered
📍 LOCATION INSIGHTS
Specific advantages of the areas where selected properties are located
💡 RECOMMENDATIONS
Top 3 properties from the selection with reasoning
Investment potential
Points to consider before purchase
🤝 NEGOTIATION TIPS
Property-specific negotiation strategies
Format your response in a clear, structured way using the above sections.
"""
)
return analysis.content
def get_location_trends(self, city: str) -> str:
"""Get price trends for different localities in the city"""
raw_response = self.firecrawl.extract([
f"https://www.99acres.com/property-rates-and-price-trends-in-{city.lower()}-prffid/*"
], {
'prompt': """Extract price trends data for ALL major localities in the city.
IMPORTANT:
- Return data for at least 5-10 different localities
- Include both premium and affordable areas
- Do not skip any locality mentioned in the source
- Format as a list of locations with their respective data
""",
'schema': LocationsResponse.model_json_schema(),
})
if isinstance(raw_response, dict) and raw_response.get('success'):
locations = raw_response['data'].get('locations', [])
analysis = self.agent.run(
f"""As a real estate expert, analyze these location price trends for {city}:
{locations}
Please provide:
1. A bullet-point summary of the price trends for each location
2. Identify the top 3 locations with:
- Highest price appreciation
- Best rental yields
- Best value for money
3. Investment recommendations:
- Best locations for long-term investment
- Best locations for rental income
- Areas showing emerging potential
4. Specific advice for investors based on these trends
Format the response as follows:
📊 LOCATION TRENDS SUMMARY
[Bullet points for each location]
🏆 TOP PERFORMING AREAS
[Bullet points for best areas]
💡 INVESTMENT INSIGHTS
[Bullet points with investment advice]
🎯 RECOMMENDATIONS
[Bullet points with specific recommendations]
"""
)
return analysis.content
return "No price trends data available"
def create_property_agent():
"""Create PropertyFindingAgent with API keys from session state"""
if 'property_agent' not in st.session_state:
st.session_state.property_agent = PropertyFindingAgent(
firecrawl_api_key=st.session_state.firecrawl_key,
openai_api_key=st.session_state.openai_key,
model_id=st.session_state.model_id
)
def main():
st.set_page_config(
page_title="AI Real Estate Agent",
page_icon="🏠",
layout="wide"
)
with st.sidebar:
st.title("🔑 API Configuration")
st.subheader("🤖 Model Selection")
model_id = st.selectbox(
"Choose OpenAI Model",
options=["o3-mini", "gpt-4o"],
help="Select the AI model to use. Choose gpt-4o if your api doesn't have access to o3-mini"
)
st.session_state.model_id = model_id
st.divider()
st.subheader("🔐 API Keys")
firecrawl_key = st.text_input(
"Firecrawl API Key",
type="password",
help="Enter your Firecrawl API key"
)
openai_key = st.text_input(
"OpenAI API Key",
type="password",
help="Enter your OpenAI API key"
)
if firecrawl_key and openai_key:
st.session_state.firecrawl_key = firecrawl_key
st.session_state.openai_key = openai_key
create_property_agent()
st.title("🏠 AI Real Estate Agent")
st.info(
"""
Welcome to the AI Real Estate Agent!
Enter your search criteria below to get property recommendations
and location insights.
"""
)
col1, col2 = st.columns(2)
with col1:
city = st.text_input(
"City",
placeholder="Enter city name (e.g., Bangalore)",
help="Enter the city where you want to search for properties"
)
property_category = st.selectbox(
"Property Category",
options=["Residential", "Commercial"],
help="Select the type of property you're interested in"
)
with col2:
max_price = st.number_input(
"Maximum Price (in Crores)",
min_value=0.1,
max_value=100.0,
value=5.0,
step=0.1,
help="Enter your maximum budget in Crores"
)
property_type = st.selectbox(
"Property Type",
options=["Flat", "Individual House"],
help="Select the specific type of property"
)
if st.button("🔍 Start Search", use_container_width=True):
if 'property_agent' not in st.session_state:
st.error("⚠️ Please enter your API keys in the sidebar first!")
return
if not city:
st.error("⚠️ Please enter a city name!")
return
try:
with st.spinner("🔍 Searching for properties..."):
property_results = st.session_state.property_agent.find_properties(
city=city,
max_price=max_price,
property_category=property_category,
property_type=property_type
)
st.success("✅ Property search completed!")
st.subheader("🏘️ Property Recommendations")
st.markdown(property_results)
st.divider()
with st.spinner("📊 Analyzing location trends..."):
location_trends = st.session_state.property_agent.get_location_trends(city)
st.success("✅ Location analysis completed!")
with st.expander("📈 Location Trends Analysis of the city"):
st.markdown(location_trends)
except Exception as e:
st.error(f"❌ An error occurred: {str(e)}")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,4 @@
agno
firecrawl-py==1.9.0
pydantic
streamlit

View file

@ -29,6 +29,17 @@ if all(api_keys.values()):
search_query = st.text_input("Research paper search query")
def process_with_gpt4(result):
"""Processes an arXiv search result to produce a structured markdown output.
This function takes a search result from arXiv and generates a markdown-formatted
table containing details about each paper. The table includes columns for the
paper's title, authors, a brief abstract, and a link to the paper on arXiv.
Args:
result (str): The raw search result from arXiv, typically in a text format.
Returns:
str: A markdown-formatted string containing a table with paper details."""
prompt = f"""
Based on the following arXiv search result, provide a proper structured output in markdown that is readable by the users.
Each paper should have a title, authors, abstract, and link.

View file

@ -6,4 +6,5 @@ pgvector
requests
sqlalchemy
pypdf
duckduckgo-search
duckduckgo-search
nest_asyncio

View file

@ -0,0 +1,84 @@
# 🐋 Deepseek Local RAG Reasoning Agent
A powerful reasoning agent that combines local Deepseek models with RAG capabilities. Built using Deepseek (via Ollama), Snowflake for embeddings, Qdrant for vector storage, and Agno for agent orchestration, this application offers both simple local chat and advanced RAG-enhanced interactions with comprehensive document processing and web search capabilities.
## Features
- **Dual Operation Modes**
- Local Chat Mode: Direct interaction with Deepseek locally
- RAG Mode: Enhanced reasoning with document context and web search integration - llama3.2
- **Document Processing** (RAG Mode)
- PDF document upload and processing
- Web page content extraction
- Automatic text chunking and embedding
- Vector storage in Qdrant cloud
- **Intelligent Querying** (RAG Mode)
- RAG-based document retrieval
- Similarity search with threshold filtering
- Automatic fallback to web search
- Source attribution for answers
- **Advanced Capabilities**
- Exa AI web search integration
- Custom domain filtering for web search
- Context-aware response generation
- Chat history management
- Thinking process visualization
- **Model Specific Features**
- Flexible model selection:
- Deepseek r1 1.5b (lighter, suitable for most laptops)
- Deepseek r1 7b (more capable, requires better hardware)
- Snowflake Arctic Embedding model (SOTA) for vector embeddings
- Agno Agent framework for orchestration
- Streamlit-based interactive interface
## Prerequisites
### 1. Ollama Setup
1. Install [Ollama](https://ollama.ai)
2. Pull the Deepseek r1 model(s):
```bash
# For the lighter model
ollama pull deepseek-r1:1.5b
# For the more capable model (if your hardware supports it)
ollama pull deepseek-r1:7b
ollama pull snowflake-arctic-embed
ollama pull llama3.2
```
### 2. Qdrant Cloud Setup (for RAG Mode)
1. Visit [Qdrant Cloud](https://cloud.qdrant.io/)
2. Create an account or sign in
3. Create a new cluster
4. Get your credentials:
- Qdrant API Key: Found in API Keys section
- Qdrant URL: Your cluster URL (format: `https://xxx-xxx.cloud.qdrant.io`)
### 3. Exa AI API Key (Optional)
1. Visit [Exa AI](https://exa.ai)
2. Sign up for an account
3. Generate an API key for web search capabilities
## How to Run
1. Clone the repository:
```bash
git clone https://github.com/Shubhamsaboo/awesome-llm-apps.git
cd rag_tutorials/deepseek_local_rag_agent
```
2. Install dependencies:
```bash
pip install -r requirements.txt
```
3. Run the application:
```bash
streamlit run deepseek_rag_agent.py
```

View file

@ -0,0 +1,526 @@
import os
import tempfile
from datetime import datetime
from typing import List
import streamlit as st
import bs4
from agno.agent import Agent
from agno.models.ollama import Ollama
from langchain_community.document_loaders import PyPDFLoader, WebBaseLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_qdrant import QdrantVectorStore
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams
from langchain_core.embeddings import Embeddings
from agno.tools.exa import ExaTools
from agno.embedder.ollama import OllamaEmbedder
class OllamaEmbedderr(Embeddings):
def __init__(self, model_name="snowflake-arctic-embed"):
"""
Initialize the OllamaEmbedderr with a specific model.
Args:
model_name (str): The name of the model to use for embedding.
"""
self.embedder = OllamaEmbedder(id=model_name, dimensions=1024)
def embed_documents(self, texts: List[str]) -> List[List[float]]:
return [self.embed_query(text) for text in texts]
def embed_query(self, text: str) -> List[float]:
return self.embedder.get_embedding(text)
# Constants
COLLECTION_NAME = "test-deepseek-r1"
# Streamlit App Initialization
st.title("🐋 Deepseek Local RAG Reasoning Agent")
# Session State Initialization
if 'google_api_key' not in st.session_state:
st.session_state.google_api_key = ""
if 'qdrant_api_key' not in st.session_state:
st.session_state.qdrant_api_key = ""
if 'qdrant_url' not in st.session_state:
st.session_state.qdrant_url = ""
if 'model_version' not in st.session_state:
st.session_state.model_version = "deepseek-r1:1.5b" # Default to lighter model
if 'vector_store' not in st.session_state:
st.session_state.vector_store = None
if 'processed_documents' not in st.session_state:
st.session_state.processed_documents = []
if 'history' not in st.session_state:
st.session_state.history = []
if 'exa_api_key' not in st.session_state:
st.session_state.exa_api_key = ""
if 'use_web_search' not in st.session_state:
st.session_state.use_web_search = False
if 'force_web_search' not in st.session_state:
st.session_state.force_web_search = False
if 'similarity_threshold' not in st.session_state:
st.session_state.similarity_threshold = 0.7
if 'rag_enabled' not in st.session_state:
st.session_state.rag_enabled = True # RAG is enabled by default
# Sidebar Configuration
st.sidebar.header("🤖 Agent Configuration")
# Model Selection
st.sidebar.header("📦 Model Selection")
model_help = """
- 1.5b: Lighter model, suitable for most laptops
- 7b: More capable but requires better GPU/RAM
Choose based on your hardware capabilities.
"""
st.session_state.model_version = st.sidebar.radio(
"Select Model Version",
options=["deepseek-r1:1.5b", "deepseek-r1:7b"],
help=model_help
)
st.sidebar.info("Run ollama pull deepseek-r1:7b or deepseek-r1:1.5b respectively")
# RAG Mode Toggle
st.sidebar.header("🔍 RAG Configuration")
st.session_state.rag_enabled = st.sidebar.toggle("Enable RAG Mode", value=st.session_state.rag_enabled)
# Clear Chat Button
if st.sidebar.button("🗑️ Clear Chat History"):
st.session_state.history = []
st.rerun()
# Show API Configuration only if RAG is enabled
if st.session_state.rag_enabled:
st.sidebar.header("🔑 API Configuration")
qdrant_api_key = st.sidebar.text_input("Qdrant API Key", type="password", value=st.session_state.qdrant_api_key)
qdrant_url = st.sidebar.text_input("Qdrant URL",
placeholder="https://your-cluster.cloud.qdrant.io:6333",
value=st.session_state.qdrant_url)
# Update session state
st.session_state.qdrant_api_key = qdrant_api_key
st.session_state.qdrant_url = qdrant_url
# Search Configuration (only shown in RAG mode)
st.sidebar.header("🎯 Search Configuration")
st.session_state.similarity_threshold = st.sidebar.slider(
"Document Similarity Threshold",
min_value=0.0,
max_value=1.0,
value=0.7,
help="Lower values will return more documents but might be less relevant. Higher values are more strict."
)
# Add in the sidebar configuration section, after the existing API inputs
st.sidebar.header("🌐 Web Search Configuration")
st.session_state.use_web_search = st.sidebar.checkbox("Enable Web Search Fallback", value=st.session_state.use_web_search)
if st.session_state.use_web_search:
exa_api_key = st.sidebar.text_input(
"Exa AI API Key",
type="password",
value=st.session_state.exa_api_key,
help="Required for web search fallback when no relevant documents are found"
)
st.session_state.exa_api_key = exa_api_key
# Optional domain filtering
default_domains = ["arxiv.org", "wikipedia.org", "github.com", "medium.com"]
custom_domains = st.sidebar.text_input(
"Custom domains (comma-separated)",
value=",".join(default_domains),
help="Enter domains to search from, e.g.: arxiv.org,wikipedia.org"
)
search_domains = [d.strip() for d in custom_domains.split(",") if d.strip()]
# Search Configuration moved inside RAG mode check
# Utility Functions
def init_qdrant() -> QdrantClient | None:
"""Initialize Qdrant client with configured settings.
Returns:
QdrantClient: The initialized Qdrant client if successful.
None: If the initialization fails.
"""
if not all([st.session_state.qdrant_api_key, st.session_state.qdrant_url]):
return None
try:
return QdrantClient(
url=st.session_state.qdrant_url,
api_key=st.session_state.qdrant_api_key,
timeout=60
)
except Exception as e:
st.error(f"🔴 Qdrant connection failed: {str(e)}")
return None
# Document Processing Functions
def process_pdf(file) -> List:
"""Process PDF file and add source metadata."""
try:
with tempfile.NamedTemporaryFile(delete=False, suffix='.pdf') as tmp_file:
tmp_file.write(file.getvalue())
loader = PyPDFLoader(tmp_file.name)
documents = loader.load()
# Add source metadata
for doc in documents:
doc.metadata.update({
"source_type": "pdf",
"file_name": file.name,
"timestamp": datetime.now().isoformat()
})
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200
)
return text_splitter.split_documents(documents)
except Exception as e:
st.error(f"📄 PDF processing error: {str(e)}")
return []
def process_web(url: str) -> List:
"""Process web URL and add source metadata."""
try:
loader = WebBaseLoader(
web_paths=(url,),
bs_kwargs=dict(
parse_only=bs4.SoupStrainer(
class_=("post-content", "post-title", "post-header", "content", "main")
)
)
)
documents = loader.load()
# Add source metadata
for doc in documents:
doc.metadata.update({
"source_type": "url",
"url": url,
"timestamp": datetime.now().isoformat()
})
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200
)
return text_splitter.split_documents(documents)
except Exception as e:
st.error(f"🌐 Web processing error: {str(e)}")
return []
# Vector Store Management
def create_vector_store(client, texts):
"""Create and initialize vector store with documents."""
try:
# Create collection if needed
try:
client.create_collection(
collection_name=COLLECTION_NAME,
vectors_config=VectorParams(
size=1024,
distance=Distance.COSINE
)
)
st.success(f"📚 Created new collection: {COLLECTION_NAME}")
except Exception as e:
if "already exists" not in str(e).lower():
raise e
# Initialize vector store
vector_store = QdrantVectorStore(
client=client,
collection_name=COLLECTION_NAME,
embedding=OllamaEmbedderr()
)
# Add documents
with st.spinner('📤 Uploading documents to Qdrant...'):
vector_store.add_documents(texts)
st.success("✅ Documents stored successfully!")
return vector_store
except Exception as e:
st.error(f"🔴 Vector store error: {str(e)}")
return None
def get_web_search_agent() -> Agent:
"""Initialize a web search agent."""
return Agent(
name="Web Search Agent",
model=Ollama(id="llama3.2"),
tools=[ExaTools(
api_key=st.session_state.exa_api_key,
include_domains=search_domains,
num_results=5
)],
instructions="""You are a web search expert. Your task is to:
1. Search the web for relevant information about the query
2. Compile and summarize the most relevant information
3. Include sources in your response
""",
show_tool_calls=True,
markdown=True,
)
def get_rag_agent() -> Agent:
"""Initialize the main RAG agent."""
return Agent(
name="DeepSeek RAG Agent",
model=Ollama(id=st.session_state.model_version),
instructions="""You are an Intelligent Agent specializing in providing accurate answers.
When asked a question:
- Analyze the question and answer the question with what you know.
When given context from documents:
- Focus on information from the provided documents
- Be precise and cite specific details
When given web search results:
- Clearly indicate that the information comes from web search
- Synthesize the information clearly
Always maintain high accuracy and clarity in your responses.
""",
show_tool_calls=True,
markdown=True,
)
def check_document_relevance(query: str, vector_store, threshold: float = 0.7) -> tuple[bool, List]:
if not vector_store:
return False, []
retriever = vector_store.as_retriever(
search_type="similarity_score_threshold",
search_kwargs={"k": 5, "score_threshold": threshold}
)
docs = retriever.invoke(query)
return bool(docs), docs
chat_col, toggle_col = st.columns([0.9, 0.1])
with chat_col:
prompt = st.chat_input("Ask about your documents..." if st.session_state.rag_enabled else "Ask me anything...")
with toggle_col:
st.session_state.force_web_search = st.toggle('🌐', help="Force web search")
# Check if RAG is enabled
if st.session_state.rag_enabled:
qdrant_client = init_qdrant()
# File/URL Upload Section
st.sidebar.header("📁 Data Upload")
uploaded_file = st.sidebar.file_uploader("Upload PDF", type=["pdf"])
web_url = st.sidebar.text_input("Or enter URL")
# Process documents
if uploaded_file:
file_name = uploaded_file.name
if file_name not in st.session_state.processed_documents:
with st.spinner('Processing PDF...'):
texts = process_pdf(uploaded_file)
if texts and qdrant_client:
if st.session_state.vector_store:
st.session_state.vector_store.add_documents(texts)
else:
st.session_state.vector_store = create_vector_store(qdrant_client, texts)
st.session_state.processed_documents.append(file_name)
st.success(f"✅ Added PDF: {file_name}")
if web_url:
if web_url not in st.session_state.processed_documents:
with st.spinner('Processing URL...'):
texts = process_web(web_url)
if texts and qdrant_client:
if st.session_state.vector_store:
st.session_state.vector_store.add_documents(texts)
else:
st.session_state.vector_store = create_vector_store(qdrant_client, texts)
st.session_state.processed_documents.append(web_url)
st.success(f"✅ Added URL: {web_url}")
# Display sources in sidebar
if st.session_state.processed_documents:
st.sidebar.header("📚 Processed Sources")
for source in st.session_state.processed_documents:
if source.endswith('.pdf'):
st.sidebar.text(f"📄 {source}")
else:
st.sidebar.text(f"🌐 {source}")
if prompt:
# Add user message to history
st.session_state.history.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.write(prompt)
if st.session_state.rag_enabled:
# Existing RAG flow remains unchanged
with st.spinner("🤔Evaluating the Query..."):
try:
rewritten_query = prompt
with st.expander("Evaluating the query"):
st.write(f"User's Prompt: {prompt}")
except Exception as e:
st.error(f"❌ Error rewriting query: {str(e)}")
rewritten_query = prompt
# Step 2: Choose search strategy based on force_web_search toggle
context = ""
docs = []
if not st.session_state.force_web_search and st.session_state.vector_store:
# Try document search first
retriever = st.session_state.vector_store.as_retriever(
search_type="similarity_score_threshold",
search_kwargs={
"k": 5,
"score_threshold": st.session_state.similarity_threshold
}
)
docs = retriever.invoke(rewritten_query)
if docs:
context = "\n\n".join([d.page_content for d in docs])
st.info(f"📊 Found {len(docs)} relevant documents (similarity > {st.session_state.similarity_threshold})")
elif st.session_state.use_web_search:
st.info("🔄 No relevant documents found in database, falling back to web search...")
# Step 3: Use web search if:
# 1. Web search is forced ON via toggle, or
# 2. No relevant documents found AND web search is enabled in settings
if (st.session_state.force_web_search or not context) and st.session_state.use_web_search and st.session_state.exa_api_key:
with st.spinner("🔍 Searching the web..."):
try:
web_search_agent = get_web_search_agent()
web_results = web_search_agent.run(rewritten_query).content
if web_results:
context = f"Web Search Results:\n{web_results}"
if st.session_state.force_web_search:
st.info(" Using web search as requested via toggle.")
else:
st.info(" Using web search as fallback since no relevant documents were found.")
except Exception as e:
st.error(f"❌ Web search error: {str(e)}")
# Step 4: Generate response using the RAG agent
with st.spinner("🤖 Thinking..."):
try:
rag_agent = get_rag_agent()
if context:
full_prompt = f"""Context: {context}
Original Question: {prompt}
Please provide a comprehensive answer based on the available information."""
else:
full_prompt = f"Original Question: {prompt}\n"
st.info(" No relevant information found in documents or web search.")
response = rag_agent.run(full_prompt)
# Add assistant response to history
st.session_state.history.append({
"role": "assistant",
"content": response.content
})
# Display assistant response
with st.chat_message("assistant"):
st.write(response.content)
# Show sources if available
if not st.session_state.force_web_search and 'docs' in locals() and docs:
with st.expander("🔍 See document sources"):
for i, doc in enumerate(docs, 1):
source_type = doc.metadata.get("source_type", "unknown")
source_icon = "📄" if source_type == "pdf" else "🌐"
source_name = doc.metadata.get("file_name" if source_type == "pdf" else "url", "unknown")
st.write(f"{source_icon} Source {i} from {source_name}:")
st.write(f"{doc.page_content[:200]}...")
except Exception as e:
st.error(f"❌ Error generating response: {str(e)}")
else:
# Simple mode without RAG
with st.spinner("🤖 Thinking..."):
try:
rag_agent = get_rag_agent()
web_search_agent = get_web_search_agent() if st.session_state.use_web_search else None
# Handle web search if forced or enabled
context = ""
if st.session_state.force_web_search and web_search_agent:
with st.spinner("🔍 Searching the web..."):
try:
web_results = web_search_agent.run(prompt).content
if web_results:
context = f"Web Search Results:\n{web_results}"
st.info(" Using web search as requested.")
except Exception as e:
st.error(f"❌ Web search error: {str(e)}")
# Generate response
if context:
full_prompt = f"""Context: {context}
Question: {prompt}
Please provide a comprehensive answer based on the available information."""
else:
full_prompt = prompt
response = rag_agent.run(full_prompt)
response_content = response.content
# Extract thinking process and final response
import re
think_pattern = r'<think>(.*?)</think>'
think_match = re.search(think_pattern, response_content, re.DOTALL)
if think_match:
thinking_process = think_match.group(1).strip()
final_response = re.sub(think_pattern, '', response_content, flags=re.DOTALL).strip()
else:
thinking_process = None
final_response = response_content
# Add assistant response to history (only the final response)
st.session_state.history.append({
"role": "assistant",
"content": final_response
})
# Display assistant response
with st.chat_message("assistant"):
if thinking_process:
with st.expander("🤔 See thinking process"):
st.markdown(thinking_process)
st.markdown(final_response)
except Exception as e:
st.error(f"❌ Error generating response: {str(e)}")
else:
st.warning("You can directly talk to r1 locally! Toggle the RAG mode to upload documents!")

View file

@ -0,0 +1,7 @@
agno
exa==0.5.26
qdrant-client==1.12.1
langchain-qdrant==0.2.0
langchain-community==0.3.13
streamlit==1.41.1
ollama

View file

@ -21,6 +21,23 @@ Instead, you MUST treat the context as if its contents are entirely part of your
""".strip()
def initialize_config(openai_key: str, anthropic_key: str, cohere_key: str, db_url: str) -> RAGLiteConfig:
"""Initializes and returns a RAGLiteConfig object with the specified API keys and database URL.
This function sets the provided API keys in the environment variables and returns a
RAGLiteConfig object configured with the given database URL and pre-defined settings for
language model, embedder, and reranker.
Args:
openai_key (str): The API key for OpenAI services.
anthropic_key (str): The API key for Anthropic services.
cohere_key (str): The API key for Cohere services.
db_url (str): The database URL for connecting to the desired data source.
Returns:
RAGLiteConfig: A configuration object initialized with the specified parameters.
Raises:
ValueError: If there is an issue setting up the configuration, an error is raised with details."""
try:
os.environ["OPENAI_API_KEY"] = openai_key
os.environ["ANTHROPIC_API_KEY"] = anthropic_key
@ -39,6 +56,17 @@ def initialize_config(openai_key: str, anthropic_key: str, cohere_key: str, db_u
raise ValueError(f"Configuration error: {e}")
def process_document(file_path: str) -> bool:
"""Processes a document by inserting it into a system with a given configuration.
This function checks if a configuration is initialized in the session state.
If the configuration is present, it attempts to insert the document located
at the given file path using this configuration.
Args:
file_path (str): The path to the document to be processed.
Returns:
bool: True if the document was successfully processed; False otherwise."""
try:
if not st.session_state.get('my_config'):
raise ValueError("Configuration not initialized")
@ -49,6 +77,18 @@ def process_document(file_path: str) -> bool:
return False
def perform_search(query: str) -> List[dict]:
"""Conducts a hybrid search and returns a list of ranked chunks based on the query.
This function performs a search using a hybrid search method, retrieves the relevant
chunks, and reranks them according to the query. It handles any exceptions that occur
during the process and logs the errors.
Args:
query (str): The search query string.
Returns:
List[dict]: A list of dictionaries representing the ranked chunks. Returns an
empty list if no results are found or if an error occurs."""
try:
chunk_ids, scores = hybrid_search(query, num_results=10, config=st.session_state.my_config)
if not chunk_ids:

View file

@ -20,9 +20,29 @@ db = Chroma(collection_name="pharma_database",
persist_directory='./pharma_db')
def format_docs(docs):
"""Formats a list of document objects into a single string.
Args:
docs (list): A list of document objects, each having a 'page_content' attribute.
Returns:
str: A single string containing the page content from each document,
separated by double newlines."""
return "\n\n".join(doc.page_content for doc in docs)
def add_to_db(uploaded_files):
"""Processes and adds uploaded PDF files to the database.
This function checks if any files have been uploaded. If files are uploaded,
it saves each file to a temporary location, processes the content using a PDF loader,
and splits the content into smaller chunks. Each chunk, along with its metadata,
is then added to the database. Temporary files are removed after processing.
Args:
uploaded_files (list): A list of uploaded file objects to be processed.
Returns:
None"""
# Check if files are uploaded
if not uploaded_files:
st.error("No files uploaded!")
@ -59,6 +79,18 @@ def add_to_db(uploaded_files):
os.remove(temp_file_path)
def run_rag_chain(query):
"""Processes a query using a Retrieval-Augmented Generation (RAG) chain.
This function utilizes a RAG chain to answer a given query. It retrieves
relevant context using similarity search and then generates a response
based on this context using a chat model. The chat model is pre-configured
with a prompt template specialized in pharmaceutical sciences.
Args:
query (str): The user's question that needs to be answered.
Returns:
str: A response generated by the chat model, based on the retrieved context."""
# Create a Retriever Object and apply Similarity Search
retriever = db.as_retriever(search_type="similarity", search_kwargs={'k': 5})
@ -98,6 +130,24 @@ def run_rag_chain(query):
return response
def main():
"""Initialize and manage the PharmaQuery application interface.
This function sets up the Streamlit application interface for PharmaQuery,
a Pharmaceutical Insight Retrieval System. Users can enter queries related
to the pharmaceutical industry, upload research documents, and manage API
keys for enhanced functionality.
The main features include:
- Query input area for users to ask questions about the pharmaceutical industry.
- Submission button to process the query and display the retrieved insights.
- Sidebar for API key input and management.
- File uploader for adding research documents to the database, enhancing query responses.
Args:
None
Returns:
None"""
st.set_page_config(page_title="PharmaQuery", page_icon=":microscope:")
st.header("Pharmaceutical Insight Retrieval System")