Abid Ali Awan commited on
Commit
3f8fdb1
Β·
1 Parent(s): 1818834

Update README.md: Change emoji from πŸ“‰ to ✈️ to better reflect the theme of travel.

Browse files
Files changed (3) hide show
  1. README.md +1 -1
  2. app.py +170 -0
  3. requirements.txt +2 -0
README.md CHANGED
@@ -1,6 +1,6 @@
1
  ---
2
  title: Travel With Kimi K2
3
- emoji: πŸ“‰
4
  colorFrom: purple
5
  colorTo: gray
6
  sdk: gradio
 
1
  ---
2
  title: Travel With Kimi K2
3
+ emoji: ✈️
4
  colorFrom: purple
5
  colorTo: gray
6
  sdk: gradio
app.py ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import time
3
+
4
+ import gradio as gr
5
+ from firecrawl import FirecrawlApp
6
+ from groq import Groq
7
+
8
+ # Initialize API clients
9
+ firecrawl_client = FirecrawlApp(api_key=os.environ["FIRECRAWL_API_KEY"])
10
+ groq_client = Groq(api_key=os.environ["GROQ_API_KEY"])
11
+
12
+
13
+ def search_flights(origin: str, destination: str, limit: int = 5):
14
+ query = f"Find flights from {origin} to {destination}"
15
+ results = firecrawl_client.search(
16
+ query,
17
+ limit=limit,
18
+ tbs="qdr:w", # results from the past week
19
+ timeout=30000,
20
+ )
21
+ flights = []
22
+ for item in results.data:
23
+ flights.append(
24
+ {
25
+ "title": item["title"],
26
+ "url": item["url"],
27
+ "description": item["description"],
28
+ }
29
+ )
30
+ return flights
31
+
32
+
33
+ def summarize_flights(flights):
34
+ messages = [
35
+ {"role": "system", "content": "You are a helpful assistant."},
36
+ {
37
+ "role": "user",
38
+ "content": f"""
39
+ Summarize these flight search results in clear, visually appealing markdown. For each flight, use the following format:
40
+
41
+ ---
42
+ ## [Airline Name]
43
+
44
+ - **Route:** [Origin] β†’ [Destination]
45
+ - **Price:** [Price or 'Not quoted']
46
+ - **Key Points:** [Key features or restrictions]
47
+ - **Book:** [Link with airline name](URL)
48
+
49
+ ---
50
+
51
+ {flights}
52
+ """,
53
+ },
54
+ ]
55
+ resp = groq_client.chat.completions.create(
56
+ model="moonshotai/kimi-k2-instruct",
57
+ messages=messages,
58
+ max_tokens=512,
59
+ temperature=0.7,
60
+ )
61
+ return resp.choices[0].message.content.strip()
62
+
63
+
64
+ def scrape_flight_details(url: str):
65
+ try:
66
+ scrape_result = firecrawl_client.scrape_url(
67
+ url=url,
68
+ formats=["markdown"],
69
+ max_age=3600000,
70
+ )
71
+ content = getattr(scrape_result, "markdown", None)
72
+ if not content:
73
+ return "No markdown content returned from scrape."
74
+ content = content[:1500] # limit for display
75
+ # Summarize scraped content
76
+ messages = [
77
+ {"role": "system", "content": "You are a helpful assistant."},
78
+ {
79
+ "role": "user",
80
+ "content": f"Summarize the key details from this flight booking page for a traveler.\n{content}",
81
+ },
82
+ ]
83
+ resp = groq_client.chat.completions.create(
84
+ model="moonshotai/kimi-k2-instruct",
85
+ messages=messages,
86
+ max_tokens=512,
87
+ temperature=0.7,
88
+ )
89
+ return resp.choices[0].message.content.strip()
90
+ except Exception as e:
91
+ import traceback
92
+
93
+ tb = traceback.format_exc()
94
+ return f"Error scraping details: {type(e).__name__}: {e}\nTraceback:\n{tb}"
95
+
96
+
97
+ def travel_deal_finder(origin, destination, deep_search):
98
+ flights = search_flights(origin, destination)
99
+ summary = summarize_flights(str(flights))
100
+ scraped_details = "No results to scrape."
101
+ if deep_search:
102
+ for flight in flights:
103
+ url = flight["url"]
104
+ details = scrape_flight_details(url)
105
+ if not details.startswith("Error scraping details"):
106
+ scraped_details = details
107
+ break
108
+ else:
109
+ scraped_details = (
110
+ "Deep Search is disabled. Only summarized search results are shown above."
111
+ )
112
+ return summary, scraped_details
113
+
114
+
115
+ def travel_deal_finder_with_time(origin, destination, deep_search):
116
+ start = time.time()
117
+ summary, scraped_details = travel_deal_finder(origin, destination, deep_search)
118
+ elapsed = time.time() - start
119
+ return (
120
+ summary,
121
+ scraped_details,
122
+ gr.update(interactive=True), # Re-enable button
123
+ f"⏱️ Processing time: {elapsed:.2f} seconds",
124
+ )
125
+
126
+
127
+ def main():
128
+ with gr.Blocks() as demo:
129
+ gr.Markdown(
130
+ "# Travel Deal Finder ✈️\nEnter your origin and destination to find and summarize the best flight deals!"
131
+ )
132
+ with gr.Row():
133
+ origin = gr.Textbox(label="Origin City", value="New York")
134
+ destination = gr.Textbox(label="Destination City", value="Tokyo")
135
+ deep_search = gr.Checkbox(
136
+ label="Deep Search (scrape top results for details)", value=False
137
+ )
138
+ search_btn = gr.Button("Find Deals")
139
+ summary_output = gr.Markdown(label="Flight Deals Summary")
140
+ scrape_output = gr.Markdown(label="Top Result Details (Scraped)")
141
+ time_output = gr.Markdown(label="Processing Time")
142
+
143
+ def on_search(o, d, ds):
144
+ # Disable button immediately
145
+ return (
146
+ gr.update(), # summary_output placeholder
147
+ gr.update(), # scrape_output placeholder
148
+ gr.update(interactive=False), # Disable button
149
+ "Processing...", # Show processing message
150
+ )
151
+
152
+ search_btn.click(
153
+ on_search,
154
+ inputs=[origin, destination, deep_search],
155
+ outputs=[summary_output, scrape_output, search_btn, time_output],
156
+ queue=False,
157
+ )
158
+
159
+ search_btn.click(
160
+ travel_deal_finder_with_time,
161
+ inputs=[origin, destination, deep_search],
162
+ outputs=[summary_output, scrape_output, search_btn, time_output],
163
+ queue=True,
164
+ )
165
+
166
+ demo.launch()
167
+
168
+
169
+ if __name__ == "__main__":
170
+ main()
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ groq==0.30.0
2
+ firecrawl-py==2.16.1