Claude commited on
Commit
e5efb9a
ยท
unverified ยท
1 Parent(s): 4c3c333

Fix: Simplify dependencies and add app_gradio.py for HuggingFace Spaces

Browse files

- Copy app.py to app_gradio.py as main Space file
- Remove unnecessary dependencies (FastAPI, pydantic, numpy, requests)
- Fix python-multipart version conflict with Gradio
- Update README.md to use app_gradio.py as app_file
- Keep only essential packages: gradio, anthropic, chromadb, sentence-transformers

Files changed (3) hide show
  1. README.md +3 -3
  2. app_gradio.py +346 -0
  3. requirements.txt +0 -10
README.md CHANGED
@@ -1,11 +1,11 @@
1
  ---
2
- title: Financial RAG with Metacognitive Agent
3
- emoji: ๐Ÿ’ผ
4
  colorFrom: blue
5
  colorTo: green
6
  sdk: gradio
7
  sdk_version: 5.0.0
8
- app_file: app.py
9
  pinned: false
10
  license: mit
11
  ---
 
1
  ---
2
+ title: Financial RAG Chatbot with Metacognition
3
+ emoji: ๐Ÿฆ
4
  colorFrom: blue
5
  colorTo: green
6
  sdk: gradio
7
  sdk_version: 5.0.0
8
+ app_file: app_gradio.py
9
  pinned: false
10
  license: mit
11
  ---
