Spaces:
Running
Running
File size: 9,027 Bytes
dceecec cd5d77c d403426 dceecec d403426 cd5d77c d403426 dceecec d403426 cd5d77c dceecec cd5d77c dceecec fed463b 184d70e fed463b dceecec fed463b 184d70e dceecec 184d70e fed463b dceecec 184d70e dceecec cd5d77c fed463b dceecec fed463b cd5d77c dceecec fed463b 184d70e fed463b 184d70e fed463b dceecec fed463b d403426 dceecec d403426 dceecec 312f42b 184d70e fed463b 184d70e d403426 cd5d77c dceecec 184d70e d403426 fed463b cd5d77c d403426 dceecec d403426 fed463b dceecec be40cbe dceecec be40cbe dceecec be40cbe dceecec d403426 cd5d77c dceecec d403426 fed463b dceecec fed463b dceecec cd5d77c 184d70e d403426 184d70e cd5d77c 184d70e fed463b 184d70e 312f42b cd5d77c |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# cycles_chat_app.py โ CycleNavigator (4-Cycle ๋ฒ์ )
import os, math, numpy as np, matplotlib.pyplot as plt, gradio as gr
import openai
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# 0. OpenAI API key
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
if "OPENAI_API_KEY" not in os.environ:
os.environ["OPENAI_API_KEY"] = input("๐ Enter your OpenAI API key: ").strip()
openai.api_key = os.environ["OPENAI_API_KEY"]
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# 1. Wave-style chart utilities
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
CYCLES = {
"K-Wave (50 yr)": 50, # ์ฅ๊ธฐ ์ฝ๋๋ผํฐ์ํ ํ๋
"Business Cycle (Juglar 9 yr)": 9, # ์ค๋นํฌ์ยท์ ์ฉ ๊ฒฝ๊ธฐ์ํ
"Finance Cycle (80 yr)": 80, # ์ ์ฉยท๊ธ์ต ์ฅ์ฃผ๊ธฐ
"Hegemony Cycle (250 yr)": 250, # ํจ๊ถ ํฅ๋ง
}
COLOR_MAP = {50: "#ff3333", 9: "#66ff66", 80: "#ffcc00", 250: "#66ccff"}
AMPLITUDE_MAP = {50: 1.0, 9: 0.6, 80: 1.6, 250: 4.0}
CENTER = 2025 # alignment reference
def _half_sine(xs, period, amp):
phase = np.mod(xs - CENTER, period)
y = amp * np.sin(np.pi * phase / period)
y[y < 0] = 0
return y
def build_wave_chart_and_summary(start: int, end: int):
xs = np.linspace(start, end, (end - start) * 4)
fig, ax = plt.subplots(figsize=(14, 6))
fig.subplots_adjust(top=0.9)
summaries, align_years, all_year_labels = [], None, set()
for period in sorted(set(CYCLES.values())): # iterate periods in ascending order
col, amp = COLOR_MAP[period], AMPLITUDE_MAP[period]
for frac in np.linspace(amp / 30, amp, 30):
ax.plot(xs, _half_sine(xs, period, frac),
color=col, alpha=0.85, lw=0.6)
years = [CENTER + n * period for n in range(
math.ceil((start - CENTER) / period),
math.floor((end - CENTER) / period) + 1)]
name = [k for k, v in CYCLES.items() if v == period][0]
summaries.append(f"{name} peaks: {years}")
align_years = set(years) if align_years is None else align_years & set(years)
all_year_labels.update(years)
# small baseline labels
for y in sorted(all_year_labels):
if start <= y <= end:
ax.text(y, -0.1, str(y), ha="center", va="top",
fontsize=6, color="white", rotation=90)
ax.set_facecolor("black"); fig.patch.set_facecolor("black")
ax.set_xlim(start, end)
ax.set_ylim(-0.2, max(AMPLITUDE_MAP.values()) + 0.2)
ax.set_xlabel("Year", color="white")
ax.set_ylabel("Relative Amplitude", color="white")
ax.tick_params(colors="white")
for spine in ax.spines.values():
spine.set_color("white")
ax.grid(axis="y", color="white", ls="--", lw=.3, alpha=.3)
# alignment marker
ax.axvline(CENTER, color="white", ls="--", lw=1, alpha=.6)
arrow_y = AMPLITUDE_MAP[250] * 1.05
ax.annotate("", xy=(CENTER - 125, arrow_y), xytext=(CENTER + 125, arrow_y),
arrowprops=dict(arrowstyle="<|-|>", color="white", lw=1.2))
ax.text(CENTER, arrow_y + .15, "250 yr", color="white",
ha="center", va="bottom", fontsize=10, fontweight="bold")
summary = (f"Range {start}-{end}\n"
+ "\n".join(summaries)
+ "\nAlignment year inside range: "
+ (", ".join(map(str, sorted(align_years))) if align_years else "None"))
return fig, summary
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# 2. GPT chat helper
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
BASE_PROMPT = (
"You are a concise and accurate Korean assistant. "
"Always incorporate the provided [Chart summary] when replying."
)
def chat_with_gpt(history, user_msg, chart_summary):
msgs = [{"role": "system", "content": BASE_PROMPT}]
if chart_summary != "No chart yet.":
msgs.append({"role": "system", "content": f"[Chart summary]\n{chart_summary}"})
for u, a in history:
msgs += [{"role": "user", "content": u}, {"role": "assistant", "content": a}]
msgs.append({"role": "user", "content": user_msg})
reply = openai.chat.completions.create(
model="gpt-3.5-turbo",
messages=msgs,
max_tokens=600,
temperature=0.7
).choices[0].message.content.strip()
return reply
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# 3. Gradio UI
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
custom_css = """
#wave_plot {width: 100% !important;}
#wave_plot canvas {width: 100% !important; height: auto !important;}
"""
with gr.Blocks(css=custom_css, theme=gr.themes.Soft()) as demo:
# โโ ์๋น์ค ์ ๋ชฉ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
gr.Markdown("## ๐ญ **CycleNavigator**")
# โโ ๋ค ์ฌ์ดํด ๊ฐ๋จ ์ค๋ช
(์์ ๊ธ์จ) โโโโโโโโโโโโโโโโโโโโโโโโ
gr.Markdown(
"<sub>"
"**Business (Juglar 9 yr)** โ Credit-driven capital investment booms & busts.โ"
"**K-Wave (50 yr)** โ Long-wave industrial & technological shifts.โ"
"**Finance (80 yr)** โ Extended credit cycles culminating in crises.โ"
"**Hegemony (250 yr)** โ Rise & decline of world powers."
"</sub>"
)
chart_summary_state = gr.State(value="No chart yet.")
with gr.Tabs():
# โธ Tab 1 โ Timeline Chart
with gr.TabItem("Timeline Chart"):
fig0, summ0 = build_wave_chart_and_summary(1500, 2500)
plot_out = gr.Plot(value=fig0, elem_id="wave_plot")
with gr.Row():
start_year = gr.Number(label="Start Year", value=1500)
end_year = gr.Number(label="End Year", value=2500)
zoom_in_btn = gr.Button("๐ Zoom In")
zoom_out_btn = gr.Button("๐ Zoom Out")
def refresh_chart(s, e):
fig, summ = build_wave_chart_and_summary(int(s), int(e))
return fig, summ
start_year.change(refresh_chart, [start_year, end_year],
[plot_out, chart_summary_state])
end_year.change(refresh_chart, [start_year, end_year],
[plot_out, chart_summary_state])
def zoom(s, e, factor):
mid = (s + e) / 2
span = (e - s) * factor / 2
new_s, new_e = int(mid - span), int(mid + span)
fig, summ = build_wave_chart_and_summary(new_s, new_e)
return new_s, new_e, fig, summ
zoom_in_btn.click(
lambda s, e: zoom(s, e, 0.5),
inputs=[start_year, end_year],
outputs=[start_year, end_year, plot_out, chart_summary_state],
)
zoom_out_btn.click(
lambda s, e: zoom(s, e, 2.0),
inputs=[start_year, end_year],
outputs=[start_year, end_year, plot_out, chart_summary_state],
)
# โธ Tab 2 โ GPT Chat
with gr.TabItem("GPT Chat"):
chatbot = gr.Chatbot(label="Assistant")
user_in = gr.Textbox(lines=3, placeholder="๋ฉ์์ง๋ฅผ ์
๋ ฅํ์ธ์โฆ")
send_btn = gr.Button("Send", variant="primary")
def respond(chat_hist, user_msg, summary):
ans = chat_with_gpt(chat_hist, user_msg, summary)
chat_hist.append((user_msg, ans))
return chat_hist, gr.Textbox(value="", interactive=True)
send_btn.click(respond, [chatbot, user_in, chart_summary_state],
[chatbot, user_in])
user_in.submit(respond, [chatbot, user_in, chart_summary_state],
[chatbot, user_in])
if __name__ == "__main__":
demo.launch()
|