Merge remote-tracking branch 'upstream/main'
This commit is contained in:
commit
ec15fe3a12
19 changed files with 1364 additions and 5 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
|
|
|
|||
50
ai_agent_tutorials/ai_3dpygame_r1/README.md
Normal file
50
ai_agent_tutorials/ai_3dpygame_r1/README.md
Normal 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.
|
||||
173
ai_agent_tutorials/ai_3dpygame_r1/ai_3dpygame_r1.py
Normal file
173
ai_agent_tutorials/ai_3dpygame_r1/ai_3dpygame_r1.py
Normal 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")
|
||||
4
ai_agent_tutorials/ai_3dpygame_r1/requirements.txt
Normal file
4
ai_agent_tutorials/ai_3dpygame_r1/requirements.txt
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
agno
|
||||
langchain-openai
|
||||
browser-use
|
||||
streamlit
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
||||
|
|
|
|||
61
ai_agent_tutorials/ai_real_estate_agent/README.md
Normal file
61
ai_agent_tutorials/ai_real_estate_agent/README.md
Normal 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
|
||||
|
||||
321
ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py
Normal file
321
ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py
Normal 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()
|
||||
4
ai_agent_tutorials/ai_real_estate_agent/requirements.txt
Normal file
4
ai_agent_tutorials/ai_real_estate_agent/requirements.txt
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
agno
|
||||
firecrawl-py==1.9.0
|
||||
pydantic
|
||||
streamlit
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -6,4 +6,5 @@ pgvector
|
|||
requests
|
||||
sqlalchemy
|
||||
pypdf
|
||||
duckduckgo-search
|
||||
duckduckgo-search
|
||||
nest_asyncio
|
||||
|
|
|
|||
84
rag_tutorials/deepseek_local_rag_agent/README.md
Normal file
84
rag_tutorials/deepseek_local_rag_agent/README.md
Normal 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
|
||||
```
|
||||
|
||||
526
rag_tutorials/deepseek_local_rag_agent/deepseek_rag_agent.py
Normal file
526
rag_tutorials/deepseek_local_rag_agent/deepseek_rag_agent.py
Normal 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!")
|
||||
7
rag_tutorials/deepseek_local_rag_agent/requirements.txt
Normal file
7
rag_tutorials/deepseek_local_rag_agent/requirements.txt
Normal 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
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue