abdibrokhim commited on
Commit
21ffffb
·
1 Parent(s): 954241d

tons of changes

Browse files
Files changed (7) hide show
  1. app.py +144 -93
  2. bagoodex_client.py +17 -1
  3. helpers.py +190 -43
  4. prompts.py +46 -0
  5. r_types.py +83 -0
  6. test_api.py +21 -0
  7. test_app.py +41 -0
app.py CHANGED
@@ -1,132 +1,183 @@
1
  import os
2
- import requests
3
  import gradio as gr
4
- from openai import OpenAI
5
- from dotenv import load_dotenv
6
- # from helpers import format_images, format_videos, format_links, format_local_map, format_knowledge
7
  from bagoodex_client import BagoodexClient
8
-
9
- # Load API key from .env
10
- load_dotenv()
11
- AIML_API_KEY = os.getenv('AIML_API_KEY')
12
- API_URL = 'https://api.aimlapi.com'
 
 
 
 
 
13
 
14
  client = BagoodexClient()
15
 
16
- def format_knowledge(result):
17
- title = result.get('title', 'Unknown')
18
- type_ = result.get('type', '')
19
- born = result.get('born', '')
20
- died = result.get('died', '')
21
- content = f"""
22
- **{title}**
23
- Type: {type_}
24
- Born: {born}
25
- Died: {died}
26
  """
27
- return gr.Markdown(content)
28
-
29
- def format_images(result):
30
- urls = [item.get("original", "") for item in result]
31
- return urls
32
-
33
- # Helper formatting functions
34
- def format_videos(result):
35
- return [vid.get('link', '') for vid in result]
36
-
37
- # Advanced search functions
38
- def perform_video_search(followup_id):
39
- if not followup_id:
40
- return []
41
- result = client.get_videos(followup_id)
42
- return format_videos(result)
43
-
44
- def format_links(result):
45
- links_md = "**Links:**\n"
46
- for url in result:
47
- title = url.rstrip('/').split('/')[-1]
48
- links_md += f"- [{title}]({url})\n"
49
- return gr.Markdown(links_md)
50
-
51
- # Define the chat function
52
- def chat_function(message, history, followup_id):
53
  followup_id_new, answer = client.complete_chat(message)
54
- return answer, followup_id_new
 
 
 
 
 
 
 
 
 
 
 
55
 
56
- def format_local_map(result):
57
- link = result.get('link', '')
58
- image_url = result.get('image', '')
59
- html = f"""
60
- <div>
61
- <strong>Local Map:</strong><br>
62
- <a href='{link}' target='_blank'>View on Google Maps</a><br>
63
- <img src='{image_url}' style='width:100%;'/>
64
- </div>
65
  """
66
- return gr.HTML(html)
 
 
 
 
 
 
 
 
 
 
 
67
 
68
- def append_local_map(followup_id, chatbot_value):
69
- if not followup_id:
70
- return chatbot_value
71
- result = client.get_local_map(followup_id)
72
- formatted = format_local_map(result)
73
- new_message = {"role": "assistant", "content": formatted}
74
- return chatbot_value + [new_message]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
 
76
- def append_knowledge(followup_id, chatbot_value):
77
- if not followup_id:
78
- return chatbot_value
79
- result = client.get_knowledge(followup_id)
80
- formatted = format_knowledge(result)
81
- new_message = {"role": "assistant", "content": formatted}
82
- return chatbot_value + [new_message]
 
 
 
83
 
84
- # Define advanced search functions
85
- def perform_image_search(followup_id):
86
- if not followup_id:
 
 
87
  return []
88
- result = client.get_images(followup_id)
89
- urls = format_images(result)
90
- return urls
 
 
 
 
 
 
 
91
 
92
- def perform_links_search(followup_id):
93
- if not followup_id:
94
  return gr.Markdown("No followup ID available.")
95
- result = client.get_links(followup_id)
96
  return format_links(result)
97
 
98
- # Custom CSS
 
 
99
  css = """
100
  #chatbot {
101
  height: 100%;
102
  }
103
  """
104
 
105
- # Build UI
106
  with gr.Blocks(css=css, fill_height=True) as demo:
 
107
  followup_state = gr.State(None)
 
 
 
108
  with gr.Row():
109
  with gr.Column(scale=3):
110
  with gr.Row():
111
  btn_local_map = gr.Button("Local Map Search", variant="secondary", size="sm")
112
  btn_knowledge = gr.Button("Knowledge Base", variant="secondary", size="sm")
 
 
