gooookim commited on
Commit
2344ef4
ยท
verified ยท
1 Parent(s): 9d466f8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +200 -55
app.py CHANGED
@@ -1,70 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
2
- from huggingface_hub import InferenceClient
3
 
4
 
5
- def respond(
6
- message,
7
- history: list[dict[str, str]],
8
- system_message,
9
- max_tokens,
10
- temperature,
11
- top_p,
12
- hf_token: gr.OAuthToken,
13
- ):
14
- """
15
- For more information on `huggingface_hub` Inference API support, please check the docs: https://huggingface.co/docs/huggingface_hub/v0.22.2/en/guides/inference
16
- """
17
- client = InferenceClient(token=hf_token.token, model="openai/gpt-oss-20b")
18
 
19
- messages = [{"role": "system", "content": system_message}]
20
 
21
- messages.extend(history)
 
 
22
 
23
- messages.append({"role": "user", "content": message})
24
 
25
- response = ""
 
 
 
 
 
 
26
 
27
- for message in client.chat_completion(
28
- messages,
29
- max_tokens=max_tokens,
30
- stream=True,
31
- temperature=temperature,
32
- top_p=top_p,
33
- ):
34
- choices = message.choices
35
- token = ""
36
- if len(choices) and choices[0].delta.content:
37
- token = choices[0].delta.content
38
 
39
- response += token
40
- yield response
 
 
 
 
 
 
 
 
 
41
 
42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  """
44
- For information on how to customize the ChatInterface, peruse the gradio docs: https://www.gradio.app/docs/chatinterface
45
- """
46
- chatbot = gr.ChatInterface(
47
- respond,
48
- type="messages",
49
- additional_inputs=[
50
- gr.Textbox(value="You are a friendly Chatbot.", label="System message"),
51
- gr.Slider(minimum=1, maximum=2048, value=512, step=1, label="Max new tokens"),
52
- gr.Slider(minimum=0.1, maximum=4.0, value=0.7, step=0.1, label="Temperature"),
53
- gr.Slider(
54
- minimum=0.1,
55
- maximum=1.0,
56
- value=0.95,
57
- step=0.05,
58
- label="Top-p (nucleus sampling)",
59
- ),
60
- ],
61
- )
62
-
63
- with gr.Blocks() as demo:
64
- with gr.Sidebar():
65
- gr.LoginButton()
66
- chatbot.render()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
68
 
69
  if __name__ == "__main__":
70
- demo.launch()
 
 
1
+ # app.py
2
+ # Naver News Search API -> Gradio chat-like UI (HF Spaces friendly)
3
+ #
4
+ # HF Spaces Secrets ์„ค์ •:
5
+ # NAVER_CLIENT_ID = ๋ฐœ๊ธ‰๋ฐ›์€ Client ID
6
+ # NAVER_CLIENT_SECRET = ๋ฐœ๊ธ‰๋ฐ›์€ Client Secret
7
+ #
8
+ # ๋กœ์ปฌ ์‹คํ–‰ ์‹œ(์„ ํƒ):
9
+ # export NAVER_CLIENT_ID="..."
10
+ # export NAVER_CLIENT_SECRET="..."
11
+
12
+ import os
13
+ import html
14
+ import re
15
+ from datetime import datetime
16
+ from typing import Dict, Any, List, Tuple, Optional
17
+
18
+ import requests
19
  import gradio as gr
 
20
 
21
 
22
+ NAVER_NEWS_ENDPOINT = "https://openapi.naver.com/v1/search/news.json"
 
 
 
 
 
 
 
 
 
 
 
 
23
 
 
24
 
25
+ def _get_env(name: str) -> str:
26
+ v = os.getenv(name, "").strip()
27
+ return v
28
 
 
29
 
30
+ def _strip_tags(text: str) -> str:
31
+ # ๋„ค์ด๋ฒ„ ๋‰ด์Šค API ๊ฒฐ๊ณผ์˜ title/description์€ <b>...</b>๊ฐ€ ํฌํ•จ๋  ์ˆ˜ ์žˆ์–ด ์ œ๊ฑฐ
32
+ # (HTML ํƒœ๊ทธ ์ œ๊ฑฐ + ์—”ํ‹ฐํ‹ฐ ์ฒ˜๋ฆฌ)
33
+ if not text:
34
+ return ""
35
+ text = re.sub(r"<[^>]+>", "", text)
36
+ return html.unescape(text).strip()
37
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
+ def _format_pubdate(pub_date: str) -> str:
40
+ # ์˜ˆ: "Mon, 19 Jan 2026 09:30:00 +0900"
41
+ # ํŒŒ์‹ฑ ์‹คํŒจ ์‹œ ์›๋ฌธ ๋ฐ˜ํ™˜
42
+ if not pub_date:
43
+ return ""
44
+ try:
45
+ dt = datetime.strptime(pub_date, "%a, %d %b %Y %H:%M:%S %z")
46
+ # ํ•œ๊ตญ ํ™˜๊ฒฝ์—์„œ ๋ณด๊ธฐ ์ข‹์€ ํฌ๋งท
47
+ return dt.strftime("%Y-%m-%d %H:%M:%S %z")
48
+ except Exception:
49
+ return pub_date
50
 
