Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -2,27 +2,27 @@ import os
|
|
2 |
import asyncio
|
3 |
from typing import List, Dict
|
4 |
|
5 |
-
# Protobuf C-extension ๋์ pure-Python ๊ตฌํ
|
6 |
-
|
7 |
|
8 |
-
# .env ๋ฐ Space Secrets ๋ก๋
|
9 |
from dotenv import load_dotenv
|
10 |
load_dotenv()
|
11 |
|
12 |
-
# Gradio client ๋ฒ๊ทธ
|
13 |
import gradio_client.utils as client_utils
|
14 |
-
|
15 |
-
def
|
16 |
try:
|
17 |
-
return
|
18 |
except Exception:
|
19 |
return "Any"
|
20 |
-
client_utils.json_schema_to_python_type =
|
21 |
|
22 |
# Google API Key ๊ฒ์ฆ
|
23 |
api_key = os.getenv("GOOGLE_API_KEY")
|
24 |
if not api_key:
|
25 |
-
raise EnvironmentError("GOOGLE_API_KEY๋ฅผ Secrets์ ์ถ๊ฐํด์ฃผ์ธ์.")
|
26 |
os.environ["GOOGLE_API_KEY"] = api_key
|
27 |
|
28 |
# ChromaDB ๊ฒฝ๋ก ์ค์
|
@@ -42,46 +42,31 @@ from google.genai import types
|
|
42 |
class SimpleRAGSystem:
|
43 |
def __init__(self, db_path: str = None, collection: str = "KH_media_docs"):
|
44 |
db_path = db_path or os.getenv("CHROMA_DB_DIR")
|
45 |
-
print(f"๐ RAG ์ด๊ธฐํ: DB ๊ฒฝ๋ก={db_path}, ์ปฌ๋ ์
={collection}")
|
46 |
-
# ์๋ฒ ๋ฉ ๋ชจ๋ธ
|
47 |
-
print("๐ KR-SBERT ๋ก๋ ์ค...")
|
48 |
self.embedding_model = SentenceTransformer("snunlp/KR-SBERT-V40K-klueNLI-augSTS")
|
49 |
-
print("โ
์๋ฒ ๋ฉ ์ค๋น ์๋ฃ")
|
50 |
-
# ChromaDB ์ฐ๊ฒฐ
|
51 |
-
print(f"๐๏ธ ChromaDB ์ฐ๊ฒฐ: {db_path}")
|
52 |
self.client = chromadb.PersistentClient(path=db_path)
|
53 |
self.collection = self.client.get_collection(name=collection)
|
54 |
count = self.collection.count()
|
55 |
-
print(f"โ
DB ์ค๋น๋จ: {collection} ({count}๋ฌธ์)")
|
56 |
if count == 0:
|
57 |
-
raise RuntimeError("ChromaDB๊ฐ ๋น์ด์์ต๋๋ค.
|
58 |
|
59 |
def search_similar_docs(self, query: str, top_k: int = 5) -> List[Dict]:
|
60 |
emb = self.embedding_model.encode(query).tolist()
|
61 |
res = self.collection.query(
|
62 |
-
query_embeddings=[emb],
|
63 |
-
|
64 |
-
include=["documents","metadatas","distances"]
|
65 |
)
|
66 |
docs = []
|
67 |
-
for doc, meta
|
68 |
-
|
69 |
-
docs.append({"content":doc, "metadata":meta, "similarity":score})
|
70 |
return docs
|
71 |
|
72 |
-
def get_status(self) -> str:
|
73 |
-
return f"๐ '{self.collection.name}'์ {self.collection.count()}๋ฌธ์ ์ค๋น"
|
74 |
-
|
75 |
-
# RAG ์์คํ
์ธ์คํด์ค
|
76 |
rag_system = SimpleRAGSystem()
|
77 |
|
78 |
# === Google ADK ์ค์ ===
|
79 |
session_svc = InMemorySessionService()
|
80 |
-
agent = Agent(
|
81 |
-
|
82 |
-
|
83 |
-
instruction="๋น์ ์ ๊ฒฝํฌ๋ ๋ฏธ๋์ดํ๊ณผ ์๋ด AI์
๋๋ค. ์ฐธ๊ณ ๋ฌธ์๋ฅผ ๋ฐํ์ผ๋ก ๋ต๋ณํ์ธ์."
|
84 |
-
)
|
85 |
runner = Runner(agent=agent, app_name="khu_media_chatbot", session_service=session_svc)
|
86 |
session_id = None
|
87 |
|
@@ -90,52 +75,42 @@ async def get_response(prompt: str) -> str:
|
|
90 |
if session_id is None:
|
91 |
sess = await session_svc.create_session(app_name="khu_media_chatbot", user_id="user")
|
92 |
session_id = sess.id
|
93 |
-
response = ""
|
94 |
content = types.Content(role="user", parts=[types.Part(text=prompt)])
|
|
|
95 |
for ev in runner.run(user_id="user", session_id=session_id, new_message=content):
|
96 |
if ev.is_final_response():
|
97 |
response = ev.content.parts[0].text
|
98 |
return response
|
99 |
|
100 |
-
# === Gradio
|
101 |
with gr.Blocks(title="๊ฒฝํฌ๋ ๋ฏธ๋์ดํ๊ณผ AI ์๋ด์ฌ", theme="soft") as app:
|
102 |
gr.Markdown("# ๐ฌ ๊ฒฝํฌ๋ ๋ฏธ๋์ดํ๊ณผ AI ์๋ด์ฌ")
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
send = gr.Button("์ ์ก")
|
108 |
-
gr.Markdown("### ๐ก FAQ")
|
109 |
-
for q in ["์ ๊ณต ์ ๋ณด","์ฃผ์ ๊ณผ๋ชฉ","์ทจ์
์ ๋ง","ํ์ ํ๋"]:
|
110 |
-
btn = gr.Button(q, size="sm")
|
111 |
-
btn.click(lambda x=q: x, outputs=[msg])
|
112 |
-
with gr.Column(scale=1):
|
113 |
-
status = gr.Textbox(label="์์คํ
์ํ", value=rag_system.get_status(), interactive=False)
|
114 |
-
gr.Button("๐ ์๋ก๊ณ ์นจ").click(lambda: rag_system.get_status(), outputs=[status])
|
115 |
-
gr.Button("๐๏ธ ์ด๊ธฐํ").click(lambda: [], outputs=[chatbot])
|
116 |
def chat_fn(user_input, history):
|
|
|
|
|
117 |
docs = rag_system.search_similar_docs(user_input)
|
118 |
-
|
119 |
-
|
|
|
|
|
|
|
|
|
120 |
resp = asyncio.run(get_response(prompt))
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
send.click(chat_fn, inputs=[msg, chatbot], outputs=[chatbot, msg])
|
125 |
msg.submit(chat_fn, inputs=[msg, chatbot], outputs=[chatbot, msg])
|
|
|
126 |
gr.Markdown(f"""
|
127 |
-
---
|
128 |
-
|
129 |
-
|
130 |
-
**LLM**: Gemini Flash-Lite
|
131 |
-
**์ํ**: {rag_system.get_status()}
|
132 |
-
"""
|
133 |
)
|
134 |
|
135 |
if __name__ == "__main__":
|
136 |
-
app.launch(
|
137 |
-
server_name="0.0.0.0",
|
138 |
-
server_port=int(os.environ.get("PORT", 7860)),
|
139 |
-
share=False,
|
140 |
-
show_api=False # API ํญ ์จ๊ธฐ๊ธฐ
|
141 |
-
)
|
|
|
2 |
import asyncio
|
3 |
from typing import List, Dict
|
4 |
|
5 |
+
# Protobuf C-extension ๋์ pure-Python ๊ตฌํ ์ฌ์ฉ
|
6 |
+
ios.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] = "python"
|
7 |
|
8 |
+
# .env ํ์ผ ๋ฐ Space Secrets ๋ก๋
|
9 |
from dotenv import load_dotenv
|
10 |
load_dotenv()
|
11 |
|
12 |
+
# Gradio client ๋ฒ๊ทธ ์ฐํ (OpenAPI ํ์ฑ)
|
13 |
import gradio_client.utils as client_utils
|
14 |
+
orig = client_utils.json_schema_to_python_type
|
15 |
+
def safe_json_type(schema, defs=None):
|
16 |
try:
|
17 |
+
return orig(schema, defs)
|
18 |
except Exception:
|
19 |
return "Any"
|
20 |
+
client_utils.json_schema_to_python_type = safe_json_type
|
21 |
|
22 |
# Google API Key ๊ฒ์ฆ
|
23 |
api_key = os.getenv("GOOGLE_API_KEY")
|
24 |
if not api_key:
|
25 |
+
raise EnvironmentError("GOOGLE_API_KEY๋ฅผ SettingsโSecrets์ ์ถ๊ฐํด์ฃผ์ธ์.")
|
26 |
os.environ["GOOGLE_API_KEY"] = api_key
|
27 |
|
28 |
# ChromaDB ๊ฒฝ๋ก ์ค์
|
|
|
42 |
class SimpleRAGSystem:
|
43 |
def __init__(self, db_path: str = None, collection: str = "KH_media_docs"):
|
44 |
db_path = db_path or os.getenv("CHROMA_DB_DIR")
|
|
|
|
|
|
|
45 |
self.embedding_model = SentenceTransformer("snunlp/KR-SBERT-V40K-klueNLI-augSTS")
|
|
|
|
|
|
|
46 |
self.client = chromadb.PersistentClient(path=db_path)
|
47 |
self.collection = self.client.get_collection(name=collection)
|
48 |
count = self.collection.count()
|
|
|
49 |
if count == 0:
|
50 |
+
raise RuntimeError("ChromaDB๊ฐ ๋น์ด์์ต๋๋ค.")
|
51 |
|
52 |
def search_similar_docs(self, query: str, top_k: int = 5) -> List[Dict]:
|
53 |
emb = self.embedding_model.encode(query).tolist()
|
54 |
res = self.collection.query(
|
55 |
+
query_embeddings=[emb], n_results=top_k,
|
56 |
+
include=["documents", "metadatas"]
|
|
|
57 |
)
|
58 |
docs = []
|
59 |
+
for doc, meta in zip(res["documents"][0], res["metadatas"][0]):
|
60 |
+
docs.append({"role": "system", "content": doc})
|
|
|
61 |
return docs
|
62 |
|
|
|
|
|
|
|
|
|
63 |
rag_system = SimpleRAGSystem()
|
64 |
|
65 |
# === Google ADK ์ค์ ===
|
66 |
session_svc = InMemorySessionService()
|
67 |
+
agent = Agent(model="gemini-2.0-flash-lite",
|
68 |
+
name="khu_media_advisor",
|
69 |
+
instruction="๊ฒฝํฌ๋ ๋ฏธ๋์ดํ๊ณผ ์๋ด์ฌ๋ก ๋ต๋ณํ์ธ์.")
|
|
|
|
|
70 |
runner = Runner(agent=agent, app_name="khu_media_chatbot", session_service=session_svc)
|
71 |
session_id = None
|
72 |
|
|
|
75 |
if session_id is None:
|
76 |
sess = await session_svc.create_session(app_name="khu_media_chatbot", user_id="user")
|
77 |
session_id = sess.id
|
|
|
78 |
content = types.Content(role="user", parts=[types.Part(text=prompt)])
|
79 |
+
response = ""
|
80 |
for ev in runner.run(user_id="user", session_id=session_id, new_message=content):
|
81 |
if ev.is_final_response():
|
82 |
response = ev.content.parts[0].text
|
83 |
return response
|
84 |
|
85 |
+
# === Gradio UI ===
|
86 |
with gr.Blocks(title="๊ฒฝํฌ๋ ๋ฏธ๋์ดํ๊ณผ AI ์๋ด์ฌ", theme="soft") as app:
|
87 |
gr.Markdown("# ๐ฌ ๊ฒฝํฌ๋ ๋ฏธ๋์ดํ๊ณผ AI ์๋ด์ฌ")
|
88 |
+
chatbot = gr.Chatbot(type="messages", height=400)
|
89 |
+
msg = gr.Textbox(show_label=False, placeholder="์ง๋ฌธ์ ์
๋ ฅํ์ธ์...")
|
90 |
+
send = gr.Button("์ ์ก")
|
91 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
def chat_fn(user_input, history):
|
93 |
+
history = history or []
|
94 |
+
# RAG ์ปจํ
์คํธ
|
95 |
docs = rag_system.search_similar_docs(user_input)
|
96 |
+
# Combine existing history (dicts) with new user message
|
97 |
+
new_history = history + [{"role": "user", "content": user_input}]
|
98 |
+
# Insert docs as system messages
|
99 |
+
new_history += docs
|
100 |
+
# Build prompt text from history
|
101 |
+
prompt = "\n".join([f"{m['role']}: {m['content']}" for m in new_history])
|
102 |
resp = asyncio.run(get_response(prompt))
|
103 |
+
new_history.append({"role": "assistant", "content": resp})
|
104 |
+
return new_history, ""
|
105 |
+
|
106 |
send.click(chat_fn, inputs=[msg, chatbot], outputs=[chatbot, msg])
|
107 |
msg.submit(chat_fn, inputs=[msg, chatbot], outputs=[chatbot, msg])
|
108 |
+
|
109 |
gr.Markdown(f"""
|
110 |
+
---
|
111 |
+
**์ํ**: ChromaDB์ ์ค๋น๋ ๋ฌธ์ ์ = {rag_system.collection.count()}
|
112 |
+
"""
|
|
|
|
|
|
|
113 |
)
|
114 |
|
115 |
if __name__ == "__main__":
|
116 |
+
app.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT",7860)), share=False, show_api=False)
|
|
|
|
|
|
|
|
|
|