openfree commited on
Commit
184d70e
ยท
verified ยท
1 Parent(s): d403426

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +80 -82
app.py CHANGED
@@ -10,129 +10,127 @@ if "OPENAI_API_KEY" not in os.environ:
10
  openai.api_key = os.environ["OPENAI_API_KEY"]
11
 
12
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
13
- # 1. Cycle-chart utilities
14
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
15
  CYCLES = {
16
  "Tech Cycle (50 yr)": 50,
17
  "Finance Cycle (80 yr)": 80,
18
  "Hegemony Cycle (250 yr)": 250,
19
  }
20
- CENTER = 2025 # reference year
21
-
22
- def make_cycle_points(period, start, end):
23
- n_min = math.floor((start - CENTER) / period)
24
- n_max = math.ceil((end - CENTER) / period)
25
- return [CENTER + n * period for n in range(n_min, n_max + 1)]
26
-
27
- def build_chart_and_summary(start, end):
28
- # --------- build figure (tower style) ----------
29
- fig, ax = plt.subplots(figsize=(7, 5))
30
- align_years, summaries = None, []
31
-
32
- for idx, (name, period) in enumerate(CYCLES.items()):
33
- years = make_cycle_points(period, start, end)
34
- ax.scatter([idx] * len(years), years, s=60)
35
- for y in years:
36
- ax.text(idx, y + 5, str(y), ha="center", va="bottom", fontsize=7)
37
- summaries.append(f"{name.replace(' ', ' ')}: {years}")
 
 
 
 
 
 
 
 
 
 
38
  align_years = set(years) if align_years is None else align_years & set(years)
39
 
40
- if align_years:
41
- for y in sorted(align_years):
42
- ax.axhline(y, color="black", linestyle="--", linewidth=1)
43
- ax.text(-0.4, y + 8, f"Alignment โ†’ {y}", fontsize=9, fontweight="bold")
44
-
45
- ax.set_xticks(range(len(CYCLES)), CYCLES.keys(), rotation=15)
46
- ax.set_ylabel("Year")
47
- ax.set_xlabel("Cycle")
48
- ax.set_title("Cycle Events and Alignment")
49
- ax.set_ylim(start - 10, end + 10)
50
- ax.invert_yaxis() # older years at bottom, newer at top
51
- ax.grid(axis="y", alpha=0.3)
52
-
53
- # --------- build textual summary ----------
54
- align_txt = ", ".join(map(str, sorted(align_years))) if align_years else "None"
55
- summary = (
56
- f"Range: {start}-{end}\n"
57
- + "\n".join(summaries)
58
- + f"\nAlignment years: {align_txt}"
59
- )
60
  return fig, summary
61
 
62
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€๏ฟฝ๏ฟฝโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
63
- # 2. GPT chat
64
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
65
  BASE_PROMPT = (
66
- "You are a concise, accurate assistant who answers in Korean. "
67
- "If the chart summary is provided, take it into account."
68
  )
69
 
70
- def chat_with_gpt(history, user_message, chart_summary):
71
- messages = [{"role": "system", "content": BASE_PROMPT}]
72
- if chart_summary and chart_summary != "No chart yet.":
73
- messages.append({"role": "system", "content": f"[Chart summary]\n{chart_summary}"})
74
 
75
  for u, a in history:
76
- messages.extend([{"role": "user", "content": u},
77
- {"role": "assistant", "content": a}])
78
 
79
- messages.append({"role": "user", "content": user_message})
80
-
81
- response = openai.chat.completions.create(
82
- model="gpt-3.5-turbo",
83
- messages=messages,
84
- max_tokens=600,
85
- temperature=0.7,
86
- )
87
- return response.choices[0].message.content.strip()
88
 
89
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
90
- # 3. Gradio UI
91
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
92
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
93
- gr.Markdown("## ๐Ÿ“Š Three-Cycle Timeline & ๐Ÿ’ฌ GPT Chat")
94
 
95
  chart_summary_state = gr.State(value="No chart yet.")
96
 
97
  with gr.Tabs():
98
- # ---------- Tab 1: Chart ----------
99
  with gr.TabItem("Timeline Chart"):
100
  start_year = gr.Number(label="Start Year", value=1500)
101
  end_year = gr.Number(label="End Year", value=2500)
102
 
103
- fig0, summary0 = build_chart_and_summary(1500, 2500)
104
  plot_out = gr.Plot(value=fig0)
105
 
106
- def update_chart(s, e):
107
- fig, summ = build_chart_and_summary(int(s), int(e))
108
  return fig, summ
109
 
110
- start_year.change(update_chart, [start_year, end_year],
111
  [plot_out, chart_summary_state])
112
- end_year.change(update_chart, [start_year, end_year],
113
  [plot_out, chart_summary_state])
114
 
115
- # ---------- Tab 2: GPT Chat ----------
116
  with gr.TabItem("GPT Chat"):
117
  chatbot = gr.Chatbot(label="Assistant")
118
- user_in = gr.Textbox(placeholder="๋ฉ”์‹œ์ง€๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”...", lines=3)
119
  send_btn = gr.Button("Send", variant="primary")
120
 
121
- def respond(chat_history, user_msg, chart_summary):
122
- assistant_msg = chat_with_gpt(chat_history, user_msg, chart_summary)
123
- chat_history.append((user_msg, assistant_msg))
124
- return chat_history, gr.Textbox(value="", interactive=True)
125
-
126
- send_btn.click(
127
- respond,
128
- inputs=[chatbot, user_in, chart_summary_state],
129
- outputs=[chatbot, user_in],
130
- )
131
- user_in.submit(
132
- respond,
133
- inputs=[chatbot, user_in, chart_summary_state],
134
- outputs=[chatbot, user_in],
135
- )
136
 
137
  if __name__ == "__main__":
138
  demo.launch()
 
10
  openai.api_key = os.environ["OPENAI_API_KEY"]
11
 
12
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
13
+ # 1. Wave-style chart utilities (โ† NEW)
14
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
15
  CYCLES = {
16
  "Tech Cycle (50 yr)": 50,
17
  "Finance Cycle (80 yr)": 80,
18
  "Hegemony Cycle (250 yr)": 250,
19
  }
20
+ COLOR_MAP = {50: "#ff3333", 80: "#ffcc00", 250: "#66ccff"} # red / yellow / blue
21
+ AMPLITUDE_MAP = {50: 1.0, 80: 1.6, 250: 4.0} # taller tower for longer cycle
22
+ CENTER = 2025 # alignment reference
23
+
24
+ def _half_sine(xs, period, amp):
25
+ """positive half-sine wave, aligned so peak at CENTER, 0 elsewhere"""
26
+ phase = np.mod(xs - CENTER, period)
27
+ y = amp * np.sin(np.pi * phase / period)
28
+ y[y < 0] = 0
29
+ return y
30
+
31
+ def build_wave_chart_and_summary(start: int, end: int):
32
+ """return (matplotlib figure, text summary)"""
33
+ xs = np.linspace(start, end, (end - start) * 4) # 4 pts/yr โ‡’ smooth
34
+ fig, ax = plt.subplots(figsize=(10, 3))
35
+ summaries, align_years = [], None
36
+
37
+ # draw many stacked half-sine lines for each cycle โ†’ โ€œtowerโ€ ๋А๋‚Œ
38
+ for period in sorted(CYCLES.values()):
39
+ col, amp = COLOR_MAP[period], AMPLITUDE_MAP[period]
40
+ for frac in np.linspace(amp / 30, amp, 30): # 30 concentric lines
41
+ ax.plot(xs, _half_sine(xs, period, frac),
42
+ color=col, alpha=0.85, lw=0.6)
43
+
44
+ years = [CENTER + n * period for n in range(
45
+ math.ceil((start - CENTER) / period),
46
+ math.floor((end - CENTER) / period) + 1)]
47
+ summaries.append(f"{period}-yr cycle peaks: {years}")
48
  align_years = set(years) if align_years is None else align_years & set(years)
49
 
50
+ # cosmeticโ€Šโ€”โ€Šblack bg & white grid
51
+ ax.set_facecolor("black"); fig.patch.set_facecolor("black")
52
+ ax.set_xlim(start, end); ax.set_ylim(0, max(AMPLITUDE_MAP.values()) + .2)
53
+ ax.set_xlabel("Year", color="white"); ax.set_ylabel("Relative Amplitude", color="white")
54
+ ax.set_title("Technology ยท Finance ยท Hegemony Cycles", color="white", pad=20)
55
+ ax.tick_params(colors="white"); [s.set_color("white") for s in ax.spines.values()]
56
+ ax.grid(color="white", ls="--", lw=.3, alpha=.3)
57
+
58
+ # vertical dashed line at alignment year + 250-yr span arrow
59
+ ax.axvline(CENTER, color="white", ls="--", lw=1, alpha=.6)
60
+ arrow_y = AMPLITUDE_MAP[250] * 1.05
61
+ ax.annotate("", xy=(CENTER - 125, arrow_y), xytext=(CENTER + 125, arrow_y),
62
+ arrowprops=dict(arrowstyle="<|-|>", color="white", lw=1.2))
63
+ ax.text(CENTER, arrow_y + .15, "250 yr", color="white",
64
+ ha="center", va="bottom", fontsize=10, fontweight="bold")
65
+
66
+ summary = (f"Range {start}-{end}\n" +
67
+ "\n".join(summaries) +
68
+ f"\nAlignment year inside range: {', '.join(map(str, sorted(align_years))) if align_years else 'None'}")
 
