openfree commited on
Commit
2284074
·
verified ·
1 Parent(s): 6ce4225

Create app-backup.py

Browse files
Files changed (1) hide show
  1. app-backup.py +385 -0
app-backup.py ADDED
@@ -0,0 +1,385 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ import requests
4
+ import json
5
+ import time
6
+ from dotenv import load_dotenv
7
+
8
+ # .env 파일 로드 (있는 경우)
9
+ load_dotenv()
10
+
11
+ def create_deepseek_interface():
12
+ # 환경 변수에서 API 키 가져오기
13
+ api_key = os.getenv("FW_API_KEY")
14
+ serphouse_api_key = os.getenv("SERPHOUSE_API_KEY")
15
+
16
+ if not api_key:
17
+ print("경고: FW_API_KEY 환경 변수가 설정되지 않았습니다.")
18
+ if not serphouse_api_key:
19
+ print("경고: SERPHOUSE_API_KEY 환경 변수가 설정되지 않았습니다.")
20
+
21
+ # 키워드 추출 함수 (LLM 기반)
22
+ def extract_keywords_with_llm(query):
23
+ if not api_key:
24
+ return "LLM 키워드 추출을 위한 FW_API_KEY가 설정되지 않았습니다.", query
25
+
26
+ # LLM을 사용하여 키워드 추출 (DeepSeek 모델 사용)
27
+ url = "https://api.fireworks.ai/inference/v1/chat/completions"
28
+ payload = {
29
+ "model": "accounts/fireworks/models/deepseek-v3-0324",
30
+ "max_tokens": 200,
31
+ "temperature": 0.1, # 일관된 결과를 위해 낮은 온도 사용
32
+ "messages": [
33
+ {
34
+ "role": "system",
35
+ "content": "사용자의 질문에서 웹 검색에 효과적인 핵심 키워드를 추출하세요. 키워드 사이에 쉼표나 공백을 넣지 말고 하나의 검색어처럼 수정해서 제공하세요. 예를 들어 '한덕수 국무총리 탄핵 결과'처럼 공백으로만 구분하세요."
36
+ },
37
+ {
38
+ "role": "user",
39
+ "content": query
40
+ }
41
+ ]
42
+ }
43
+ headers = {
44
+ "Accept": "application/json",
45
+ "Content-Type": "application/json",
46
+ "Authorization": f"Bearer {api_key}"
47
+ }
48
+
49
+ try:
50
+ response = requests.post(url, headers=headers, json=payload)
51
+ response.raise_for_status()
52
+ result = response.json()
53
+
54
+ # 응답에서 키워드 추출
55
+ keywords = result["choices"][0]["message"]["content"].strip()
56
+
57
+ # 키워드가 너무 길거나 형식이 잘못된 경우 원본 쿼리 사용
58
+ if len(keywords) > 100:
59
+ return f"추출된 키워드: {keywords}", query
60
+
61
+ return f"추출된 키워드: {keywords}", keywords
62
+
63
+ except Exception as e:
64
+ print(f"키워드 추출 중 오류 발생: {str(e)}")
65
+ return f"키워드 추출 중 오류 발생: {str(e)}", query
66
+
67
+ # SerpHouse API를 사용하여 검색 수행 함수
68
+ def search_with_serphouse(query):
69
+ if not serphouse_api_key:
70
+ return "SERPHOUSE_API_KEY가 설정되지 않았습니다."
71
+
72
+ try:
73
+ # 키워드 추출
74
+ extraction_result, search_query = extract_keywords_with_llm(query)
75
+ print(f"원본 쿼리: {query}")
76
+ print(extraction_result)
77
+
78
+ # 문서 코드를 더 자세히 분석해 보니 기본 GET 방식 활용이 좋을 것 같습니다
79
+ url = "https://api.serphouse.com/serp/live"
80
+
81
+ # 한글 검색어인지 확인
82
+ is_korean = any('\uAC00' <= c <= '\uD7A3' for c in search_query)
83
+
84
+ # 간소화된 파라미터로 시도
85
+ params = {
86
+ "q": search_query,
87
+ "domain": "google.com",
88
+ "serp_type": "web", # 기본 웹 검색으로 변경
89
+ "device": "desktop",
90
+ "lang": "ko" if is_korean else "en"
91
+ }
92
+
93
+ headers = {
94
+ "Authorization": f"Bearer {serphouse_api_key}"
95
+ }
96
+
97
+ print(f"SerpHouse API 호출 중... 기본 GET 방식으로 시도")
98
+ print(f"검색어: {search_query}")
99
+ print(f"요청 URL: {url} - 파라미터: {params}")
100
+
101
+ # GET 요청 수행
102
+ response = requests.get(url, headers=headers, params=params)
103
+ response.raise_for_status()
104
+
105
+ print(f"SerpHouse API 응답 상태 코드: {response.status_code}")
106
+ search_results = response.json()
107
+
108
+ # 응답 구조 확인
109
+ print(f"응답 구조: {list(search_results.keys()) if isinstance(search_results, dict) else '딕셔너리 아님'}")
110
+
111
+ # 검색 결과 파싱 및 포맷팅
112
+ formatted_results = []
113
+ formatted_results.append(f"검색어: {search_query}\n\n")
114
+
115
+ # 다양한 가능한 응답 구조에 대한 처리
116
+ organic_results = None
117
+
118
+ # 가능한 응답 구조 1
119
+ if "results" in search_results and "organic" in search_results["results"]:
120
+ organic_results = search_results["results"]["organic"]
121
+
122
+ # 가능한 응답 구조 2
123
+ elif "organic" in search_results:
124
+ organic_results = search_results["organic"]
125
+
126
+ # 가능한 응답 구조 3 (중첩된 results)
127
+ elif "results" in search_results and "results" in search_results["results"]:
128
+ if "organic" in search_results["results"]["results"]:
129
+ organic_results = search_results["results"]["results"]["organic"]
130
+
131
+ # organic_results가 있으면 처리
132
+ if organic_results and len(organic_results) > 0:
133
+ # 응답 구조 출력
134
+ print(f"첫번째 organic 결과 구조: {organic_results[0].keys() if len(organic_results) > 0 else 'empty'}")
135
+
136
+ for result in organic_results[:5]: # 상위 5개 결과만 표시
137
+ title = result.get("title", "제목 없음")
138
+ snippet = result.get("snippet", "내용 없음")
139
+ link = result.get("link", "#")
140
+
141
+ formatted_results.append(
142
+ f"제목: {title}\n"
143
+ f"내용: {snippet}\n"
144
+ f"링크: {link}\n\n"
145
+ )
146
+
147
+ print(f"검색 결과 {len(organic_results)}개 찾음")
148
+ return "".join(formatted_results)
149
+
150
+ # 결과가 없거나 예상치 못한 구조인 경우
151
+ print("검색 결과 없음 또는 예상치 못한 응답 구조")
152
+ print(f"응답 구조 상세: {search_results.keys() if hasattr(search_results, 'keys') else '불명확한 구조'}")
153
+
154
+ # 응답 내용에서 오류 메시지 찾기
155
+ error_msg = "검색 결과가 없거나 응답 형식이 예상과 다릅니다"
156
+ if "error" in search_results:
157
+ error_msg = search_results["error"]
158
+ elif "message" in search_results:
159
+ error_msg = search_results["message"]
160
+
161
+ return f"검색어 '{search_query}'에 대한 결과: {error_msg}"
162
+
163
+ except Exception as e:
164
+ error_msg = f"검색 중 오류 발생: {str(e)}"
165
+ print(error_msg)
166
+ import traceback
167
+ print(traceback.format_exc())
168
+
169
+ # 디버깅 목적으로 API 요청 상세 정보 추가
170
+ return f"검색 중 오류가 발생했습니다: {str(e)}\n\n" + \
171
+ f"API 요청 상세 정보:\n" + \
172
+ f"- URL: {url}\n" + \
173
+ f"- 검색어: {search_query}\n" + \
174
+ f"- 파라미터: {params}\n"
175
+
176
+ # 스트리밍 방식으로 DeepSeek API 호출 함수
177
+ def query_deepseek_streaming(message, history, use_deep_research):
178
+ if not api_key:
179
+ yield history, "환경 변수 FW_API_KEY가 설정되지 않았습니다. 서버에서 환경 변수를 확인해주세요."
180
+ return
181
+
182
+ search_context = ""
183
+ search_info = ""
184
+ if use_deep_research:
185
+ try:
186
+ # 검색 수행 (첫 메시지 전달)
187
+ yield history + [(message, "🔍 최적의 키워드 추출 및 웹 검색 중...")], ""
188
+
189
+ # 검색 실행 - 디버깅을 위한 로그 추가
190
+ print(f"Deep Research 활성화됨: 메시지 '{message}'에 대한 검색 시작")
191
+ search_results = search_with_serphouse(message)
192
+ print(f"검색 결과 수신 완료: {search_results[:100]}...") # 결과 앞부분만 출력
193
+
194
+ if not search_results.startswith("검색 중 오류 발생") and not search_results.startswith("SERPHOUSE_API_KEY"):
195
+ search_context = f"""
196
+ 다음은 사용자 질문과 관련된 최신 검색 결과입니다. 이 정보를 참고하여 정확하고 최신 정보가 반영된 응답을 제공하세요:
197
+ {search_results}
198
+ 위 검색 결과를 기반으로 사용자의 다음 질문에 답변하세요. 검색 결과에서 명확한 답변을 찾을 수 없는 경우, 당신의 지식을 활용하여 최선의 답변을 제공하세요.
199
+ 검색 결과를 인용할 때는 출처를 명시하고, 답변이 최신 정보를 반영하도록 하세요.
200
+ """
201
+ search_info = f"🔍 Deep Research 기능 활성화: 관련 웹 검색 결과를 기반으로 응답 생성 중..."
202
+ else:
203
+ print(f"검색 실패 또는 결과 없음: {search_results}")
204
+ except Exception as e:
205
+ print(f"Deep Research 처리 중 예외 발생: {str(e)}")
206
+ search_info = f"🔍 Deep Research 기능 오류: {str(e)}"
207
+
208
+ # API 요청을 위한 대화 기록 준비
209
+ messages = []
210
+ for user, assistant in history:
211
+ messages.append({"role": "user", "content": user})
212
+ messages.append({"role": "assistant", "content": assistant})
213
+
214
+ # 검색 컨텍스트가 있으면 시스템 메시지 추가
215
+ if search_context:
216
+ # DeepSeek 모델은 시스템 메시지를 지원합니다
217
+ messages.insert(0, {"role": "system", "content": search_context})
218
+
219
+ # 새 사용자 메시지 추가
220
+ messages.append({"role": "user", "content": message})
221
+
222
+ # API 요청 준비
223
+ url = "https://api.fireworks.ai/inference/v1/chat/completions"
224
+ payload = {
225
+ "model": "accounts/fireworks/models/deepseek-v3-0324",
226
+ "max_tokens": 20480,
227
+ "top_p": 1,
228
+ "top_k": 40,
229
+ "presence_penalty": 0,
230
+ "frequency_penalty": 0,
231
+ "temperature": 0.6,
232
+ "messages": messages,
233
+ "stream": True # 스트리밍 활성화
234
+ }
235
+ headers = {
236
+ "Accept": "application/json",
237
+ "Content-Type": "application/json",
238
+ "Authorization": f"Bearer {api_key}"
239
+ }
240
+
241
+ try:
242
+ # 스트리밍 응답 요청
243
+ response = requests.request("POST", url, headers=headers, data=json.dumps(payload), stream=True)
244
+ response.raise_for_status() # HTTP 오류 발생 시 예외 발생
245
+
246
+ # 메시지를 추가하고 초기 응답으로 시작
247
+ new_history = history.copy()
248
+
249
+ # search_info가 있으면 시작 메시지에 포함
250
+ start_msg = search_info if search_info else ""
251
+ new_history.append((message, start_msg))
252
+
253
+ # 응답 전체 텍스트
254
+ full_response = start_msg
255
+
256
+ # 스트리밍 응답 처리
257
+ for line in response.iter_lines():
258
+ if line:
259
+ line_text = line.decode('utf-8')
260
+
261
+ # 'data: ' 접두사 제거
262
+ if line_text.startswith("data: "):
263
+ line_text = line_text[6:]
264
+
265
+ # 스트림 종료 메시지 확인
266
+ if line_text == "[DONE]":
267
+ break
268
+
269
+ try:
270
+ # JSON 파싱
271
+ chunk = json.loads(line_text)
272
+ chunk_content = chunk.get("choices", [{}])[0].get("delta", {}).get("content", "")
273
+
274
+ if chunk_content:
275
+ full_response += chunk_content
276
+ # 채팅 기록 업데이트
277
+ new_history[-1] = (message, full_response)
278
+ yield new_history, ""
279
+ except json.JSONDecodeError:
280
+ continue
281
+
282
+ # 최종 응답 반환
283
+ yield new_history, ""
284
+
285
+ except requests.exceptions.RequestException as e:
286
+ error_msg = f"API 오류: {str(e)}"
287
+ if hasattr(e, 'response') and e.response and e.response.status_code == 401:
288
+ error_msg = "인증 실패. 환경 변수 FW_API_KEY를 확인해주세요."
289
+ yield history, error_msg
290
+
291
+ # Gradio 인터페이스 생성
292
+ with gr.Blocks(theme="soft", fill_height=True) as demo:
293
+ # 헤더 섹션
294
+ gr.Markdown(
295
+ """
296
+ # 🤖 DeepSeek V3 스트리밍 인터페이스
297
+ ### Fireworks AI가 제공하는 고급 AI 모델 - 실시간 응답 지원
298
+ """
299
+ )
300
+
301
+ # 메인 레이아웃
302
+ with gr.Row():
303
+ # 메인 콘텐츠 영역
304
+ with gr.Column():
305
+ # 채팅 인터페이스
306
+ chatbot = gr.Chatbot(
307
+ height=500,
308
+ show_label=False,
309
+ container=True
310
+ )
311
+
312
+ # Deep Research 토글 및 상태 표시 추가
313
+ with gr.Row():
314
+ with gr.Column(scale=3):
315
+ use_deep_research = gr.Checkbox(
316
+ label="Deep Research 활성화",
317
+ info="최적의 키워드 추출 및 웹 검색을 통한 최신 정보 활용",
318
+ value=False
319
+ )
320
+ with gr.Column(scale=1):
321
+ api_status = gr.Markdown("API 상태: 준비됨")
322
+
323
+ # API 키 상태 확인 및 표시
324
+ if not serphouse_api_key:
325
+ api_status.value = "⚠️ SERPHOUSE_API_KEY가 설정되지 않았습니다"
326
+ if not api_key:
327
+ api_status.value = "⚠️ FW_API_KEY가 설정되지 않았습니다"
328
+ if api_key and serphouse_api_key:
329
+ api_status.value = "✅ API 키 설정 완료"
330
+
331
+ # 입��� 영역
332
+ with gr.Row():
333
+ msg = gr.Textbox(
334
+ label="메시지",
335
+ placeholder="여기에 프롬프트를 입력하세요...",
336
+ show_label=False,
337
+ scale=9
338
+ )
339
+ submit = gr.Button("전송", variant="primary", scale=1)
340
+
341
+ # 대화 초기화 버튼
342
+ with gr.Row():
343
+ clear = gr.ClearButton([msg, chatbot], value="🧹 대화 초기화")
344
+
345
+ # 예제 쿼리
346
+ gr.Examples(
347
+ examples=[
348
+ "딥러닝에서 트랜스포머와 RNN의 차이점을 설명해주세요.",
349
+ "특정 범위 내의 소수를 찾는 파이썬 함수를 작성해주세요.",
350
+ "강화학습의 주요 개념을 요약해주세요."
351
+ ],
352
+ inputs=msg
353
+ )
354
+
355
+ # 오류 메시지 표시
356
+ error_box = gr.Markdown("")
357
+
358
+ # 버튼과 기능 연결
359
+ submit.click(
360
+ query_deepseek_streaming,
361
+ inputs=[msg, chatbot, use_deep_research],
362
+ outputs=[chatbot, error_box]
363
+ ).then(
364
+ lambda: "",
365
+ None,
366
+ [msg]
367
+ )
368
+
369
+ # Enter 키 제출 허용
370
+ msg.submit(
371
+ query_deepseek_streaming,
372
+ inputs=[msg, chatbot, use_deep_research],
373
+ outputs=[chatbot, error_box]
374
+ ).then(
375
+ lambda: "",
376
+ None,
377
+ [msg]
378
+ )
379
+
380
+ return demo
381
+
382
+ # 인터페이스 실행
383
+ if __name__ == "__main__":
384
+ demo = create_deepseek_interface()
385
+ demo.launch(debug=True)