113
  chat = gr.ChatInterface(
114
  fn=chat_function,
115
  type="messages",
116
- additional_inputs=[followup_state],
117
- additional_outputs=[followup_state],
118
  )
119
- # Wire up the buttons to append to chat history
120
  btn_local_map.click(
121
- append_local_map,
122
- inputs=[followup_state, chat.chatbot],
123
  outputs=chat.chatbot
124
  )
125
  btn_knowledge.click(
126
- append_knowledge,
127
- inputs=[followup_state, chat.chatbot],
128
  outputs=chat.chatbot
129
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  with gr.Column(scale=1):
131
  gr.Markdown("### Advanced Search Options")
132
  with gr.Column(variant="panel"):
@@ -134,21 +185,21 @@ with gr.Blocks(css=css, fill_height=True) as demo:
134
  btn_videos = gr.Button("Search Videos")
135
  btn_links = gr.Button("Search Links")
136
  gallery_output = gr.Gallery(label="Image Results", columns=2)
137
- video_output = gr.Gallery(label="Video Results", columns=1, visible=True)
138
  links_output = gr.Markdown(label="Links Results")
139
  btn_images.click(
140
- perform_image_search,
141
  inputs=[followup_state],
142
  outputs=[gallery_output]
143
  )
144
  btn_videos.click(
145
- perform_video_search,
146
  inputs=[followup_state],
147
  outputs=[video_output]
148
  )
149
  btn_links.click(
150
- perform_links_search,
151
  inputs=[followup_state],
152
  outputs=[links_output]
153
  )
154
- demo.launch()
 
1
  import os
 
2
  import gradio as gr
 
 
 
3
  from bagoodex_client import BagoodexClient
4
+ from r_types import ChatMessage
5
+ from prompts import SYSTEM_PROMPT_FOLLOWUP, SYSTEM_PROMPT_MAP, SYSTEM_PROMPT_BASE
6
+ from helpers import (
7
+ embed_video,
8
+ embed_image,
9
+ format_links,
10
+ embed_google_map,
11
+ format_knowledge,
12
+ format_followup_questions
13
+ )
14
 
15
  client = BagoodexClient()
16
 
17
+ # ----------------------------
18
+ # Chat & Follow-up Functions
19
+ # ----------------------------
20
+ def chat_function(message, history, followup_state, chat_history_state):
 
 
 
 
 
 
21
  """
22
+ Process a new user message.
23
+ Appends the message and response to the conversation,
24
+ and retrieves follow-up questions.
25
+ """
26
+ # complete_chat returns a new followup id and answer
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  followup_id_new, answer = client.complete_chat(message)
28
+ # Update conversation history (if history is None, use an empty list)
29
+ if history is None:
30
+ history = []
31
+ updated_history = history + [ChatMessage({"role": "user", "content": message}),
32
+ ChatMessage({"role": "assistant", "content": answer})]
33
+ # Retrieve follow-up questions using the updated conversation
34
+ followup_questions_raw = client.base_qna(
35
+ messages=updated_history, system_prompt=SYSTEM_PROMPT_FOLLOWUP
36
+ )
37
+ # Format them using the helper
38
+ followup_md = format_followup_questions(followup_questions_raw)
39
+ return answer, followup_id_new, updated_history, followup_md
40
 
41
+ def handle_followup_click(question, followup_state, chat_history_state):
42
+ """
43
+ When a follow-up question is clicked, send it as a new message.
 
 
 
 
 
 
44
  """
45
+ if not question:
46
+ return chat_history_state, followup_state, ""
47
+ # Process the follow-up question via complete_chat
48
+ followup_id_new, answer = client.complete_chat(question)
49
+ updated_history = chat_history_state + [ChatMessage({"role": "user", "content": question}),
50
+ ChatMessage({"role": "assistant", "content": answer})]
51
+ # Get new follow-up questions
52
+ followup_questions_raw = client.base_qna(
53
+ messages=updated_history, system_prompt=SYSTEM_PROMPT_FOLLOWUP
54
+ )
55
+ followup_md = format_followup_questions(followup_questions_raw)
56
+ return updated_history, followup_id_new, followup_md
57
 
58
+ def handle_local_map_click(followup_state, chat_history_state):
59
+ """
60
+ On local map click, try to get a local map.
61
+ If issues occur, fall back to using the SYSTEM_PROMPT_MAP.
62
+ """
63
+ if not followup_state:
64
+ return chat_history_state
65
+ try:
66
+ result = client.get_local_map(followup_state)
67
+ map_url = result.get('link', '')
68
+ # Use helper to produce an embedded map iframe
69
+ html = embed_google_map(map_url)
70
+ except Exception:
71
+ # Fall back: use the base_qna call with SYSTEM_PROMPT_MAP
72
+ result = client.base_qna(
73
+ messages=chat_history_state, system_prompt=SYSTEM_PROMPT_MAP
74
+ )
75
+ # Assume result contains a 'link' field
76
+ html = embed_google_map(result.get('link', ''))
77
+ new_message = ChatMessage({"role": "assistant", "content": html})
78
+ return chat_history_state + [new_message]
79
 
80
+ def handle_knowledge_click(followup_state, chat_history_state):
81
+ """
82
+ On knowledge base click, fetch and format knowledge content.
83
+ """
84
+ if not followup_state:
85
+ return chat_history_state
86
+ result = client.get_knowledge(followup_state)
87
+ md = format_knowledge(result)
88
+ new_message = ChatMessage({"role": "assistant", "content": md})
89
+ return chat_history_state + [new_message]
90
 
91
+ # ----------------------------
92
+ # Advanced Search Functions
93
+ # ----------------------------
94
+ def perform_image_search(followup_state):
95
+ if not followup_state:
96
  return []
97
+ result = client.get_images(followup_state)
98
+ # For images we simply return a list of original URLs
99
+ return [item.get("original", "") for item in result]
100
+
101
+ def perform_video_search(followup_state):
102
+ if not followup_state:
103
+ return "<p>No followup ID available.</p>"
104
+ result = client.get_videos(followup_state)
105
+ # Use the helper to produce the embed iframes (supports multiple videos)
106
+ return embed_video(result)
107
 
108
+ def perform_links_search(followup_state):
109
+ if not followup_state:
110
  return gr.Markdown("No followup ID available.")
111
+ result = client.get_links(followup_state)
112
  return format_links(result)
113
 
114
+ # ----------------------------
115
+ # UI Build
116
+ # ----------------------------
117
  css = """
118
  #chatbot {
119
  height: 100%;
120
  }
121
  """
122
 
 
123
  with gr.Blocks(css=css, fill_height=True) as demo:
124
+ # State variables to hold followup ID and conversation history, plus follow-up questions text
125
  followup_state = gr.State(None)
126
+ chat_history_state = gr.State([]) # holds conversation history as a list of messages
127
+ followup_md_state = gr.State("") # holds follow-up questions as Markdown text
128
+
129
  with gr.Row():
130
  with gr.Column(scale=3):
131
  with gr.Row():
132
  btn_local_map = gr.Button("Local Map Search", variant="secondary", size="sm")
133
  btn_knowledge = gr.Button("Knowledge Base", variant="secondary", size="sm")
134
+ # The ChatInterface now uses additional outputs for both followup_state and conversation history,
135
+ # plus follow-up questions Markdown.
136
  chat = gr.ChatInterface(
137
  fn=chat_function,
138
  type="messages",
139
+ additional_inputs=[followup_state, chat_history_state],
140
+ additional_outputs=[followup_state, chat_history_state, followup_md_state],
141
  )
142
+ # Button callbacks to append local map and knowledge base results to chat
143
  btn_local_map.click(
144
+ fn=handle_local_map_click,
145
+ inputs=[followup_state, chat_history_state],
146
  outputs=chat.chatbot
147
  )
148
  btn_knowledge.click(
149
+ fn=handle_knowledge_click,
150
+ inputs=[followup_state, chat_history_state],
151
  outputs=chat.chatbot
152
  )
153
+ # Below the chat input, display follow-up questions and let user select one.
154
+ followup_radio = gr.Radio(
155
+ choices=[], label="Follow-up Questions (select one and click Send Follow-up)"
156
+ )
157
+ btn_send_followup = gr.Button("Send Follow-up")
158
+ # When a follow-up question is sent, update the chat conversation, followup state, and follow-up list.
159
+ btn_send_followup.click(
160
+ fn=handle_followup_click,
161
+ inputs=[followup_radio, followup_state, chat_history_state],
162
+ outputs=[chat.chatbot, followup_state, followup_md_state]
163
+ )
164
+ # Also display the follow-up questions markdown (for reference) in a Markdown component.
165
+ followup_markdown = gr.Markdown(label="Follow-up Questions", value="", visible=True)
166
+ # When the followup_md_state updates, also update the radio choices.
167
+ def update_followup_radio(md_text):
168
+ # Assume the helper output is a Markdown string with list items.
169
+ # We split the text to extract the question lines.
170
+ lines = md_text.splitlines()
171
+ questions = []
172
+ for line in lines:
173
+ if line.startswith("- "):
174
+ questions.append(line[2:])
175
+ return gr.update(choices=questions, value=None), md_text
176
+ followup_md_state.change(
177
+ fn=update_followup_radio,
178
+ inputs=[followup_md_state],
179
+ outputs=[followup_radio, followup_markdown]
180
+ )
181
  with gr.Column(scale=1):
182
  gr.Markdown("### Advanced Search Options")
183
  with gr.Column(variant="panel"):
 
185
  btn_videos = gr.Button("Search Videos")
186
  btn_links = gr.Button("Search Links")
187
  gallery_output = gr.Gallery(label="Image Results", columns=2)
188
+ video_output = gr.HTML(label="Video Results") # HTML for embedded video iframes
189
  links_output = gr.Markdown(label="Links Results")
190
  btn_images.click(
191
+ fn=perform_image_search,
192
  inputs=[followup_state],
193
  outputs=[gallery_output]
194
  )
195
  btn_videos.click(
196
+ fn=perform_video_search,
197
  inputs=[followup_state],
198
  outputs=[video_output]
199
  )
200
  btn_links.click(
201
+ fn=perform_links_search,
202
  inputs=[followup_state],
203
  outputs=[links_output]
204
  )
205
+ demo.launch()
bagoodex_client.py CHANGED
@@ -2,6 +2,9 @@ import os
2
  import requests
3
  from openai import OpenAI
4
  from dotenv import load_dotenv
 
 
 
5
 
6
  load_dotenv()
7
  API_KEY = os.getenv("AIML_API_KEY")
@@ -20,11 +23,24 @@ class BagoodexClient:
20
  """
21
  response = self.client.chat.completions.create(
22
  model="bagoodex/bagoodex-search-v1",
23
- messages=[{"role": "user", "content": query}],
 
 
 
24
  )
25
  followup_id = response.id # the unique ID for follow-up searches
26
  answer = response.choices[0].message.content
27
  return followup_id, answer
 
 
 
 
 
 
 
 
 
 
28
 
29
  def get_links(self, followup_id):
30
  headers = {"Authorization": f"Bearer {self.api_key}"}
 
2
  import requests
3
  from openai import OpenAI
4
  from dotenv import load_dotenv
5
+ from r_types import ChatMessage
6
+ from prompts import SYSTEM_PROMPT_BASE, SYSTEM_PROMPT_MAP
7
+ from typing import List
8
 
9
  load_dotenv()
10
  API_KEY = os.getenv("AIML_API_KEY")
 
23
  """
24
  response = self.client.chat.completions.create(
25
  model="bagoodex/bagoodex-search-v1",
26
+ messages=[
27
+ ChatMessage(role="user", content=SYSTEM_PROMPT_BASE),
28
+ ChatMessage(role="user", content=query)
29
+ ],
30
  )
31
  followup_id = response.id # the unique ID for follow-up searches
32
  answer = response.choices[0].message.content
33
  return followup_id, answer
34
+
35
+ def base_qna(self, messages: List[ChatMessage], system_prompt=SYSTEM_PROMPT_BASE):
36
+ response = self.client.chat.completions.create(
37
+ model="gpt-4o",
38
+ messages=[
39
+ ChatMessage(role="user", content=system_prompt),
40
+ *messages
41
+ ],
42
+ )
43
+ return response.choices[0].message.content
44
 
45
  def get_links(self, followup_id):
46
  headers = {"Authorization": f"Bearer {self.api_key}"}
helpers.py CHANGED
@@ -1,43 +1,190 @@
1
- # Helper formatting functions
2
- def format_local_map(result):
3
- html = (
4
- f"<div><strong>Local Map:</strong><br>"
5
- f"<a href='{result.get('link','')}' target='_blank'>View on Google Maps</a><br>"
6
- f"<img src='{result.get('image','')}' style='width:100%;'/></div>"
7
- )
8
- return html
9
-
10
- def format_knowledge(result):
11
- html = (
12
- f"<div><strong>{result.get('title','Unknown')}</strong><br>"
13
- f"Type: {result.get('type','')}<br>"
14
- f"Born: {result.get('born','')}<br>"
15
- f"Died: {result.get('died','')}</div>"
16
- )
17
- return html
18
-
19
- def format_images(result):
20
- # Extract 'original' URL from each image dict.
21
- urls = [item.get("original", "") for item in result]
22
- return urls
23
-
24
- def format_videos(result):
25
- html = "<div>"
26
- for vid in result:
27
- html += (
28
- f"<div style='margin-bottom:10px;'>"
29
- f"<video controls style='width:100%;' src='{vid.get('link','')}'></video><br>"
30
- f"{vid.get('title','')}"
31
- f"</div>"
32
- )
33
- html += "</div>"
34
- return html
35
-
36
- def format_links(result):
37
- html = "<div><ul>"
38
- for url in result:
39
- # Use the final part of the URL as the title.
40
- title = url.rstrip('/').split('/')[-1]
41
- html += f"<li><a href='{url}' target='_blank'>{title}</a></li>"
42
- html += "</ul></div>"
43
- return html
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dotenv import load_dotenv
2
+ import os
3
+ import gradio as gr
4
+ import urllib.parse
5
+ import re
6
+ from pytube import YouTube
7
+ from typing import List, Optional
8
+ from r_types import (
9
+ SearchVideosResponse,
10
+ SearchImagesResponse,
11
+ SearchLinksResponse,
12
+ LocalMapResponse,
13
+ KnowledgeBaseResponse
14
+ )
15
+
16
+
17
+ def get_video_id(url: str) -> Optional[str]:
18
+ """
19
+ Safely retrieve the YouTube video_id from a given URL using pytube.
20
+ Returns None if the URL is invalid or an error occurs.
21
+ """
22
+ if not url:
23
+ return None
24
+
25
+ try:
26
+ yt = YouTube(url)
27
+ return yt.video_id
28
+ except Exception:
29
+ # If the URL is invalid or pytube fails, return None
30
+ return None
31
+
32
+
33
+ def embed_video(videos: List[SearchVideosResponse]) -> str:
34
+ """
35
+ Given a list of video data (with 'link' and 'title'),
36
+ returns an HTML string of embedded YouTube iframes.
37
+ """
38
+ if not videos:
39
+ return "<p>No videos found.</p>"
40
+
41
+ # Collect each iframe snippet
42
+ iframes = []
43
+ for video in videos:
44
+ url = video.get("link", "")
45
+ video_id = get_video_id(url)
46
+ if not video_id:
47
+ # Skip invalid or non-parsable links
48
+ continue
49
+
50
+ title = video.get("title", "").replace('"', '\\"') # Escape quotes
51
+ iframe = f"""
52
+ <iframe
53
+ width="560"
54
+ height="315"
55
+ src="https://www.youtube.com/embed/{video_id}"
56
+ title="{title}"
57
+ frameborder="0"
58
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
59
+ allowfullscreen>
60
+ </iframe>
61
+ """
62
+ iframes.append(iframe)
63
+
64
+ # If no valid videos after processing, return a fallback message
65
+ if not iframes:
66
+ return "<p>No valid YouTube videos found.</p>"
67
+
68
+ # Join all iframes into one HTML string
69
+ return "\n".join(iframes)
70
+
71
+
72
+ def embed_image(json_data: SearchImagesResponse) -> str:
73
+ """
74
+ Given image data with 'original' (URL) and 'title',
75
+ returns an HTML string with an <img> tag.
76
+ """
77
+ title = json_data.get("title", "").replace('"', '\\"')
78
+ original = json_data.get("original", "")
79
+
80
+ if not original:
81
+ return "<p>No image URL provided.</p>"
82
+
83
+ embed_html = f"""
84
+ <img src="{original}" alt="{title}" style="width:100%">
85
+ """
86
+ return embed_html
87
+
88
+
89
+ def build_search_links_response(urls: List[str]) -> List[SearchLinksResponse]:
90
+ """
91
+ Convert raw URLs into a list of dicts,
92
+ each with 'title' and 'link' keys for display.
93
+ """
94
+ results = []
95
+ for url in urls:
96
+ # Extract the last part of the URL as a rough "title"
97
+ raw_title = url.rstrip("/").split("/")[-1]
98
+
99
+ # Decode URL-encoded entities like %20
100
+ decoded_title = urllib.parse.unquote(raw_title)
101
+
102
+ # Replace hyphens/underscores with spaces
103
+ nice_title = decoded_title.replace("_", " ").replace("-", " ")
104
+
105
+ results.append({"title": nice_title, "link": url})
106
+ return results
107
+
108
+
109
+ def format_links(links: List[SearchLinksResponse]) -> str:
110
+ """
111
+ Convert a list of {'title': str, 'link': str} objects
112
+ into a bulleted Markdown string with clickable links.
113
+ """
114
+ if not links:
115
+ return "No links found."
116
+
117
+ links_md = "### Links\n\n"
118
+ for item in links:
119
+ links_md += f"- [{item['title']}]({item['link']})\n"
120
+ return links_md
121
+
122
+
123
+ def embed_google_map(map_url: str) -> str:
124
+ """
125
+ Extracts a textual location from the given Google Maps URL
126
+ and returns an embedded Google Map iframe for that location.
127
+ Assumes you have a valid API key in place of 'YOUR_API_KEY'.
128
+ """
129
+ load_dotenv()
130
+ GOOGLE_MAPS_API_KEY = os.getenv("GOOGLE_MAPS_API_KEY")
131
+
132
+ if not map_url:
133
+ return "<p>Invalid Google Maps URL.</p>"
134
+
135
+ # Attempt to extract "San+Francisco,+CA" from the URL
136
+ match = re.search(r"/maps/place/([^/]+)", map_url)
137
+ if not match:
138
+ return "Invalid Google Maps URL. Could not extract location."
139
+
140
+ location_text = match.group(1)
141
+ # Remove query params or additional slashes from the captured group
142
+ location_text = re.split(r"[/?]", location_text)[0]
143
+
144
+ # URL-encode location to avoid issues with special characters
145
+ encoded_location = urllib.parse.quote(location_text, safe="")
146
+
147
+ embed_html = f"""
148
+ <iframe
149
+ width="600"
150
+ height="450"
151
+ style="border:0"
152
+ loading="lazy"
153
+ allowfullscreen
154
+ src="https://www.google.com/maps/embed/v1/place?key={GOOGLE_MAPS_API_KEY}&q={encoded_location}">
155
+ </iframe>
156
+ """
157
+ return embed_html
158
+
159
+
160
+ def format_knowledge(result: KnowledgeBaseResponse) -> str:
161
+ """
162
+ Given a dictionary of knowledge data (e.g., about a person),
163
+ produce a Markdown string summarizing that info.
164
+ """
165
+ title = result.get("title", "Unknown")
166
+ type_ = result.get("type", "")
167
+ born = result.get("born", "")
168
+ died = result.get("died", "")
169
+
170
+ content = f"""
171
+ **{title}**
172
+ Type: {type_}
173
+ Born: {born}
174
+ Died: {died}
175
+ """
176
+ return content
177
+
178
+
179
+ def format_followup_questions(questions: List[str]) -> str:
180
+ """
181
+ Given a list of follow-up questions, return a Markdown string
182
+ with each question as a bulleted list item.
183
+ """
184
+ if not questions:
185
+ return "No follow-up questions provided."
186
+
187
+ questions_md = "### Follow-up Questions\n\n"
188
+ for question in questions:
189
+ questions_md += f"- {question}\n"
190
+ return questions_md
prompts.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ SYSTEM_PROMPT_BASE = """
2
+ ######SYSTEM INIATED######
3
+ You will be given a conversation chat (e.g., text/ paragraph).
4
+ Answer the given conversation chat with a relevant response.
5
+
6
+ ######NOTE######
7
+ Be nice and polite in your responses!
8
+ ######SYSTEM SHUTDOWN######
9
+ """
10
+
11
+ SYSTEM_PROMPT_MAP = """
12
+ ######SYSTEM INIATED######
13
+ You will be given a content from conversation chat (e.g., text/ paragraph).
14
+ Your task is to analyze the given content and provide different types of places as close as possible to the given content.
15
+ For exampl: If the given content (conversation chat) was about "How to make a slingshot", you can provide places like "Hardware store", "Woodworking shop", "Outdoor sports store", etc.
16
+ Make sure the places you provide are relevant to the given content. And as much as close to the given content, the better.
17
+ Your final output should be a list of places.
18
+ Here's JSON format example:
19
+ ```json
20
+ {
21
+ "places": ["Hardware store", "Woodworking shop", "Outdoor sports store"]
22
+ }
23
+ ```
24
+ ######NOTE######
25
+ Make sure to return only JSON data! Nothing else!
26
+ ######SYSTEM SHUTDOWN######
27
+ """
28
+
29
+
30
+ SYSTEM_PROMPT_FOLLOWUP = """
31
+ ######SYSTEM INIATED######
32
+ You will be given a content from conversation chat (e.g., text/ paragraph).
33
+ Your task is to analyze the given content and provide a follow-up question based on the given content.
34
+ For example: If the given content (conversation chat) was about "How to make a slingshot", you can provide a follow-up question like "What materials are needed to make a slingshot?".
35
+ Make sure the follow-up question you provide is relevant to the given content.
36
+ Your final output should be a List of follow-up question.
37
+ Here's JSON format example:
38
+ ```json
39
+ {
40
+ "followup_question": ["What materials are needed to make a slingshot?", "How to make a slingshot more powerful?"]
41
+ }
42
+ ```
43
+ ######NOTE######
44
+ Make sure to return only JSON data! Nothing else!
45
+ ######SYSTEM SHUTDOWN######
46
+ """
r_types.py ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import TypedDict
2
+
3
+ # [ChatMessage]:
4
+ # <response>
5
+ # {
6
+ # "role": "system",
7
+ # "content": "Hello, how can I help you today?"
8
+ # }
9
+ # </response>
10
+
11
+ class ChatMessage(TypedDict):
12
+ role: str
13
+ content: str
14
+
15
+
16
+ # [Search Videos]:
17
+ # <response>
18
+ # Videos:
19
+ # [{'link': 'https://www.youtube.com/watch?v=X9oWGuKypuY', 'thumbnail': 'https://dmwtgq8yidg0m.cloudfront.net/medium/d3G6HeC5BO93-video-thumb.jpeg', 'title': 'Easy Home Made Slingshot'}, {'link': 'https://www.youtube.com/watch?v=V2iZF8oAXHo&pp=ygUMI2d1bGVsaGFuZGxl', 'thumbnail': 'https://dmwtgq8yidg0m.cloudfront.net/medium/sb2Iw9Ug-Pne-video-thumb.jpeg', 'title': 'Making an Apple Wood Slingshot | Woodcraft'}]
20
+ # </response>
21
+
22
+ class SearchVideosResponse(TypedDict):
23
+ link: str
24
+ thumbnail: str
25
+ title: str
26
+
27
+
28
+ # [Search Images]:
29
+ # <response>
30
+ # [{'source': '', 'original': 'https://i.ytimg.com/vi/iYlJirFtYaA/sddefault.jpg', 'title': 'How to make a Slingshot using Pencils ...', 'source_name': 'YouTube'}, {'source': '', 'original': 'https://i.ytimg.com/vi/HWSkVaptzRA/maxresdefault.jpg', 'title': 'How to make a Slingshot at Home - YouTube', 'source_name': 'YouTube'}, {'source': '', 'original': 'https://content.instructables.com/FHB/VGF8/FHXUOJKJ/FHBVGF8FHXUOJKJ.jpg?auto=webp', 'title': 'Country Boy" Style Slingshot ...', 'source_name': 'Instructables'}, {'source': '', 'original': 'https://i.ytimg.com/vi/6wXqlJVw03U/maxresdefault.jpg', 'title': 'Make slingshot using popsicle stick ...', 'source_name': 'YouTube'}, {'source': '', 'original': 'https://ds-tc.prod.pbskids.org/designsquad/diy/DESIGN-SQUAD-42.jpg', 'title': 'Build | Indoor Slingshot . DESIGN SQUAD ...', 'source_name': 'PBS KIDS'}, {'source': '', 'original': 'https://i.ytimg.com/vi/wCxFkPLuNyA/maxresdefault.jpg', 'title': 'Paper Ninja Weapons ...', 'source_name': 'YouTube'}, {'source': '', 'original': 'https://i0.wp.com/makezine.com/wp-content/uploads/2015/01/slingshot1.jpg?fit=800%2C600&ssl=1', 'title': 'Rotating Bearings ...', 'source_name': 'Make Magazine'}, {'source': '', 'original': 'https://makeandtakes.com/wp-content/uploads/IMG_1144-1.jpg', 'title': 'Make a DIY Stick Slingshot Kids Craft', 'source_name': 'Make and Takes'}, {'source': '', 'original': 'https://i.ytimg.com/vi/X9oWGuKypuY/maxresdefault.jpg', 'title': 'Easy Home Made Slingshot - YouTube', 'source_name': 'YouTube'}, {'source': '', 'original': 'https://www.wikihow.com/images/thumb/4/41/Make-a-Sling-Shot-Step-7-Version-5.jpg/550px-nowatermark-Make-a-Sling-Shot-Step-7-Version-5.jpg', 'title': 'How to Make a Sling Shot: 15 Steps ...', 'source_name': 'wikiHow'}]
31
+ # </response>
32
+
33
+ class SearchImagesResponse(TypedDict):
34
+ source: str
35
+ original: str
36
+ title: str
37
+ source: str
38
+ source_name: str
39
+
40
+
41
+ # [Links]:
42
+ # <response>
43
+ # ['https://www.reddit.com/r/slingshots/comments/1d50p3e/how_to_build_a_sling_at_home_thats_not_shit/', 'https://www.instructables.com/Make-a-Giant-Slingshot/', 'https://www.mudandbloom.com/blog/stick-slingshot', 'https://pbskids.org/designsquad/build/indoor-slingshot/', 'https://www.instructables.com/How-to-Make-a-Slingshot-2/']
44
+ # </response>
45
+
46
+ class SearchLinksResponse(TypedDict):
47
+ title: str
48
+ link: str
49
+
50
+
51
+ ### Local Map Response:
52
+ # <response>
53
+ # {
54
+ # "link": "https://www.google.com/maps/place/San+Francisco,+CA/data=!4m2!3m1!1s0x80859a6d00690021:0x4a501367f076adff?sa=X&ved=2ahUKEwjqg7eNz9KLAxVCFFkFHWSPEeIQ8gF6BAgqEAA&hl=en",
55
+ # "image": "https://dmwtgq8yidg0m.cloudfront.net/images/TdNFUpcEvvHL-local-map.webp"
56
+ # }
57
+ # </response>
58
+
59
+ class LocalMapResponse(TypedDict):
60
+ link: str
61
+ imgae: str
62
+
63
+
64
+ ### Model Response:
65
+
66
+ # <response>
67
+ # ```
68
+ # {
69
+ # 'title': 'Nikola Tesla',
70
+ # 'type': 'Engineer and futurist',
71
+ # 'description': None,
72
+ # 'born': 'July 10, 1856, Smiljan, Croatia',
73
+ # 'died': 'January 7, 1943 (age 86 years), The New Yorker A Wyndham Hotel, New York, NY'
74
+ # }
75
+ # ```
76
+ # </response>
77
+
78
+ class KnowledgeBaseResponse(TypedDict):
79
+ title: str
80
+ type: str
81
+ description: str
82
+ born: str
83
+ died: str
test_api.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from openai import OpenAI
3
+
4
+ client = OpenAI(
5
+ base_url="https://api.aimlapi.com/v1",
6
+ api_key=os.getenv("AIML_API_KEY"),
7
+ )
8
+
9
+ response = client.chat.completions.create(
10
+ model="gpt-4o",
11
+ messages=[
12
+ {
13
+ "role": "user",
14
+ "content": "Tell me, why is the sky blue?"
15
+ },
16
+ ],
17
+ )
18
+
19
+ message = response.choices[0].message.content
20
+
21
+ print(f"Assistant: {message}")
test_app.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import re
3
+
4
+ def embed_google_map(map_url):
5
+ """
6
+ Extracts a textual location from the Google Maps URL and returns
7
+ an embedded Google Map iframe.
8
+ """
9
+ # Simple approach: try to extract "San+Francisco,+CA" from the URL
10
+ # by capturing what's after '/maps/place/'
11
+ match = re.search(r'/maps/place/([^/]+)', map_url)
12
+ if not match:
13
+ return "Invalid Google Maps URL. Could not extract location."
14
+
15
+ location_text = match.group(1)
16
+ # location_text might contain extra parameters, so let's just
17
+ # strip everything after the first '?' or '/':
18
+ location_text = re.split(r'[/?]', location_text)[0]
19
+
20
+ embed_html = f"""
21
+ <iframe
22
+ width="600"
23
+ height="450"
24
+ style="border:0"
25
+ loading="lazy"
26
+ allowfullscreen
27
+ src="https://www.google.com/maps/embed/v1/place?key=YOUR_API_KEY&q={location_text}">
28
+ </iframe>
29
+ """
30
+ return embed_html
31
+
32
+ with gr.Blocks() as demo:
33
+ map_url_input = gr.Textbox(label="Enter Google Maps URL")
34
+ map_output = gr.HTML(label="Embedded Google Map")
35
+
36
+ map_url_input.change(embed_google_map, inputs=map_url_input, outputs=map_output)
37
+
38
+ demo.launch()
39
+
40
+
41
+ # <iframe src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d11988.613116381091!2d69.19850824650604!3d41.3055290002301!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x38ae8bc5b351bc23%3A0x707af898bf0cdb4d!2sEsenin%20St%2018%2C%20Tashkent%2C%20Uzbekistan!5e0!3m2!1sen!2s!4v1740469397909!5m2!1sen!2s" width="600" height="450" style="border:0;" allowfullscreen="" loading="lazy" referrerpolicy="no-referrer-when-downgrade"></iframe>