51
 
52
+ def naver_news_search(
53
+ query: str,
54
+ display: int = 10,
55
+ sort: str = "date",
56
+ start: int = 1,
57
+ timeout: int = 10,
58
+ ) -> Dict[str, Any]:
59
+ client_id = _get_env("NAVER_CLIENT_ID")
60
+ client_secret = _get_env("NAVER_CLIENT_SECRET")
61
+
62
+ if not client_id or not client_secret:
63
+ raise RuntimeError(
64
+ "ํ™˜๊ฒฝ๋ณ€์ˆ˜ NAVER_CLIENT_ID / NAVER_CLIENT_SECRET ์ด ์„ค์ •๋˜์–ด ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. "
65
+ "Hugging Face Spaces์˜ Secrets์— ์ถ”๊ฐ€ํ•ด ์ฃผ์„ธ์š”."
66
+ )
67
+
68
+ headers = {
69
+ "X-Naver-Client-Id": client_id,
70
+ "X-Naver-Client-Secret": client_secret,
71
+ }
72
+
73
+ params = {
74
+ "query": query,
75
+ "display": int(display),
76
+ "start": int(start),
77
+ "sort": sort,
78
+ }
79
+
80
+ r = requests.get(
81
+ NAVER_NEWS_ENDPOINT,
82
+ headers=headers,
83
+ params=params,
84
+ timeout=timeout,
85
+ )
86
+
87
+ # ๋„ค์ด๋ฒ„ API๊ฐ€ ์—๋Ÿฌ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒฝ์šฐ, body์— ์ƒ์„ธ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์–ด ๊ทธ๋Œ€๋กœ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•ด ์ฒ˜๋ฆฌ
88
+ if r.status_code != 200:
89
+ try:
90
+ detail = r.json()
91
+ except Exception:
92
+ detail = {"text": r.text}
93
+ raise RuntimeError(f"๋„ค์ด๋ฒ„ API ํ˜ธ์ถœ ์‹คํŒจ (HTTP {r.status_code}): {detail}")
94
+
95
+ return r.json()
96
+
97
+
98
+ def render_results(data: Dict[str, Any], max_items: int = 10) -> str:
99
+ items = data.get("items", [])[:max_items]
100
+ total = data.get("total", None)
101
+
102
+ lines: List[str] = []
103
+ if total is not None:
104
+ lines.append(f"- ์ด ๊ฒ€์ƒ‰๊ฒฐ๊ณผ: {total:,}๊ฑด")
105
+ lines.append(f"- ๋ฐ˜ํ™˜ ๊ฐœ์ˆ˜: {len(items)}๊ฑด")
106
+ lines.append("")
107
+
108
+ for i, it in enumerate(items, start=1):
109
+ title = _strip_tags(it.get("title", ""))
110
+ desc = _strip_tags(it.get("description", ""))
111
+ link = it.get("link", "")
112
+ origin = it.get("originallink", "")
113
+ pub = _format_pubdate(it.get("pubDate", ""))
114
+
115
+ # ๊ฐ€๋…์„ฑ์„ ์œ„ํ•ด ํ•ญ๋ชฉ๋ณ„ ๋ธ”๋ก ํ˜•ํƒœ๋กœ ๊ตฌ์„ฑ
116
+ lines.append(f"{i}. **{title}**")
117
+ if pub:
118
+ lines.append(f" - ๋ฐœํ–‰: {pub}")
119
+ if origin:
120
+ lines.append(f" - ์›๋ฌธ: {origin}")
121
+ if link:
122
+ lines.append(f" - ๋งํฌ: {link}")
123
+ if desc:
124
+ lines.append(f" - ์š”์•ฝ: {desc}")
125
+ lines.append("")
126
+
127
+ return "\n".join(lines).strip()
128
+
129
+
130
+ def handle_search(
131
+ user_query: str,
132
+ chat_history: List[Dict[str, str]],
133
+ display: int,
134
+ sort: str,
135
+ ) -> Tuple[List[Dict[str, str]], str]:
136
+ q = (user_query or "").strip()
137
+ if not q:
138
+ return chat_history, ""
139
+
140
+ # ์‚ฌ์šฉ์ž ๋ฉ”์‹œ์ง€ ์ถ”๊ฐ€
141
+ chat_history = chat_history + [{"role": "user", "content": q}]
142
+
143
+ try:
144
+ data = naver_news_search(query=q, display=display, sort=sort, start=1)
145
+ assistant_text = render_results(data, max_items=display)
146
+ except Exception as e:
147
+ assistant_text = f"์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.\n\n- {e}"
148
+
149
+ # ์‘๋‹ต ์ถ”๊ฐ€
150
+ chat_history = chat_history + [{"role": "assistant", "content": assistant_text}]
151
+ return chat_history, ""
152
+
153
+
154
+ with gr.Blocks(title="Naver News Search (Chat UI)") as demo:
155
+ gr.Markdown(
156
+ """
157
+ # ๋„ค์ด๋ฒ„ ๋‰ด์Šค ๊ฒ€์ƒ‰ ํ…Œ์ŠคํŠธ (Gradio)
158
+ - ํ•˜๋‹จ ์ž…๋ ฅ์ฐฝ์— ๊ฒ€์ƒ‰์–ด๋ฅผ ์ž…๋ ฅํ•˜๋ฉด, ๊ฒฐ๊ณผ๋ฅผ **์ฑ„ํŒ… ํ˜•ํƒœ**๋กœ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.
159
+ - ์ธ์ฆํ‚ค๋Š” Hugging Face Spaces **Secrets**์˜ `NAVER_CLIENT_ID`, `NAVER_CLIENT_SECRET`์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
160
  """
161
+ )
162
+
163
+ with gr.Row():
164
+ display = gr.Slider(
165
+ minimum=1, maximum=20, value=10, step=1, label="ํ‘œ์‹œ ๊ฐœ์ˆ˜(display)"
166
+ )
167
+ sort = gr.Dropdown(
168
+ choices=["date", "sim"],
169
+ value="date",
170
+ label="์ •๋ ฌ(sort)",
171
+ info="date=์ตœ์‹ ์ˆœ, sim=์ •ํ™•๋„์ˆœ",
172
+ )
173
+
174
+ chatbot = gr.Chatbot(
175
+ value=[],
176
+ label="๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ",
177
+ type="messages",
178
+ height=520,
179
+ )
180
+
181
+ # ํ•˜๋‹จ ์ž…๋ ฅ ์˜์—ญ(์š”์ฒญํ•˜์‹  '๋ฐ”๋‹ฅ' ๋ฐฐ์น˜)
182
+ with gr.Row():
183
+ query_in = gr.Textbox(
184
+ label="๊ฒ€์ƒ‰์–ด",
185
+ placeholder="์˜ˆ: ์ธ๊ณต์ง€๋Šฅ, ๊ฐ์‚ฌ์›, ๋ฐฉ์œ„์‚ฐ์—…, ๋ฐ˜๋„์ฒด ...",
186
+ scale=8,
187
+ )
188
+ search_btn = gr.Button("๊ฒ€์ƒ‰", variant="primary", scale=2)
189
+
190
+ with gr.Row():
191
+ clear_btn = gr.Button("๋Œ€ํ™” ์ง€์šฐ๊ธฐ", variant="secondary")
192
+
193
+ # ์ด๋ฒคํŠธ ๋ฐ”์ธ๋”ฉ: ์—”ํ„ฐ/๋ฒ„ํŠผ ๋ชจ๋‘ ๋™์ผ ์ฒ˜๋ฆฌ
194
+ search_btn.click(
195
+ fn=handle_search,
196
+ inputs=[query_in, chatbot, display, sort],
197
+ outputs=[chatbot, query_in],
198
+ api_name="search",
199
+ )
200
+ query_in.submit(
201
+ fn=handle_search,
202
+ inputs=[query_in, chatbot, display, sort],
203
+ outputs=[chatbot, query_in],
204
+ api_name="search_submit",
205
+ )
206
+
207
+ def clear_chat():
208
+ return []
209
+
210
+ clear_btn.click(fn=clear_chat, inputs=[], outputs=[chatbot], api_name="clear")
211
 
212
 
213
  if __name__ == "__main__":
214
+ # HF Spaces์—์„œ๋Š” ๊ธฐ๋ณธ์œผ๋กœ 7860 ํฌํŠธ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
215
+ demo.launch(server_name="0.0.0.0", server_port=7860)