69
  return fig, summary
70
 
71
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€๏ฟฝ๏ฟฝโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
72
+ # 2. GPT chat (์ฐจํŠธ ์š”์•ฝ โ†’ system ํ”„๋กฌํ”„ํŠธ์— ์ž๋™ ์‚ฝ์ž…)
73
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
74
  BASE_PROMPT = (
75
+ "๋‹น์‹ ์€ ๊ฐ„๊ฒฐํ•˜๊ณ  ์ •ํ™•ํ•œ ํ•œ๊ตญ์–ด ์–ด์‹œ์Šคํ„ดํŠธ์ž…๋‹ˆ๋‹ค. "
76
+ "์‚ฌ์šฉ์ž์˜ ์งˆ๋ฌธ์— ๋‹ตํ•  ๋•Œ, ์ œ๊ณต๋œ [Chart summary] ๋‚ด์šฉ์„ ๋ฐ˜๋“œ์‹œ ๋ฐ˜์˜ํ•˜์„ธ์š”."
77
  )
78
 
79
+ def chat_with_gpt(history, user_msg, chart_summary):
80
+ msgs = [{"role": "system", "content": BASE_PROMPT}]
81
+ if chart_summary != "No chart yet.": # ์ฐจํŠธ๊ฐ€ ์ด๋ฏธ ๊ทธ๋ ค์กŒ๋‹ค๋ฉด
82
+ msgs.append({"role": "system", "content": f"[Chart summary]\n{chart_summary}"})
83
 
84
  for u, a in history:
85
+ msgs += [{"role": "user", "content": u}, {"role": "assistant", "content": a}]
86
+ msgs.append({"role": "user", "content": user_msg})
87
 
88
+ answer = openai.chat.completions.create(
89
+ model="gpt-3.5-turbo", messages=msgs,
90
+ max_tokens=600, temperature=0.7).choices[0].message.content.strip()
91
+ return answer
 
 
 
 
 
92
 
93
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
94
+ # 3. Gradio UI
95
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
96
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
97
+ gr.Markdown("## ๐Ÿ“Š Wave-style Cycle Timeline & ๐Ÿ’ฌ GPT Chat")
98
 
99
  chart_summary_state = gr.State(value="No chart yet.")
100
 
101
  with gr.Tabs():
102
+ # โ–ธ Tab 1 โ€” Chart
103
  with gr.TabItem("Timeline Chart"):
104
  start_year = gr.Number(label="Start Year", value=1500)
105
  end_year = gr.Number(label="End Year", value=2500)
106
 
107
+ fig0, summ0 = build_wave_chart_and_summary(1500, 2500)
108
  plot_out = gr.Plot(value=fig0)
109
 
110
+ def refresh_chart(s, e):
111
+ fig, summ = build_wave_chart_and_summary(int(s), int(e))
112
  return fig, summ
113
 
114
+ start_year.change(refresh_chart, [start_year, end_year],
115
  [plot_out, chart_summary_state])
116
+ end_year.change(refresh_chart, [start_year, end_year],
117
  [plot_out, chart_summary_state])
118
 
119
+ # โ–ธ Tab 2 โ€” GPT Chat
120
  with gr.TabItem("GPT Chat"):
121
  chatbot = gr.Chatbot(label="Assistant")
122
+ user_in = gr.Textbox(lines=3, placeholder="๋ฉ”์‹œ์ง€๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”โ€ฆ")
123
  send_btn = gr.Button("Send", variant="primary")
124
 
125
+ def respond(chat_hist, user_msg, summary):
126
+ reply = chat_with_gpt(chat_hist, user_msg, summary)
127
+ chat_hist.append((user_msg, reply))
128
+ return chat_hist, gr.Textbox(value="", interactive=True)
129
+
130
+ send_btn.click(respond, [chatbot, user_in, chart_summary_state],
131
+ [chatbot, user_in])
132
+ user_in.submit(respond, [chatbot, user_in, chart_summary_state],
133
+ [chatbot, user_in])
 
 
 
 
 
 
134
 
135
  if __name__ == "__main__":
136
  demo.launch()