biftekpatates commited on
Commit
3cb5f2b
ยท
verified ยท
1 Parent(s): 5353257

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +175 -96
app.py CHANGED
@@ -1,108 +1,187 @@
 
1
  import os
2
- import gradio as gr
3
- from dotenv import load_dotenv
 
 
 
4
  from openai import OpenAI
 
 
5
  from prompts.initial_prompt import INITIAL_PROMPT
6
  from prompts.main_prompt import MAIN_PROMPT
7
 
8
- # .env ํŒŒ์ผ์—์„œ OPENAI_API_KEY ๋กœ๋“œ
9
- if os.path.exists(".env"):
10
- load_dotenv(".env")
11
-
12
  OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
 
 
13
  client = OpenAI(api_key=OPENAI_API_KEY)
14
 
15
-
16
-
17
- def gpt_call(history, user_message,
18
- model="gpt-4o-mini",
19
- max_tokens=512,
20
- temperature=0.7,
21
- top_p=0.95):
22
- """
23
- OpenAI ChatCompletion API๋ฅผ ํ†ตํ•ด ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•˜๋Š” ํ•จ์ˆ˜.
24
- - history: [(user_text, assistant_text), ...]
25
- - user_message: ์‚ฌ์šฉ์ž๊ฐ€ ๋ฐฉ๊ธˆ ์ž…๋ ฅํ•œ ๋ฉ”์‹œ์ง€
26
- """
27
- # 1) ์‹œ์Šคํ…œ ๋ฉ”์‹œ์ง€(=MAIN_PROMPT)๋ฅผ ๊ฐ€์žฅ ์•ž์— ์ถ”๊ฐ€
28
- messages = [{"role": "system", "content": MAIN_PROMPT}]
29
-
30
- # 2) ๊ธฐ์กด ๋Œ€ํ™” ๊ธฐ๋ก(history)์„ OpenAI ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜
31
- # user_text -> 'user' / assistant_text -> 'assistant'
32
- for user_text, assistant_text in history:
33
- if user_text:
34
- messages.append({"role": "user", "content": user_text})
35
- if assistant_text:
36
- messages.append({"role": "assistant", "content": assistant_text})
37
-
38
- # 3) ๋งˆ์ง€๋ง‰์— ์ด๋ฒˆ ์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ์„ ์ถ”๊ฐ€
39
- messages.append({"role": "user", "content": user_message})
40
-
41
- # 4) OpenAI API ํ˜ธ์ถœ
42
- completion = client.chat.completions.create(
43
- model=model,
44
- messages=messages,
45
- max_tokens=max_tokens,
46
- temperature=temperature,
47
- top_p=top_p
48
  )
49
- return completion.choices[0].message.content
50
-
51
- def respond(user_message, history):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  """
53
- Gradio ์ƒ์—์„œ submitํ•  ๋•Œ ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜
54
- - user_message: ์‚ฌ์šฉ์ž๊ฐ€ ๋ฐฉ๊ธˆ ์นœ ๋ฉ”์‹œ์ง€
55
- - history: ๊ธฐ์กด (user, assistant) ํŠœํ”Œ ๋ฆฌ์ŠคํŠธ
 
 
56
  """
57
- # ์‚ฌ์šฉ์ž๊ฐ€ ๋นˆ ๋ฌธ์ž์—ด์„ ๋ณด๋ƒˆ๋‹ค๋ฉด ์•„๋ฌด ์ผ๋„ ํ•˜์ง€ ์•Š์Œ
58
- if not user_message:
59
- return "", history
60
-
61
- # GPT ๋ชจ๋ธ๋กœ๋ถ€ํ„ฐ ์‘๋‹ต์„ ๋ฐ›์Œ
62
- assistant_reply = gpt_call(history, user_message)
63
-
64
- # history์— (user, assistant) ์Œ ์ถ”๊ฐ€
65
- history.append((user_message, assistant_reply))
66
-
67
- # Gradio์—์„œ๋Š” (์ƒˆ๋กœ ๋น„์›Œ์งˆ ์ž…๋ ฅ์ฐฝ, ๊ฐฑ์‹ ๋œ history)๋ฅผ ๋ฐ˜ํ™˜
68
- return "", history
69
-
70
- ##############################
71
- # Gradio Blocks UI
72
- ##############################
73
- with gr.Blocks() as demo:
74
- gr.Markdown("## Simple Chat Interface")
75
-
76
- # Chatbot ์ดˆ๊ธฐ ์ƒํƒœ๋ฅผ ์„ค์ •
77
- # ์ฒซ ๋ฒˆ์งธ ๋ฉ”์‹œ์ง€๋Š” (user="", assistant=INITIAL_PROMPT) ํ˜•ํƒœ๋กœ ๋„ฃ์–ด
78
- # ํ™”๋ฉด์ƒ์—์„œ 'assistant'๊ฐ€ INITIAL_PROMPT๋ฅผ ๋งํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๊ฒŒ ํ•จ
79
- chatbot = gr.Chatbot(
80
- value=[("", INITIAL_PROMPT)], # (user, assistant)
81
- height=500
82
- )
83
-
84
- # (user, assistant) ์Œ์„ ์ €์žฅํ•  ํžˆ์Šคํ† ๋ฆฌ ์ƒํƒœ
85
- # ์—ฌ๊ธฐ์„œ๋„ ๋™์ผํ•œ ์ดˆ๊ธฐ ์ƒํƒœ๋ฅผ ๋„ฃ์–ด์คŒ
86
- state_history = gr.State([("", INITIAL_PROMPT)])
87
-
88
- # ์‚ฌ์šฉ์ž ์ž…๋ ฅ
89
- user_input = gr.Textbox(
90
- placeholder="Type your message here...",
91
- label="Your Input"
 
 
 
 
 
 
 
 
92
  )
