1- import ollama
21import json
32import os
43import re
54from dotenv import load_dotenv
5+ from langchain_ollama import OllamaLLM
66from mock_database import search_flights , check_ollama_availability
77
88# Load environment variables
99load_dotenv ()
10- OLLAMA_MODEL = os .getenv ("OLLAMA_MODEL" , "llama2:latest" ) # Load model dynamically
11-
10+ OLLAMA_MODEL = os .getenv ("OLLAMA_MODEL" , "llama2:latest" )
1211OLLAMA_AVAILABLE = check_ollama_availability ()
1312
14- def extract_entities_llama2 (query ):
13+ # Initialize Ollama LLM
14+ def initialize_ollama ():
15+ """Initialize the Ollama LLM model safely."""
16+ try :
17+ ollama_llm = OllamaLLM (model = OLLAMA_MODEL )
18+ print (f"🟢 Successfully initialized Ollama LLM with model: { OLLAMA_MODEL } " )
19+ return ollama_llm
20+ except Exception as e :
21+ print (f"❌ Failed to initialize Ollama LLM: { str (e )} " )
22+ return None
23+
24+ ollama_llm = initialize_ollama ()
25+
26+ CITY_MAPPING = {
27+ "ny" : "New York" ,
28+ "la" : "Los Angeles" ,
29+ "ch" : "Chicago" ,
30+ "sf" : "San Francisco" ,
31+ "mi" : "Miami"
32+ }
33+
34+ def extract_entities_ollama (query ):
1535 """
16- Uses Ollama to extract structured flight details from a query.
17- Ensures the response is always valid JSON .
36+ Uses Ollama to extract structured flight details from a query and ensures correct data mapping .
37+ If Ollama fails to extract an entity, fallback to a keyword-based search .
1838 """
19- if not OLLAMA_AVAILABLE :
39+ if not OLLAMA_AVAILABLE or not ollama_llm :
2040 print ("⚠️ Ollama server is unavailable. Using basic keyword search." )
2141 return {}
2242
2343 print (f"🟢 Using Ollama model: { OLLAMA_MODEL } " )
24- prompt = f'''
25- Extract flight details from this user query and respond ** only** in valid JSON.
26- Do not include any extra text, explanations , or markdown.
27-
44+ prompt = f"""
45+ Extract flight details from the following user query and return only valid JSON.
46+ Do not include any explanations, additional text , or markdown.
47+
2848 Query: "{ query } "
29-
30- Example valid responses:
31- {{"origin": "New York", "destination": "London", "flight_number": "AA123", "time": "10:00 AM", "date": "2025-05-01", "airline": "Global Airways"}}
32- {{"origin": "Chicago", "destination": null, "flight_number": null, "time": null, "date": null, "airline": null}}
49+
50+ The response should be in this exact JSON format:
51+ {{
52+ "origin": "City Name",
53+ "destination": "City Name",
54+ "flight_number": "Flight Number",
55+ "date": "YYYY-MM-DD",
56+ "airline": "Airline Name"
57+ }}
3358
3459 If a value is missing, set it to `null`.
35-
36- '''
60+ """
3761
3862 try :
39- print (f"🟢 Sending request to Ollama with model: { OLLAMA_MODEL } " )
40- response = ollama .chat (model = OLLAMA_MODEL .strip (), messages = [{"role" : "user" , "content" : prompt }])
41- content = response ["message" ]["content" ].strip ()
63+ print (f"🟢 Sending request to Ollama for entity extraction..." )
64+ response = ollama_llm .invoke (prompt )
4265
43- # 🔍 Extract the valid JSON from the response
44- json_match = re .search (r"\{.*\}" , content , re .DOTALL )
66+ # Extract valid JSON from response
67+ json_match = re .search (r"\{.*\}" , response , re .DOTALL )
4568 if json_match :
46- json_str = json_match .group (0 ) # Extract JSON part
47- extracted = json .loads (json_str ) # Convert to dictionary
69+ json_str = json_match .group (0 )
70+ extracted = json .loads (json_str )
4871
49- # 🛠️ Clean extracted data (remove placeholders)
50- extracted_clean = {k : v for k , v in extracted .items () if v and v .lower () not in ["null" , "none" ]}
72+ # ✅ Clean extracted values
73+ if extracted .get ("destination" ) == "City Name" :
74+ extracted ["destination" ] = None # Ignore placeholder values
75+ if extracted .get ("origin" ) == "NYC" :
76+ extracted ["origin" ] = "New York" # Fix Ollama misinterpretation
5177
52- print (f"🟢 Extracted Entities: { extracted_clean } " )
78+ # ✅ If no flight number is extracted, try regex
79+ if not extracted .get ("flight_number" ):
80+ extracted ["flight_number" ] = extract_flight_number (query )
81+
82+ extracted_clean = {k : v for k , v in extracted .items () if v } # Remove None values
83+ print (f"🟢 Extracted Entities from Ollama: { extracted_clean } " )
5384 return extracted_clean
5485
5586 else :
56- print (f"⚠️ No valid JSON found in response. Raw response: { content } " )
57- return {}
87+ print (f"⚠️ No valid JSON found in response. Falling back to keyword search. " )
88+ return extract_entities_from_keywords ( query )
5889
5990 except json .JSONDecodeError as jde :
60- print (f"⚠️ JSONDecodeError: { jde } . Raw response: { content } " )
61- return {}
91+ print (f"⚠️ JSONDecodeError: { jde } . Falling back to keyword search. " )
92+ return extract_entities_from_keywords ( query )
6293 except Exception as e :
63- print (f"⚠️ Error during entity extraction: { e } " )
64- return {}
94+ print (f"⚠️ Error during entity extraction: { e } . Falling back to keyword search." )
95+ return extract_entities_from_keywords (query )
96+
97+ def extract_flight_number (query ):
98+ """
99+ Extracts a flight number from a query using regex.
100+ Flight numbers typically have a two-letter airline code followed by 3-4 digits (e.g., "NY100").
101+ """
102+ match = re .search (r"\b[A-Z]{2}\d{3,4}\b" , query )
103+ return match .group (0 ) if match else None
104+
105+
106+ def extract_entities_from_keywords (query ):
107+ """
108+ Fallback function to extract entities from a query using simple keyword matching.
109+ """
110+ keywords = query .lower ()
111+
112+ cities = ["new york" , "los angeles" , "chicago" , "san francisco" , "miami" , "paris" , "tokyo" , "london" , "rio de janeiro" , "sydney" ]
113+ airlines = ["global airways" , "pacific routes" , "euro connect" , "ocean pacific" , "south american airways" ]
114+
115+ origin = next ((city for city in cities if city in keywords ), None )
116+ destination = None # Can't always determine destination reliably from keywords
117+ flight_number_match = re .search (r"\b[A-Z]{2}\d{3,4}\b" , query ) # Match flight numbers like "NY100"
118+ flight_number = flight_number_match .group (0 ) if flight_number_match else None
119+ airline = next ((air for air in airlines if air in keywords ), None )
120+
121+ extracted = {
122+ "origin" : origin ,
123+ "destination" : destination ,
124+ "flight_number" : flight_number ,
125+ "airline" : airline
126+ }
127+
128+ extracted_clean = {k : v for k , v in extracted .items () if v } # Remove None values
129+ print (f"🟢 Extracted Entities from Keywords: { extracted_clean } " )
130+ return extracted_clean
65131
66132
67133def process_query (query ):
68134 """
69135 Process user query and return relevant flight information.
70- Uses keyword search if Ollama is unavailable .
136+ Uses Ollama for entity extraction instead of Transformers .
71137 """
72138 try :
73139 print (f"🟢 Processing query: { query } " )
74140
75- # Extract structured entities (if Ollama is available)
76- search_params = extract_entities_llama2 (query ) if OLLAMA_AVAILABLE else {}
141+ # Extract structured entities using Ollama
142+ search_params = extract_entities_ollama (query )
77143
78- # Perform a keyword-based search instead of semantic search
79- if search_params :
80- print (f"🟢 Searching with extracted parameters: { search_params } " )
81- matching_flights = search_flights (search_params .get ("origin" , "" ) or search_params .get ("destination" , "" ))
82- else :
83- print ("⚠️ No structured entities found. Performing direct keyword search." )
84- matching_flights = search_flights (query )
144+ # Ensure extracted values are correct before searching
145+ origin = search_params .get ("origin" )
146+ destination = search_params .get ("destination" )
147+ flight_number = search_params .get ("flight_number" )
148+ airline = search_params .get ("airline" )
149+
150+ print (f"🟢 Searching with extracted parameters: { search_params } " )
151+
152+ # Use extracted details for searching
153+ matching_flights = search_flights (origin , destination , flight_number , airline )
85154
86155 if not matching_flights :
87- return False , "No flights found matching your criteria." , []
156+ return False , "⚠️ No flights found matching your criteria. Please try again with different details ." , []
88157
89158 return True , "Here are the flights that match your criteria:" , matching_flights
90159
@@ -95,7 +164,9 @@ def process_query(query):
95164 print (f"❌ Unexpected error in process_query: { str (e )} " )
96165 return False , f"An error occurred while processing your query: { str (e )} " , []
97166
167+
168+
98169# Test
99170if __name__ == "__main__" :
100- success , message , flights = process_query ("Show me flight NY100 " )
171+ success , message , flights = process_query ("Show me flights from New York to London on May 1st " )
101172 print (f"Success: { success } , Message: { message } , Flights: { flights } " )
0 commit comments