Cycle-Navigator / app.py
openfree's picture
Update app.py
d403426 verified
raw
history blame
6.36 kB
# 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()