app_gradio.py ADDED
@@ -0,0 +1,346 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ HuggingFace Spaces์šฉ Gradio ์•ฑ
3
+ Financial RAG with Metacognitive Agent
4
+ """
5
+
6
+ import gradio as gr
7
+ import os
8
+ import sys
9
+ from loguru import logger
10
+ import asyncio
11
+ from typing import Dict, Tuple
12
+
13
+ # ๋กœ๊น… ์„ค์ •
14
+ logger.remove()
15
+ logger.add(
16
+ sys.stdout,
17
+ format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <level>{message}</level>",
18
+ level="INFO"
19
+ )
20
+
21
+ # ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ๋ฅผ Python ๊ฒฝ๋กœ์— ์ถ”๊ฐ€
22
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
23
+
24
+ from app.metacognitive_agent import MetaCognitiveAgent
25
+ from app.rag_pipeline import RAGPipeline
26
+ from services.vector_store import VectorStore
27
+ from services.embedder import Embedder
28
+ from utils.config import settings
29
+
30
+ # ๊ธ€๋กœ๋ฒŒ ๋ณ€์ˆ˜
31
+ rag_pipeline = None
32
+
33
+
34
+ def setup_vector_db():
35
+ """๋ฒกํ„ฐ DB ์ž๋™ ์„ค์ • (์—†์œผ๋ฉด ๋‹ค์šด๋กœ๋“œ ๋˜๋Š” ์ƒ์„ฑ)"""
36
+ db_path = settings.chroma_persist_directory
37
+
38
+ # ๋ฒกํ„ฐ DB๊ฐ€ ์ด๋ฏธ ์กด์žฌํ•˜๊ณ  ๋น„์–ด์žˆ์ง€ ์•Š์€์ง€ ํ™•์ธ
39
+ if os.path.exists(db_path):
40
+ if os.listdir(db_path):
41
+ logger.info("โœ… Vector DB already exists. Skipping setup.")
42
+ return True
43
+
44
+ logger.info("๐Ÿ“ฅ Vector DB not found. Setting up...")
45
+ os.makedirs(db_path, exist_ok=True)
46
+
47
+ # ์˜ต์…˜ 1: GitHub Release์—์„œ ๋‹ค์šด๋กœ๋“œ ์‹œ๋„
48
+ try:
49
+ import urllib.request
50
+ import tarfile
51
+
52
+ release_url = "https://github.com/csjjin2025/Hallucination_and_Deception_for_financial_RAG/releases/download/v1.0/chroma_db.tar.gz"
53
+ tar_path = "./data/chroma_db.tar.gz"
54
+
55
+ logger.info(f"Attempting to download from {release_url}...")
56
+ urllib.request.urlretrieve(release_url, tar_path)
57
+
58
+ # ํŒŒ์ผ ํฌ๊ธฐ ํ™•์ธ
59
+ file_size = os.path.getsize(tar_path)
60
+ if file_size > 1000:
61
+ logger.info(f"๐Ÿ“ฆ Extracting vector DB ({file_size} bytes)...")
62
+ with tarfile.open(tar_path, 'r:gz') as tar:
63
+ tar.extractall(path='./data/')
64
+ os.remove(tar_path)
65
+ logger.info("โœ… Vector DB downloaded and extracted!")
66
+ return True
67
+ else:
68
+ logger.warning(f"Downloaded file too small ({file_size} bytes)")
69
+ os.remove(tar_path)
70
+ except Exception as e:
71
+ logger.warning(f"Failed to download from Release: {e}")
72
+
73
+ # ์˜ต์…˜ 2: ํ…Œ์ŠคํŠธ DB ์ƒ์„ฑ
74
+ try:
75
+ logger.info("โš ๏ธ Creating test DB with sample data...")
76
+ import subprocess
77
+ result = subprocess.run(
78
+ ["python", "scripts/quick_setup_test_db.py"],
79
+ capture_output=True,
80
+ text=True,
81
+ timeout=300
82
+ )
83
+ if result.returncode == 0:
84
+ logger.info("โœ… Test DB created successfully!")
85
+ return True
86
+ else:
87
+ logger.error(f"Test DB creation failed: {result.stderr}")
88
+ return False
89
+ except Exception as e:
90
+ logger.error(f"Failed to create test DB: {e}")
91
+ return False
92
+
93
+
94
+ def initialize_rag_system():
95
+ """RAG ์‹œ์Šคํ…œ ์ดˆ๊ธฐํ™”"""
96
+ global rag_pipeline
97
+
98
+ try:
99
+ logger.info("=" * 80)
100
+ logger.info("๐Ÿš€ Financial RAG ์‹œ์Šคํ…œ ์ดˆ๊ธฐํ™” ์ค‘...")
101
+ logger.info("=" * 80)
102
+
103
+ # 0. Vector DB ์ž๋™ ์„ค์ •
104
+ logger.info("0๏ธโƒฃ Vector DB ์„ค์ • ํ™•์ธ ์ค‘...")
105
+ if not setup_vector_db():
106
+ logger.error("โŒ Vector DB ์„ค์ • ์‹คํŒจ")
107
+ return False
108
+
109
+ # 1. Vector Store ์ดˆ๊ธฐํ™”
110
+ logger.info("1๏ธโƒฃ Vector Store ๋กœ๋”ฉ ์ค‘...")
111
+ vector_store = VectorStore(
112
+ persist_directory=settings.chroma_persist_directory,
113
+ collection_name=settings.collection_name
114
+ )
115
+ doc_count = vector_store.collection.count()
116
+ logger.info(f"โœ… Vector Store ๋กœ๋”ฉ ์™„๋ฃŒ ({doc_count}๊ฐœ ๋ฌธ์„œ)")
117
+
118
+ # 2. Embedder ์ดˆ๊ธฐํ™”
119
+ logger.info("2๏ธโƒฃ Embedder ์ดˆ๊ธฐํ™” ์ค‘...")
120
+ embedder = Embedder(
121
+ model_type=settings.embedding_model,
122
+ model_name=settings.embedding_model_name,
123
+ openai_api_key=settings.openai_api_key,
124
+ cohere_api_key=settings.cohere_api_key
125
+ )
126
+ logger.info(f"โœ… Embedder ์ดˆ๊ธฐํ™” ์™„๋ฃŒ ({embedder.get_embedding_dimension()}์ฐจ์›)")
127
+
128
+ # 3. Metacognitive Agent ์ดˆ๊ธฐํ™”
129
+ logger.info("3๏ธโƒฃ Metacognitive Agent ์ดˆ๊ธฐํ™” ์ค‘...")
130
+ agent = MetaCognitiveAgent(api_key=settings.anthropic_api_key)
131
+ logger.info(f"โœ… Agent ์ดˆ๊ธฐํ™” ์™„๋ฃŒ ({agent.model})")
132
+
133
+ # 4. RAG Pipeline ์ƒ์„ฑ
134
+ logger.info("4๏ธโƒฃ RAG Pipeline ์ƒ์„ฑ ์ค‘...")
135
+ rag_pipeline = RAGPipeline(
136
+ vector_store=vector_store,
137
+ embedder=embedder,
138
+ metacognitive_agent=agent
139
+ )
140
+ logger.info("โœ… RAG Pipeline ์ƒ์„ฑ ์™„๋ฃŒ")
141
+
142
+ logger.info("=" * 80)
143
+ logger.info("โœจ ์‹œ์Šคํ…œ ์ค€๋น„ ์™„๋ฃŒ!")
144
+ logger.info(f"๐Ÿ“š Vector DB: {doc_count}๊ฐœ ๋ฌธ์„œ")
145
+ logger.info(f"๐Ÿค– Model: {agent.model}")
146
+ logger.info("=" * 80)
147
+
148
+ return True
149
+
150
+ except Exception as e:
151
+ logger.error(f"โŒ ์ดˆ๊ธฐํ™” ์‹คํŒจ: {str(e)}")
152
+ import traceback
153
+ logger.error(traceback.format_exc())
154
+ return False
155
+
156
+
157
+ def format_sources(sources: list) -> str:
158
+ """์ถœ์ฒ˜ ๋ฌธ์„œ ํฌ๋งทํŒ…"""
159
+ if not sources:
160
+ return "์ถœ์ฒ˜ ๋ฌธ์„œ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค."
161
+
162
+ formatted = "### ๐Ÿ“š ์ฐธ๊ณ  ๋ฌธ์„œ\n\n"
163
+ for idx, source in enumerate(sources[:3], 1): # ์ƒ์œ„ 3๊ฐœ๋งŒ ํ‘œ์‹œ
164
+ similarity = source.get('similarity', 0) * 100
165
+ filename = source.get('source_filename', 'unknown')
166
+ text = source.get('text', '')[:300] # ์•ž 300์ž๋งŒ
167
+
168
+ formatted += f"**{idx}. {filename}** (์œ ์‚ฌ๋„: {similarity:.1f}%)\n"
169
+ formatted += f"> {text}...\n\n"
170
+
171
+ return formatted
172
+
173
+
174
+ def format_metacognition(metacognition: Dict) -> str:
175
+ """๋ฉ”ํƒ€์ธ์ง€ ๊ณผ์ • ํฌ๋งทํŒ…"""
176
+ if not metacognition:
177
+ return ""
178
+
179
+ history = metacognition.get('thinking_history', [])
180
+ iterations = metacognition.get('iterations', 0)
181
+
182
+ formatted = f"\n\n### ๐Ÿง  ๋ฉ”ํƒ€์ธ์ง€ ๊ณผ์ • ({iterations}ํšŒ ๋ฐ˜๋ณต)\n\n"
183
+
184
+ for idx, step in enumerate(history, 1):
185
+ stage = step.get('stage', 'unknown')
186
+ content = step.get('content', '')
187
+
188
+ stage_emoji = {
189
+ 'planning': '๐Ÿ“‹',
190
+ 'monitoring': '๐Ÿ‘๏ธ',
191
+ 'evaluation': 'โœ…',
192
+ 'revision': '๐Ÿ”„'
193
+ }.get(stage, '๐Ÿ’ญ')
194
+
195
+ formatted += f"**{stage_emoji} {stage.capitalize()}**\n"
196
+ formatted += f"{content}\n\n"
197
+
198
+ return formatted
199
+
200
+
201
+ async def process_query_async(question: str, top_k: int, enable_metacognition: bool) -> Tuple[str, str]:
202
+ """๋น„๋™๊ธฐ ์ฟผ๋ฆฌ ์ฒ˜๋ฆฌ"""
203
+ if not rag_pipeline:
204
+ return "โŒ ์‹œ์Šคํ…œ์ด ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.", ""
205
+
206
+ if not question.strip():
207
+ return "โŒ ์งˆ๋ฌธ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.", ""
208
+
209
+ try:
210
+ logger.info(f"๐Ÿ“ ์งˆ๋ฌธ: {question}")
211
+
212
+ # RAG ํŒŒ์ดํ”„๋ผ์ธ์œผ๋กœ ์ฟผ๋ฆฌ ์ฒ˜๋ฆฌ
213
+ result = await rag_pipeline.query(
214
+ question=question,
215
+ top_k=top_k,
216
+ enable_metacognition=enable_metacognition
217
+ )
218
+
219
+ # ๋‹ต๋ณ€ ํฌ๋งทํŒ…
220
+ answer = result.get('answer', '๋‹ต๋ณ€์„ ์ƒ์„ฑํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.')
221
+ sources = result.get('sources', [])
222
+ metacognition = result.get('metacognition', None)
223
+
224
+ # ์ถœ๋ ฅ ๊ตฌ์„ฑ
225
+ main_output = f"## ๐Ÿ’ฌ ๋‹ต๋ณ€\n\n{answer}\n\n"
226
+ main_output += format_sources(sources)
227
+
228
+ # ๋ฉ”ํƒ€์ธ์ง€ ๊ณผ์ • (๋ณ„๋„ ํƒญ)
229
+ meta_output = format_metacognition(metacognition) if metacognition else "๋ฉ”ํƒ€์ธ์ง€๊ฐ€ ๋น„ํ™œ์„ฑํ™”๋˜์—ˆ์Šต๋‹ˆ๋‹ค."
230
+
231
+ logger.info("โœ… ๋‹ต๋ณ€ ์ƒ์„ฑ ์™„๋ฃŒ")
232
+ return main_output, meta_output
233
+
234
+ except Exception as e:
235
+ error_msg = f"โŒ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
236
+ logger.error(error_msg)
237
+ import traceback
238
+ logger.error(traceback.format_exc())
239
+ return error_msg, ""
240
+
241
+
242
+ def process_query(question: str, top_k: int, enable_metacognition: bool) -> Tuple[str, str]:
243
+ """Gradio์šฉ ๋™๊ธฐ ๋ž˜ํผ"""
244
+ loop = asyncio.new_event_loop()
245
+ asyncio.set_event_loop(loop)
246
+ try:
247
+ return loop.run_until_complete(process_query_async(question, top_k, enable_metacognition))
248
+ finally:
249
+ loop.close()
250
+
251
+
252
+ # Gradio ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌ์„ฑ
253
+ def create_interface():
254
+ """Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ"""
255
+
256
+ with gr.Blocks(theme=gr.themes.Soft(), title="Financial RAG with Metacognitive Agent") as demo:
257
+ gr.Markdown("""
258
+ # ๐Ÿ’ผ Financial RAG System
259
+ ### ๋ฉ”ํƒ€์ธ์ง€ ์—์ด์ „ํŠธ ๊ธฐ๋ฐ˜ ๊ธˆ์œต/๊ฒฝ์ œ ์งˆ์˜์‘๋‹ต ์‹œ์Šคํ…œ
260
+
261
+ ์ด ์‹œ์Šคํ…œ์€ ๊ธˆ์œต/๊ฒฝ์ œ ๋…ผ๋ฌธ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์งˆ๋ฌธ์— ๋‹ต๋ณ€ํ•ฉ๋‹ˆ๋‹ค.
262
+ ๋ฉ”ํƒ€์ธ์ง€ ๊ธฐ๋Šฅ์„ ํ™œ์„ฑํ™”ํ•˜๋ฉด ๋” ๊นŠ์ด ์žˆ๋Š” ์‚ฌ๊ณ  ๊ณผ์ •์„ ๊ฑฐ์ณ ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
263
+ """)
264
+
265
+ with gr.Row():
266
+ with gr.Column(scale=2):
267
+ question_input = gr.Textbox(
268
+ label="๐Ÿ’ฌ ์งˆ๋ฌธ์„ ์ž…๋ ฅํ•˜์„ธ์š”",
269
+ placeholder="์˜ˆ: ํฌํŠธํด๋ฆฌ์˜ค ๋‹ค๊ฐํ™”๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”?",
270
+ lines=3
271
+ )
272
+
273
+ with gr.Row():
274
+ top_k_slider = gr.Slider(
275
+ minimum=1,
276
+ maximum=10,
277
+ value=5,
278
+ step=1,
279
+ label="๐Ÿ” ๊ฒ€์ƒ‰ํ•  ๋ฌธ์„œ ๊ฐœ์ˆ˜"
280
+ )
281
+
282
+ metacognition_check = gr.Checkbox(
283
+ label="๐Ÿง  ๋ฉ”ํƒ€์ธ์ง€ ํ™œ์„ฑํ™”",
284
+ value=True,
285
+ info="๋” ๊นŠ์ด ์žˆ๋Š” ์‚ฌ๊ณ  ๊ณผ์ • (์ฒ˜๋ฆฌ ์‹œ๊ฐ„ ์ฆ๊ฐ€)"
286
+ )
287
+
288
+ submit_btn = gr.Button("๐Ÿš€ ์งˆ๋ฌธํ•˜๊ธฐ", variant="primary", size="lg")
289
+
290
+ gr.Markdown("""
291
+ ### ๐Ÿ’ก ์‚ฌ์šฉ ํŒ
292
+ - **๋ฉ”ํƒ€์ธ์ง€ ํ™œ์„ฑํ™”**: Planning โ†’ Monitoring ๏ฟฝ๏ฟฝ Evaluation โ†’ Revision ๊ณผ์ •์„ ๊ฑฐ์ณ ์‹ ์ค‘ํ•œ ๋‹ต๋ณ€ ์ƒ์„ฑ
293
+ - **๋ฉ”ํƒ€์ธ์ง€ ๋น„ํ™œ์„ฑํ™”**: ๋น ๋ฅธ ๋‹ต๋ณ€ ์ƒ์„ฑ
294
+ - **๊ฒ€์ƒ‰ ๋ฌธ์„œ ๊ฐœ์ˆ˜**: ๋งŽ์„์ˆ˜๋ก ๋” ๋งŽ์€ ์ •๋ณด๋ฅผ ์ฐธ๊ณ ํ•˜์ง€๋งŒ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„ ์ฆ๊ฐ€
295
+ """)
296
+
297
+ with gr.Column(scale=3):
298
+ with gr.Tabs():
299
+ with gr.Tab("๐Ÿ“ ๋‹ต๋ณ€ ๋ฐ ์ถœ์ฒ˜"):
300
+ answer_output = gr.Markdown(label="๋‹ต๋ณ€")
301
+
302
+ with gr.Tab("๐Ÿง  ๋ฉ”ํƒ€์ธ์ง€ ๊ณผ์ •"):
303
+ metacognition_output = gr.Markdown(label="์‚ฌ๊ณ  ๊ณผ์ •")
304
+
305
+ # ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ
306
+ submit_btn.click(
307
+ fn=process_query,
308
+ inputs=[question_input, top_k_slider, metacognition_check],
309
+ outputs=[answer_output, metacognition_output]
310
+ )
311
+
312
+ # Enter ํ‚ค๋กœ๋„ ์ œ์ถœ
313
+ question_input.submit(
314
+ fn=process_query,
315
+ inputs=[question_input, top_k_slider, metacognition_check],
316
+ outputs=[answer_output, metacognition_output]
317
+ )
318
+
319
+ gr.Markdown("""
320
+ ---
321
+ ### ๐Ÿ“Œ ์‹œ์Šคํ…œ ์ •๋ณด
322
+ - **๋ชจ๋ธ**: Claude 3.5 Sonnet
323
+ - **์ž„๋ฒ ๋”ฉ**: sentence-transformers/all-MiniLM-L6-v2
324
+ - **๋ฒกํ„ฐ DB**: ChromaDB
325
+ """)
326
+
327
+ return demo
328
+
329
+
330
+ # ๋ฉ”์ธ ์‹คํ–‰
331
+ if __name__ == "__main__":
332
+ # ์‹œ์Šคํ…œ ์ดˆ๊ธฐํ™”
333
+ logger.info("์‹œ์Šคํ…œ ์ดˆ๊ธฐํ™” ์‹œ์ž‘...")
334
+ success = initialize_rag_system()
335
+
336
+ if not success:
337
+ logger.error("์‹œ์Šคํ…œ ์ดˆ๊ธฐํ™” ์‹คํŒจ. ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค.")
338
+ sys.exit(1)
339
+
340
+ # Gradio ์•ฑ ์‹คํ–‰
341
+ demo = create_interface()
342
+ demo.launch(
343
+ server_name="0.0.0.0",
344
+ server_port=7860,
345
+ share=False
346
+ )
requirements.txt CHANGED
@@ -1,12 +1,6 @@
1
  # Gradio (HuggingFace Spaces)
2
  gradio>=5.0.0
3
 
4
- # FastAPI and Web Server (optional for Render.com)
5
- fastapi>=0.109.0
6
- uvicorn[standard]>=0.27.0
7
- pydantic>=2.5.0
8
- pydantic-settings>=2.1.0
9
-
10
  # Anthropic Claude
11
  anthropic>=0.18.0
12
 
@@ -17,7 +11,3 @@ sentence-transformers>=2.3.0
17
  # Utilities
18
  python-dotenv>=1.0.0
19
  loguru>=0.7.0
20
- numpy>=1.26.0
21
-
22
- # Required by ChromaDB
23
- requests>=2.28.0
 
1
  # Gradio (HuggingFace Spaces)
2
  gradio>=5.0.0
3
 
 
 
 
 
 
 
4
  # Anthropic Claude
5
  anthropic>=0.18.0
6
 
 
11
  # Utilities
12
  python-dotenv>=1.0.0
13
  loguru>=0.7.0