openfree commited on
Commit
7781d26
ยท
verified ยท
1 Parent(s): 9ee06c7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +95 -31
app.py CHANGED
@@ -1,17 +1,21 @@
1
  # -------- app_final.py --------
2
- import os, json, math, pathlib
3
  import numpy as np
4
  import plotly.graph_objects as go
5
  import gradio as gr
6
  import openai
7
 
8
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
9
- # 0. OpenAI API key
10
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
11
  if "OPENAI_API_KEY" not in os.environ:
12
  os.environ["OPENAI_API_KEY"] = input("๐Ÿ”‘ Enter your OpenAI API key: ").strip()
13
  openai.api_key = os.environ["OPENAI_API_KEY"]
14
 
 
 
 
 
15
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
16
  # 1. Cycle config
17
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
@@ -35,7 +39,54 @@ with open(EVENTS_PATH, encoding="utf-8") as f:
35
  EVENTS = {int(item["year"]): item["events"] for item in json.load(f)}
36
 
37
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
38
- # 3. Helper functions
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
40
  def half_sine(xs, period, amp):
41
  """Half-sine โ€˜towerโ€™ with zero bottom floor."""
@@ -112,7 +163,7 @@ def build_chart(start: int, end: int):
112
  height=450,
113
  margin=dict(t=30, l=40, r=40, b=40),
114
  hoverlabel=dict(bgcolor="#222", font_size=11),
115
- hovermode="x" # โ† unified x-hover
116
  )
117
  fig.update_xaxes(title="Year", range=[start, end], showgrid=False)
