File size: 6,364 Bytes
cd5d77c
 
 
 
d403426
 
 
cd5d77c
 
 
 
d403426
 
 
cd5d77c
d403426
 
 
cd5d77c
d403426
cd5d77c
 
 
 
 
 
d403426
 
 
 
cd5d77c
d403426
 
 
cd5d77c
d403426
 
cd5d77c
 
 
 
d403426
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
312f42b
 
d403426
 
 
 
 
cd5d77c
d403426
 
 
cd5d77c
 
 
 
 
 
 
 
d403426
cd5d77c
d403426
 
 
cd5d77c
d403426
 
 
cd5d77c
 
d403426
 
 
 
f6b850a
d403426
 
cd5d77c
 
d403426
 
f6b850a
d403426
 
 
 
cd5d77c
d403426
 
 
cd5d77c
 
 
d403426
 
cd5d77c
 
 
 
 
d403426
cd5d77c
 
 
 
d403426
cd5d77c
 
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
# cycles_chat_app.py
import os, math, numpy as np, matplotlib.pyplot as plt, gradio as gr
import openai

# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# 0. OpenAI 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. Cycle-chart utilities
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
CYCLES = {
    "Tech Cycle (50 yr)": 50,
    "Finance Cycle (80 yr)": 80,
    "Hegemony Cycle (250 yr)": 250,
}
CENTER = 2025  # reference year

def make_cycle_points(period, start, end):
    n_min = math.floor((start - CENTER) / period)
    n_max = math.ceil((end   - CENTER) / period)
    return [CENTER + n * period for n in range(n_min, n_max + 1)]

def build_chart_and_summary(start, end):
    # --------- build figure (tower style) ----------
    fig, ax = plt.subplots(figsize=(7, 5))
    align_years, summaries = None, []

    for idx, (name, period) in enumerate(CYCLES.items()):
        years = make_cycle_points(period, start, end)
        ax.scatter([idx] * len(years), years, s=60)
        for y in years:
            ax.text(idx, y + 5, str(y), ha="center", va="bottom", fontsize=7)
        summaries.append(f"{name.replace(' ', ' ')}: {years}")
        align_years = set(years) if align_years is None else align_years & set(years)

    if align_years:
        for y in sorted(align_years):
            ax.axhline(y, color="black", linestyle="--", linewidth=1)
            ax.text(-0.4, y + 8, f"Alignment โ†’ {y}", fontsize=9, fontweight="bold")

    ax.set_xticks(range(len(CYCLES)), CYCLES.keys(), rotation=15)
    ax.set_ylabel("Year")
    ax.set_xlabel("Cycle")
    ax.set_title("Cycle Events and Alignment")
    ax.set_ylim(start - 10, end + 10)
    ax.invert_yaxis()               # older years at bottom, newer at top
    ax.grid(axis="y", alpha=0.3)

    # --------- build textual summary ----------
    align_txt = ", ".join(map(str, sorted(align_years))) if align_years else "None"
    summary = (
        f"Range: {start}-{end}\n"
        + "\n".join(summaries)
        + f"\nAlignment years: {align_txt}"
    )
    return fig, summary

# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# 2. GPT chat
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
BASE_PROMPT = (
    "You are a concise, accurate assistant who answers in Korean. "
    "If the chart summary is provided, take it into account."
)

def chat_with_gpt(history, user_message, chart_summary):
    messages = [{"role": "system", "content": BASE_PROMPT}]
    if chart_summary and chart_summary != "No chart yet.":
        messages.append({"role": "system", "content": f"[Chart summary]\n{chart_summary}"})

    for u, a in history:
        messages.extend([{"role": "user", "content": u},
                         {"role": "assistant", "content": a}])

    messages.append({"role": "user", "content": user_message})

    response = openai.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=messages,
        max_tokens=600,
        temperature=0.7,
    )
    return response.choices[0].message.content.strip()

# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# 3. Gradio UI
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
with gr.Blocks(theme=gr.themes.Soft()) as demo:
    gr.Markdown("## ๐Ÿ“Š Three-Cycle Timeline & ๐Ÿ’ฌ GPT Chat")

    chart_summary_state = gr.State(value="No chart yet.")

    with gr.Tabs():
        # ---------- Tab 1: Chart ----------
        with gr.TabItem("Timeline Chart"):
            start_year = gr.Number(label="Start Year", value=1500)
            end_year   = gr.Number(label="End Year",   value=2500)

            fig0, summary0 = build_chart_and_summary(1500, 2500)
            plot_out = gr.Plot(value=fig0)

            def update_chart(s, e):
                fig, summ = build_chart_and_summary(int(s), int(e))
                return fig, summ

            start_year.change(update_chart, [start_year, end_year],
                              [plot_out, chart_summary_state])
            end_year.change(update_chart,   [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(placeholder="๋ฉ”์‹œ์ง€๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”...", lines=3)
            send_btn = gr.Button("Send", variant="primary")

            def respond(chat_history, user_msg, chart_summary):
                assistant_msg = chat_with_gpt(chat_history, user_msg, chart_summary)
                chat_history.append((user_msg, assistant_msg))
                return chat_history, gr.Textbox(value="", interactive=True)

            send_btn.click(
                respond,
                inputs=[chatbot, user_in, chart_summary_state],
                outputs=[chatbot, user_in],
            )
            user_in.submit(
                respond,
                inputs=[chatbot, user_in, chart_summary_state],
                outputs=[chatbot, user_in],
            )

if __name__ == "__main__":
    demo.launch()