93
-
94
- # ์ž…๋ ฅ์ด submit๋˜๋ฉด respond() ํ˜ธ์ถœ โ†’ ์ถœ๋ ฅ์€ (์ƒˆ ์ž…๋ ฅ์ฐฝ, ๊ฐฑ์‹ ๋œ chatbot)
95
- user_input.submit(
96
- respond,
97
- inputs=[user_input, state_history],
98
- outputs=[user_input, chatbot]
99
- ).then(
100
- # respond ๋๋‚œ ๋’ค, ์ตœ์‹  history๋ฅผ state_history์— ๋ฐ˜์˜
101
- fn=lambda _, h: h,
102
- inputs=[user_input, chatbot],
103
- outputs=[state_history]
104
- )
105
-
106
- # ๋ฉ”์ธ ์‹คํ–‰
107
- if __name__ == "__main__":
108
- demo.launch(server_name="0.0.0.0", server_port=7860, share=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py โ€” FastAPI-only (no WebSockets/SSE)
2
  import os
3
+ from typing import List, Optional, Tuple
4
+
5
+ from fastapi import FastAPI
6
+ from fastapi.responses import HTMLResponse, JSONResponse
7
+ from pydantic import BaseModel, Field
8
  from openai import OpenAI
9
+
10
+ # --- Your prompts (edit these files in /prompts) ---
11
  from prompts.initial_prompt import INITIAL_PROMPT
12
  from prompts.main_prompt import MAIN_PROMPT
13
 
14
+ # Read key from Hugging Face Secret (Settings โ†’ Variables and secrets)
 
 
 
15
  OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
16
+ if not OPENAI_API_KEY:
17
+ print("WARNING: OPENAI_API_KEY secret is not set. Set it in the Space settings.")
18
  client = OpenAI(api_key=OPENAI_API_KEY)
19
 
20
+ # ---------- Models for API I/O ----------
21
+ class ChatIn(BaseModel):
22
+ message: str = Field("", description="The latest user message.")
23
+ # Optional: carry conversation history as a list of [user, assistant] turns
24
+ history: Optional[List[Tuple[str, str]]] = Field(
25
+ default=None,
26
+ description="Optional history as list of (user, assistant) tuples."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  )
28
+ model: str = Field(default="gpt-4o-mini")
29
+ temperature: float = Field(default=0.7)
30
+ top_p: float = Field(default=0.95)
31
+ max_tokens: int = Field(default=600)
32
+
33
+ class ChatOut(BaseModel):
34
+ reply: str
35
+
36
+ # ---------- Core non-streaming OpenAI call (server-side) ----------
37
+ def generate_reply(
38
+ user_message: str,
39
+ history: Optional[List[Tuple[str, str]]] = None,
40
+ model: str = "gpt-4o-mini",
41
+ temperature: float = 0.7,
42
+ top_p: float = 0.95,
43
+ max_tokens: int = 600,
44
+ ) -> str:
45
  """
46
+ Builds messages as:
47
+ system: MAIN_PROMPT
48
+ assistant: INITIAL_PROMPT (so users see your intro)
49
+ ... then optional history [(user, assistant), ...]
50
+ user: latest message
51
  """
52
+ messages = [
53
+ {"role": "system", "content": MAIN_PROMPT},
54
+ {"role": "assistant", "content": INITIAL_PROMPT},
55
+ ]
56
+
57
+ if history:
58
+ for u, a in history:
59
+ if u:
60
+ messages.append({"role": "user", "content": u})
61
+ if a:
62
+ messages.append({"role": "assistant", "content": a})
63
+
64
+ messages.append({"role": "user", "content": user_message or ""})
65
+
66
+ try:
67
+ resp = client.chat.completions.create(
68
+ model=model,
69
+ messages=messages,
70
+ temperature=temperature,
71
+ top_p=top_p,
72
+ max_tokens=max_tokens,
73
+ )
74
+ return resp.choices[0].message.content
75
+ except Exception as e:
76
+ # Return the error to the UI
77
+ return f"Error: {e}"
78
+
79
+ # ---------- FastAPI app + routes ----------
80
+ app = FastAPI(title="Module Chat โ€” Basic Mode")
81
+
82
+ @app.get("/health")
83
+ async def health():
84
+ return {"status": "ok"}
85
+
86
+ @app.post("/api/chat", response_model=ChatOut)
87
+ async def api_chat(payload: ChatIn):
88
+ reply = generate_reply(
89
+ user_message=payload.message,
90
+ history=payload.history,
91
+ model=payload.model,
92
+ temperature=payload.temperature,
93
+ top_p=payload.top_p,
94
+ max_tokens=payload.max_tokens,
95
  )
96
+ return ChatOut(reply=reply)
97
+
98
+ # Single-page minimal UI that uses ONLY plain HTTPS (no WebSockets)
99
+ INDEX_HTML = f"""
100
+ <!doctype html>
101
+ <html>
102
+ <head>
103
+ <meta charset="utf-8"/>
104
+ <meta name="viewport" content="width=device-width, initial-scale=1"/>
105
+ <title>Module Chat โ€” Basic Mode</title>
106
+ <style>
107
+ :root {{ color-scheme: light dark; }}
108
+ body {{ font-family: system-ui, Arial, sans-serif; max-width: 820px; margin: 24px auto; padding: 0 12px; }}
109
+ textarea {{ width: 100%; height: 140px; }}
110
+ #reply {{ white-space: pre-wrap; border: 1px solid #ddd; padding: 12px; border-radius: 10px; min-height: 120px; }}
111
+ button {{ padding: 10px 16px; border-radius: 10px; cursor: pointer; }}
112
+ .tip {{ color: #555; font-size: 0.95rem; }}
113
+ .row {{ display: flex; gap: 8px; align-items: center; }}
114
+ .row > * {{ flex: 1; }}
115
+ .small {{ font-size: 12px; color: #666; }}
116
+ </style>
117
+ </head>
118
+ <body>
119
+ <h1>Module Chat โ€” Basic Mode</h1>
120
+ <p class="tip">
121
+ This version uses only normal HTTPS requests (works even on strict campus Wi-Fi).<br/>
122
+ <b>Assistant:</b> {INITIAL_PROMPT}
123
+ </p>
124
+
125
+ <textarea id="msg" placeholder="Type your message..."></textarea>
126
+ <div class="row">
127
+ <button id="send">Send</button>
128
+ <label style="flex:0 0 auto">
129
+ <input type="checkbox" id="keepHistory" checked /> keep history
130
+ </label>
131
+ </div>
132
+
133
+ <h3>Reply</h3>
134
+ <div id="reply"></div>
135
+
136
+ <p class="small">Tip: Press Ctrl/Cmd+Enter to send.</p>
137
+
138
+ <script>
139
+ const btn = document.getElementById("send");
140
+ const msg = document.getElementById("msg");
141
+ const out = document.getElementById("reply");
142
+ const keep = document.getElementById("keepHistory");
143
+ let history = [];
144
+
145
+ async function sendOnce() {{
146
+ out.textContent = "Thinking...";
147
+ const payload = {{
148
+ message: msg.value || "",
149
+ history: keep.checked ? history : null
150
+ }};
151
+ try {{
152
+ const res = await fetch("/api/chat", {{
153
+ method: "POST",
154
+ headers: {{ "Content-Type": "application/json" }},
155
+ body: JSON.stringify(payload)
156
+ }});
157
+ if (!res.ok) {{
158
+ const txt = await res.text();
159
+ out.textContent = "Server error: " + res.status + " " + txt;
160
+ return;
161
+ }}
162
+ const data = await res.json();
163
+ const reply = data.reply || "(no reply)";
164
+ out.textContent = reply;
165
+ if (keep.checked) {{
166
+ history.push([msg.value || "", reply]);
167
+ }}
168
+ }} catch (e) {{
169
+ out.textContent = "Network error: " + e;
170
+ }}
171
+ }}
172
+
173
+ btn.onclick = sendOnce;
174
+ msg.addEventListener("keydown", (ev) => {{
175
+ if (ev.key === "Enter" && (ev.ctrlKey || ev.metaKey)) {{
176
+ ev.preventDefault();
177
+ sendOnce();
178
+ }}
179
+ }});
180
+ </script>
181
+ </body>
182
+ </html>
183
+ """
184
+
185
+ @app.get("/", response_class=HTMLResponse)
186
+ async def root():
187
+ return INDEX_HTML