118
  fig.update_yaxes(title="Relative amplitude",
@@ -122,19 +173,15 @@ def build_chart(start: int, end: int):
122
  f"{sum(1 for y in EVENTS if start <= y <= end)}"
123
  return fig, summary
124
 
125
-
126
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
127
- # 4. GPT chat helper
128
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
129
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
130
- # 4. GPT chat helper
131
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
132
  BASE_PROMPT = (
133
  "๋‹น์‹ ์€ **CycleNavigator AI**๋กœ, ๊ฒฝ์ œ์‚ฌยท๊ตญ์ œ์ •์น˜ยท์žฅ์ฃผ๊ธฐ(9y Business, 50y K-Wave, "
134
  "80y Finance, 250y Hegemony) ๋ถ„์„์— ์ •ํ†ตํ•œ ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค. "
135
  "๋ชจ๋“  ๋‹ต๋ณ€์€ ํ•œ๊ตญ์–ด๋กœ ํ•˜๋˜ ํ•™์ˆ ์  ์ •ํ™•์„ฑ๊ณผ ์‹ค๋ฌด์  ๋ช…๋ฃŒ์„ฑ์„ ๋™์‹œ์— ๊ฐ–์ถ”์‹ญ์‹œ์˜ค. "
136
  "โœฆ ๋‹ต๋ณ€ ๊ตฌ์กฐ ์ง€์นจ: โ‘  ์งˆ๋ฌธ ํ•ต์‹ฌ ์š”์•ฝ โ†’ โ‘ก 4๋Œ€ ์ฃผ๊ธฐ์™€์˜ ๊ด€๋ จ์„ฑ ๋ช…์‹œ โ†’ "
137
- "โ‘ข ์—ญ์‚ฌยท๋ฐ์ดํ„ฐ ๊ทผ๊ฑฐ ์„ค๋ช… โ†’ โ‘ฃ ์‹œ์‚ฌ์ ยท์ „๋ง(ํ•„์š” ์‹œ) ์ˆœ์œผ๋กœ ์„œ์ˆ ํ•˜๋ฉฐ, "
138
  "๋ฒˆํ˜ธโ€ง๊ธ€๋จธ๋ฆฌํ‘œยท์งง์€ ๋ฌธ๋‹จ์„ ํ™œ์šฉํ•ด ๋…ผ๋ฆฌ์ ์œผ๋กœ ๋ฐฐ์—ดํ•ฉ๋‹ˆ๋‹ค. "
139
  "โœฆ ์ œ๊ณต๋œ [Chart summary]๋Š” ๋ฐ˜๋“œ์‹œ ํ•ด์„ยท์ธ์šฉํ•˜๊ณ , "
140
  "๊ฐ๊ด€์  ์‚ฌ์‹คยท์—ฐ๋„ยท์‚ฌ๊ฑด์„ ๊ทผ๊ฑฐ๋กœ ํ•ฉ๋‹ˆ๋‹ค. "
@@ -153,21 +200,26 @@ def chat_with_gpt(history, user_msg, chart_summary):
153
  messages.append({"role": "user", "content": user_msg})
154
 
155
  res = openai.chat.completions.create(
156
- model = "gpt-3.5-turbo",
157
- messages = messages,
158
- max_tokens = 600,
159
- temperature = 0.7
160
  )
161
  return res.choices[0].message.content.strip()
162
 
163
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
164
- # 5. Gradio UI
165
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
166
  def create_app():
167
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
168
- # โ”€โ”€ Title & subtitle
169
  gr.Markdown("## ๐Ÿ”ญ **CycleNavigator (Interactive)**")
170
- gr.Markdown("### <sub>Interactive visual service delivering insights at a glance into the four major long cyclesโ€”9-year business cycle, 50-year Kondratiev wave, 80-year financial credit cycle, and 250-year hegemony cycleโ€”through dynamic charts and AI chat.</sub>")
 
 
 
 
 
171
  gr.Markdown(
172
  "<sub>"
173
  "<b>Business 9y</b> (credit-investment business cycle) โ€ข "
@@ -176,12 +228,11 @@ def create_app():
176
  "<b>Hegemony 250y</b> (rise & fall of global powers cycle)"
177
  "</sub>"
178
  )
179
-
180
 
181
  chart_summary_state = gr.State(value="No chart yet.")
182
 
183
  with gr.Tabs():
184
- # โ”€โ”€ Tab 1: Timeline Chart โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
185
  with gr.TabItem("Timeline Chart"):
186
  with gr.Row():
187
  start_year = gr.Number(label="Start Year", value=1500, precision=0)
@@ -189,12 +240,10 @@ def create_app():
189
  zoom_in = gr.Button("๐Ÿ” Zoom In")
190
  zoom_out = gr.Button("๐Ÿ”Ž Zoom Out")
191
 
192
- # initial plot
193
  fig0, summ0 = build_chart(1500, 2500)
194
  plot = gr.Plot(value=fig0)
195
  chart_summary_state.value = summ0
196
 
197
- # refresh on year change
198
  def refresh(s, e):
199
  fig, summ = build_chart(int(s), int(e))
200
  return fig, summ
@@ -203,43 +252,58 @@ def create_app():
203
  end_year.change(refresh, [start_year, end_year],
204
  [plot, chart_summary_state])
205
 
206
- # zoom helpers
207
  def zoom(s, e, factor):
208
- mid = (s + e) / 2
209
  span = (e - s) * factor / 2
210
  ns, ne = int(mid - span), int(mid + span)
211
  fig, summ = build_chart(ns, ne)
212
  return ns, ne, fig, summ
213
 
214
  zoom_in.click(lambda s, e: zoom(s, e, 0.5),
215
- inputs = [start_year, end_year],
216
- outputs = [start_year, end_year, plot, chart_summary_state])
217
  zoom_out.click(lambda s, e: zoom(s, e, 2.0),
218
- inputs = [start_year, end_year],
219
- outputs = [start_year, end_year, plot, chart_summary_state])
220
 
221
- # JSON download
222
  gr.File(value=str(EVENTS_PATH), label="Download cycle_events.json")
223
 
224
- # โ”€โ”€ Tab 2: GPT Chat โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
225
  with gr.TabItem("GPT Chat"):
226
  chatbot = gr.Chatbot(label="Assistant")
227
  user_input = gr.Textbox(lines=3, placeholder="๋ฉ”์‹œ์ง€๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”โ€ฆ")
228
- send_btn = gr.Button("Send")
229
 
 
 
 
 
 
230
  def respond(history, msg, summ):
231
  reply = chat_with_gpt(history, msg, summ)
232
  history.append((msg, reply))
233
  return history, gr.Textbox(value="")
234
 
 
 
 
 
 
 
 
 
235
  send_btn.click(respond,
236
  [chatbot, user_input, chart_summary_state],
237
  [chatbot, user_input])
238
  user_input.submit(respond,
239
  [chatbot, user_input, chart_summary_state],
240
  [chatbot, user_input])
 
 
 
241
  return demo
242
 
243
-
 
 
244
  if __name__ == "__main__":
245
  create_app().launch()
 
1
  # -------- app_final.py --------
2
+ import os, json, math, pathlib, re, time, logging, requests
3
  import numpy as np
4
  import plotly.graph_objects as go
5
  import gradio as gr
6
  import openai
7
 
8
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
9
+ # 0. API keys & Brave Search
10
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
11
  if "OPENAI_API_KEY" not in os.environ:
12
  os.environ["OPENAI_API_KEY"] = input("๐Ÿ”‘ Enter your OpenAI API key: ").strip()
13
  openai.api_key = os.environ["OPENAI_API_KEY"]
14
 
15
+ BRAVE_KEY = os.getenv("BRAVE_KEY", "") # Brave Search API Key
16
+ BRAVE_ENDPOINT = "https://api.search.brave.com/res/v1/web/search"
17
+ logging.basicConfig(level=logging.INFO)
18
+
19
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
20
  # 1. Cycle config
21
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 
39
  EVENTS = {int(item["year"]): item["events"] for item in json.load(f)}
40
 
41
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
42
+ # 3. Brave Search helpers
43
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
44
+ def brave_search(query: str, count: int = 8):
45
+ """Call Brave Search API; return list of dicts."""
46
+ if not BRAVE_KEY:
47
+ logging.warning("โš ๏ธ BRAVE_KEY is empty; web search disabled.")
48
+ return []
49
+ try:
50
+ hdrs = {
51
+ "Accept": "application/json",
52
+ "X-Subscription-Token": BRAVE_KEY
53
+ }
54
+ data = requests.get(
55
+ BRAVE_ENDPOINT,
56
+ headers=hdrs,
57
+ params={"q": query, "count": str(count)},
58
+ timeout=15
59
+ ).json()
60
+ raw = data.get("web", {}).get("results") or []
61
+ return [
62
+ {
63
+ "title": r.get("title", ""),
64
+ "url": r.get("url", r.get("link", "")),
65
+ "snippet": r.get("description", r.get("text", "")),
66
+ "host": re.sub(r"https?://(www\.)?", "", r.get("url", "")).split("/")[0]
67
+ }
68
+ for r in raw[:count]
69
+ ]
70
+ except Exception as e:
71
+ logging.error(f"Brave Search Error: {e}")
72
+ return []
73
+
74
+ def format_search_results(query: str) -> str:
75
+ """Format search results as markdown for LLM context."""
76
+ arts = brave_search(query)
77
+ if not arts:
78
+ return f"# [Web-Search] No live results for โ€œ{query}โ€.\n"
79
+ hdr = f"# [Web-Search] Top results for โ€œ{query}โ€\n\n"
80
+ body = "\n".join(
81
+ f"**{i+1}. {a['title']}** ({a['host']})\n\n"
82
+ f"{a['snippet']}\n\n"
83
+ f"[Source]({a['url']})\n"
84
+ for i, a in enumerate(arts)
85
+ )
86
+ return hdr + body + "\n"
87
+
88
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
89
+ # 4. Helper functions (chart)
90
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
91
  def half_sine(xs, period, amp):
92
  """Half-sine โ€˜towerโ€™ with zero bottom floor."""
 
163
  height=450,
164
  margin=dict(t=30, l=40, r=40, b=40),
165
  hoverlabel=dict(bgcolor="#222", font_size=11),
166
+ hovermode="x" # unified x-hover
167
  )
168
  fig.update_xaxes(title="Year", range=[start, end], showgrid=False)
169
  fig.update_yaxes(title="Relative amplitude",
 
173
  f"{sum(1 for y in EVENTS if start <= y <= end)}"
174
  return fig, summary
175
 
 
 
 
176
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
177
+ # 5. GPT chat helper
 
178
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
179
  BASE_PROMPT = (
180
  "๋‹น์‹ ์€ **CycleNavigator AI**๋กœ, ๊ฒฝ์ œ์‚ฌยท๊ตญ์ œ์ •์น˜ยท์žฅ์ฃผ๊ธฐ(9y Business, 50y K-Wave, "
181
  "80y Finance, 250y Hegemony) ๋ถ„์„์— ์ •ํ†ตํ•œ ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค. "
182
  "๋ชจ๋“  ๋‹ต๋ณ€์€ ํ•œ๊ตญ์–ด๋กœ ํ•˜๋˜ ํ•™์ˆ ์  ์ •ํ™•์„ฑ๊ณผ ์‹ค๋ฌด์  ๋ช…๋ฃŒ์„ฑ์„ ๋™์‹œ์— ๊ฐ–์ถ”์‹ญ์‹œ์˜ค. "
183
  "โœฆ ๋‹ต๋ณ€ ๊ตฌ์กฐ ์ง€์นจ: โ‘  ์งˆ๋ฌธ ํ•ต์‹ฌ ์š”์•ฝ โ†’ โ‘ก 4๋Œ€ ์ฃผ๊ธฐ์™€์˜ ๊ด€๋ จ์„ฑ ๋ช…์‹œ โ†’ "
184
+ "โ‘ข ์—ญ์‚ฌยท๋ฐ์ดํ„ฐ ๊ทผ๊ฑฐ ์„ค๋ช… โ†’ โ‘ฃ ์‹œ์‚ฌ์ ยท์ „๋ง ์ˆœ์œผ๋กœ ์„œ์ˆ ํ•˜๋ฉฐ, "
185
  "๋ฒˆํ˜ธโ€ง๊ธ€๋จธ๋ฆฌํ‘œยท์งง์€ ๋ฌธ๋‹จ์„ ํ™œ์šฉํ•ด ๋…ผ๋ฆฌ์ ์œผ๋กœ ๋ฐฐ์—ดํ•ฉ๋‹ˆ๋‹ค. "
186
  "โœฆ ์ œ๊ณต๋œ [Chart summary]๋Š” ๋ฐ˜๋“œ์‹œ ํ•ด์„ยท์ธ์šฉํ•˜๊ณ , "
187
  "๊ฐ๊ด€์  ์‚ฌ์‹คยท์—ฐ๋„ยท์‚ฌ๊ฑด์„ ๊ทผ๊ฑฐ๋กœ ํ•ฉ๋‹ˆ๋‹ค. "
 
200
  messages.append({"role": "user", "content": user_msg})
201
 
202
  res = openai.chat.completions.create(
203
+ model="gpt-3.5-turbo",
204
+ messages=messages,
205
+ max_tokens=600,
206
+ temperature=0.7
207
  )
208
  return res.choices[0].message.content.strip()
209
 
210
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
211
+ # 6. Gradio UI
212
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
213
  def create_app():
214
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
215
+ # Title & subtitles
216
  gr.Markdown("## ๐Ÿ”ญ **CycleNavigator (Interactive)**")
217
+ gr.Markdown(
218
+ "### <sub>Interactive visual service delivering insights at a glance into "
219
+ "the four major long cyclesโ€”9-year business cycle, 50-year Kondratiev wave, "
220
+ "80-year financial credit cycle, and 250-year hegemony cycleโ€”through dynamic "
221
+ "charts and AI chat.</sub>"
222
+ )
223
  gr.Markdown(
224
  "<sub>"
225
  "<b>Business 9y</b> (credit-investment business cycle) โ€ข "
 
228
  "<b>Hegemony 250y</b> (rise & fall of global powers cycle)"
229
  "</sub>"
230
  )
 
231
 
232
  chart_summary_state = gr.State(value="No chart yet.")
233
 
234
  with gr.Tabs():
235
+ # โ”€โ”€ Tab 1: Timeline Chart โ”€โ”€
236
  with gr.TabItem("Timeline Chart"):
237
  with gr.Row():
238
  start_year = gr.Number(label="Start Year", value=1500, precision=0)
 
240
  zoom_in = gr.Button("๐Ÿ” Zoom In")
241
  zoom_out = gr.Button("๐Ÿ”Ž Zoom Out")
242
 
 
243
  fig0, summ0 = build_chart(1500, 2500)
244
  plot = gr.Plot(value=fig0)
245
  chart_summary_state.value = summ0
246
 
 
247
  def refresh(s, e):
248
  fig, summ = build_chart(int(s), int(e))
249
  return fig, summ
 
252
  end_year.change(refresh, [start_year, end_year],
253
  [plot, chart_summary_state])
254
 
 
255
  def zoom(s, e, factor):
256
+ mid = (s + e) / 2
257
  span = (e - s) * factor / 2
258
  ns, ne = int(mid - span), int(mid + span)
259
  fig, summ = build_chart(ns, ne)
260
  return ns, ne, fig, summ
261
 
262
  zoom_in.click(lambda s, e: zoom(s, e, 0.5),
263
+ inputs=[start_year, end_year],
264
+ outputs=[start_year, end_year, plot, chart_summary_state])
265
  zoom_out.click(lambda s, e: zoom(s, e, 2.0),
266
+ inputs=[start_year, end_year],
267
+ outputs=[start_year, end_year, plot, chart_summary_state])
268
 
 
269
  gr.File(value=str(EVENTS_PATH), label="Download cycle_events.json")
270
 
271
+ # โ”€โ”€ Tab 2: GPT Chat โ”€โ”€
272
  with gr.TabItem("GPT Chat"):
273
  chatbot = gr.Chatbot(label="Assistant")
274
  user_input = gr.Textbox(lines=3, placeholder="๋ฉ”์‹œ์ง€๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”โ€ฆ")
 
275
 
276
+ with gr.Row():
277
+ send_btn = gr.Button("Send", variant="primary")
278
+ web_btn = gr.Button("๐Ÿ”Ž Web Search") # NEW
279
+
280
+ # ๊ธฐ๋ณธ ๋‹ต๋ณ€
281
  def respond(history, msg, summ):
282
  reply = chat_with_gpt(history, msg, summ)
283
  history.append((msg, reply))
284
  return history, gr.Textbox(value="")
285
 
286
+ # ์›น ๊ฒ€์ƒ‰ ํ›„ ๋‹ต๋ณ€
287
+ def respond_with_search(history, msg, summ):
288
+ search_md = format_search_results(msg)
289
+ combined = f"{msg}\n\n{search_md}"
290
+ reply = chat_with_gpt(history, combined, summ)
291
+ history.append((f"{msg}\n\n(์›น๊ฒ€์ƒ‰ ์‹คํ–‰)", reply))
292
+ return history, gr.Textbox(value="")
293
+
294
  send_btn.click(respond,
295
  [chatbot, user_input, chart_summary_state],
296
  [chatbot, user_input])
297
  user_input.submit(respond,
298
  [chatbot, user_input, chart_summary_state],
299
  [chatbot, user_input])
300
+ web_btn.click(respond_with_search,
301
+ [chatbot, user_input, chart_summary_state],
302
+ [chatbot, user_input])
303
  return demo
304
 
305
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
306
+ # 7. main
307
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
308
  if __name__ == "__main__":
309
  create_app().launch()