ginipick's picture
Update app.py
eac18b7 verified
raw
history blame
20.1 kB
import os
import gradio as gr
from gradio import ChatMessage
from typing import Iterator, List, Dict, Tuple, Any
import google.generativeai as genai
from huggingface_hub import HfApi
import requests
import re
import traceback
# HuggingFace κ΄€λ ¨ API ν‚€ (슀페이슀 뢄석 용)
HF_TOKEN = os.getenv("HF_TOKEN")
hf_api = HfApi(token=HF_TOKEN)
# Gemini 2.0 Flash Thinking λͺ¨λΈ κ΄€λ ¨ API ν‚€ 및 ν΄λΌμ΄μ–ΈνŠΈ (LLM 용)
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
genai.configure(api_key=GEMINI_API_KEY)
model = genai.GenerativeModel("gemini-2.0-flash-thinking-exp-01-21")
def get_headers():
if not HF_TOKEN:
raise ValueError("Hugging Face token not found in environment variables")
return {"Authorization": f"Bearer {HF_TOKEN}"}
def get_file_content(space_id: str, file_path: str) -> str:
file_url = f"https://huggingface.co/spaces/{space_id}/raw/main/{file_path}"
try:
response = requests.get(file_url, headers=get_headers())
if response.status_code == 200:
return response.text
else:
return f"File not found or inaccessible: {file_path}"
except requests.RequestException:
return f"Error fetching content for file: {file_path}"
def get_space_structure(space_id: str) -> Dict:
try:
files = hf_api.list_repo_files(repo_id=space_id, repo_type="space")
tree = {"type": "directory", "path": "", "name": space_id, "children": []}
for file in files:
path_parts = file.split('/')
current = tree
for i, part in enumerate(path_parts):
if i == len(path_parts) - 1: # 파일
current["children"].append({"type": "file", "path": file, "name": part})
else:
found = False
for child in current["children"]:
if child["type"] == "directory" and child["name"] == part:
current = child
found = True
break
if not found:
new_dir = {"type": "directory", "path": '/'.join(path_parts[:i+1]), "name": part, "children": []}
current["children"].append(new_dir)
current = new_dir
return tree
except Exception as e:
print(f"Error in get_space_structure: {str(e)}")
return {"error": f"API request error: {str(e)}"}
def format_tree_structure(tree_data: Dict, indent: str = "") -> str:
if "error" in tree_data:
return tree_data["error"]
formatted = f"{indent}{'πŸ“' if tree_data.get('type') == 'directory' else 'πŸ“„'} {tree_data.get('name', 'Unknown')}\n"
if tree_data.get("type") == "directory":
# 디렉토리λ₯Ό λ¨Όμ €, νŒŒμΌμ„ λ‚˜μ€‘μ— ν‘œμ‹œ
for child in sorted(tree_data.get("children", []), key=lambda x: (x.get("type", "") != "directory", x.get("name", ""))):
formatted += format_tree_structure(child, indent + " ")
return formatted
def analyze_space(url: str, progress=gr.Progress()):
"""
HuggingFace Space의 app.py와 파일ꡬ쑰 등을 λΆˆλŸ¬μ™€μ„œ:
1) μ½”λ“œ μš”μ•½
2) μ½”λ“œ 뢄석
3) μ‚¬μš©λ²•
등을 λ°˜ν™˜ν•©λ‹ˆλ‹€.
"""
try:
space_id = url.split('spaces/')[-1]
if not re.match(r'^[\w.-]+/[\w.-]+$', space_id):
raise ValueError(f"Invalid Space ID format: {space_id}")
progress(0.1, desc="파일 ꡬ쑰 뢄석 쀑...")
tree_structure = get_space_structure(space_id)
if "error" in tree_structure:
raise ValueError(tree_structure["error"])
tree_view = format_tree_structure(tree_structure)
progress(0.3, desc="app.py λ‚΄μš© κ°€μ Έμ˜€λŠ” 쀑...")
app_content = get_file_content(space_id, "app.py")
progress(0.5, desc="μ½”λ“œ μš”μ•½ 쀑...")
summary = summarize_code(app_content)
progress(0.7, desc="μ½”λ“œ 뢄석 쀑...")
analysis = analyze_code(app_content)
progress(0.9, desc="μ‚¬μš©λ²• μ„€λͺ… 생성 쀑...")
usage = explain_usage(app_content)
lines_for_app_py = adjust_lines_for_code(app_content)
progress(1.0, desc="μ™„λ£Œ")
return app_content, tree_view, tree_structure, space_id, summary, analysis, usage, lines_for_app_py
except Exception as e:
print(f"Error in analyze_space: {str(e)}")
print(traceback.format_exc())
return f"였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€: {str(e)}", "", None, "", "", "", "", 10
def adjust_lines_for_code(code_content: str, min_lines: int = 10, max_lines: int = 100) -> int:
"""
μ½”λ“œμ˜ 쀄 μˆ˜μ— 맞좰 ν‘œμ‹œν•  lines 수λ₯Ό λ™μ μœΌλ‘œ μ‘°μ •ν•©λ‹ˆλ‹€.
"""
num_lines = len(code_content.split('\n'))
return min(max(num_lines, min_lines), max_lines)
# --------------------------------------------------
# Gemini 2.0 Flash Thinking λͺ¨λΈ (LLM) ν•¨μˆ˜λ“€
# --------------------------------------------------
from gradio import ChatMessage
def format_chat_history(messages: List[ChatMessage]) -> List[Dict]:
"""
ChatMessage λͺ©λ‘μ„ Gemini λͺ¨λΈμ΄ 이해할 수 μžˆλŠ” ν˜•μ‹μœΌλ‘œ λ³€ν™˜
(Thinking 메타데이터가 μžˆλŠ” λ©”μ‹œμ§€λŠ” λ¬΄μ‹œ)
"""
formatted = []
for m in messages:
if hasattr(m, "metadata") and m.metadata: # 'Thinking' λ©”μ‹œμ§€λŠ” μ œμ™Έ
continue
role = "assistant" if m.role == "assistant" else "user"
formatted.append({"role": role, "parts": [m.content or ""]})
return formatted
import google.generativeai as genai
def gemini_chat_completion(system_message: str, user_message: str, max_tokens: int = 200, temperature: float = 0.7) -> str:
"""
μ‹œμŠ€ν…œ & μœ μ € λ©”μ‹œμ§€λ‘œ Gemini λͺ¨λΈμ—κ²Œ 슀트리밍 μš”μ²­. μ΅œμ’… ν…μŠ€νŠΈ λ°˜ν™˜
"""
init_msgs = [
ChatMessage(role="system", content=system_message),
ChatMessage(role="user", content=user_message)
]
chat_history = format_chat_history(init_msgs)
chat = model.start_chat(history=chat_history)
final = ""
try:
for chunk in chat.send_message(user_message, stream=True):
parts = chunk.candidates[0].content.parts
if len(parts) == 2:
final += parts[1].text
else:
final += parts[0].text
return final.strip()
except Exception as e:
return f"LLM 호좜 쀑 였λ₯˜ λ°œμƒ: {str(e)}"
def summarize_code(app_content: str):
system_msg = "당신은 Python μ½”λ“œλ₯Ό λΆ„μ„ν•˜κ³  μš”μ•½ν•˜λŠ” AI μ‘°μˆ˜μž…λ‹ˆλ‹€. μ£Όμ–΄μ§„ μ½”λ“œλ₯Ό 3쀄 μ΄λ‚΄λ‘œ κ°„κ²°ν•˜κ²Œ μš”μ•½ν•΄μ£Όμ„Έμš”."
user_msg = f"λ‹€μŒ Python μ½”λ“œλ₯Ό 3쀄 μ΄λ‚΄λ‘œ μš”μ•½ν•΄μ£Όμ„Έμš”:\n\n{app_content}"
try:
return gemini_chat_completion(system_msg, user_msg, max_tokens=200, temperature=0.7)
except Exception as e:
return f"μš”μ•½ 생성 쀑 였λ₯˜ λ°œμƒ: {str(e)}"
def analyze_code(app_content: str):
system_msg = (
"You are a deep thinking AI. You may use extremely long chains of thought to deeply consider the problem "
"and deliberate with yourself via systematic reasoning processes to help come to a correct solution prior to answering. "
"You should enclose your thoughts and internal monologue inside tags, and then provide your solution or response to the problem. "
"당신은 Python μ½”λ“œλ₯Ό λΆ„μ„ν•˜λŠ” AI μ‘°μˆ˜μž…λ‹ˆλ‹€. μ£Όμ–΄μ§„ μ½”λ“œλ₯Ό λΆ„μ„ν•˜μ—¬ μ„œλΉ„μŠ€μ˜ νš¨μš©μ„±κ³Ό ν™œμš© μΈ‘λ©΄μ—μ„œ λ‹€μŒ ν•­λͺ©μ— λŒ€ν•΄ μ„€λͺ…ν•΄μ£Όμ„Έμš”:\n"
"A. λ°°κ²½ 및 ν•„μš”μ„±\n"
"B. κΈ°λŠ₯적 νš¨μš©μ„± 및 κ°€μΉ˜\n"
"C. 특μž₯점\n"
"D. 적용 λŒ€μƒ 및 νƒ€κ²Ÿ\n"
"E. κΈ°λŒ€νš¨κ³Ό\n"
"κΈ°μ‘΄ 및 μœ μ‚¬ ν”„λ‘œμ νŠΈμ™€ λΉ„κ΅ν•˜μ—¬ λΆ„μ„ν•΄μ£Όμ„Έμš”. Markdown ν˜•μ‹μœΌλ‘œ 좜λ ₯ν•˜μ„Έμš”."
)
user_msg = f"λ‹€μŒ Python μ½”λ“œλ₯Ό λΆ„μ„ν•΄μ£Όμ„Έμš”:\n\n{app_content}"
try:
return gemini_chat_completion(system_msg, user_msg, max_tokens=1000, temperature=0.7)
except Exception as e:
return f"뢄석 생성 쀑 였λ₯˜ λ°œμƒ: {str(e)}"
def explain_usage(app_content: str):
system_msg = (
"You are a deep thinking AI. You may use extremely long chains of thought to deeply consider the problem "
"and deliberate with yourself via systematic reasoning processes to help come to a correct solution prior to answering. "
"You should enclose your thoughts and internal monologue inside tags, and then provide your solution or response to the problem. "
"당신은 Python μ½”λ“œλ₯Ό λΆ„μ„ν•˜μ—¬ μ‚¬μš©λ²•μ„ μ„€λͺ…ν•˜λŠ” AI μ‘°μˆ˜μž…λ‹ˆλ‹€. μ£Όμ–΄μ§„ μ½”λ“œλ₯Ό λ°”νƒ•μœΌλ‘œ 마치 화면을 λ³΄λŠ” κ²ƒμ²˜λŸΌ μ‚¬μš©λ²•μ„ μƒμ„Ένžˆ μ„€λͺ…ν•΄μ£Όμ„Έμš”. Markdown ν˜•μ‹μœΌλ‘œ 좜λ ₯ν•˜μ„Έμš”."
)
user_msg = f"λ‹€μŒ Python μ½”λ“œμ˜ μ‚¬μš©λ²•μ„ μ„€λͺ…ν•΄μ£Όμ„Έμš”:\n\n{app_content}"
try:
return gemini_chat_completion(system_msg, user_msg, max_tokens=800, temperature=0.7)
except Exception as e:
return f"μ‚¬μš©λ²• μ„€λͺ… 생성 쀑 였λ₯˜ λ°œμƒ: {str(e)}"
def stream_gemini_response(user_message: str, conversation_state: List[ChatMessage]) -> Iterator[List[ChatMessage]]:
"""
Gemini에 슀트리밍 μš”μ²­.
빈 λ©”μ‹œμ§€λ„ μ—¬κΈ°μ„œ 처리(μ—λŸ¬ 없이)ν•˜λ„λ‘ 함.
"""
# λ§Œμ•½ user_messageκ°€ μ™„μ „ 빈 λ¬Έμžμ—΄μ΄λΌλ©΄, λͺ¨λΈ 호좜 λŒ€μ‹  간단 μ•ˆλ‚΄
if not user_message.strip():
conversation_state.append(
ChatMessage(
role="assistant",
content="(Note: You sent an empty message. No LLM call was made.)"
)
)
yield conversation_state
return
print(f"\n=== New Request ===\nUser message: {user_message}")
chat_history = format_chat_history(conversation_state)
chat = model.start_chat(history=chat_history)
response = chat.send_message(user_message, stream=True)
thought_buffer = ""
response_buffer = ""
thinking_complete = False
# 'Thinking' ν‘œμ‹œμš© λ©”μ‹œμ§€ μΆ”κ°€
conversation_state.append(
ChatMessage(
role="assistant",
content="",
metadata={"title": "βš™οΈ Thinking: *The thoughts produced by the model are experimental"}
)
)
try:
for chunk in response:
parts = chunk.candidates[0].content.parts
current_chunk = parts[0].text
if len(parts) == 2 and not thinking_complete:
thought_buffer += current_chunk
print(f"\n=== Complete Thought ===\n{thought_buffer}")
conversation_state[-1] = ChatMessage(
role="assistant",
content=thought_buffer,
metadata={"title": "βš™οΈ Thinking: *The thoughts produced by the model are experimental"}
)
yield conversation_state
response_buffer = parts[1].text
print(f"\n=== Starting Response ===\n{response_buffer}")
conversation_state.append(
ChatMessage(role="assistant", content=response_buffer)
)
thinking_complete = True
elif thinking_complete:
response_buffer += current_chunk
print(f"\n=== Response Chunk ===\n{current_chunk}")
conversation_state[-1] = ChatMessage(
role="assistant",
content=response_buffer
)
else:
thought_buffer += current_chunk
print(f"\n=== Thinking Chunk ===\n{current_chunk}")
conversation_state[-1] = ChatMessage(
role="assistant",
content=thought_buffer,
metadata={"title": "βš™οΈ Thinking: *The thoughts produced by the model are experimental"}
)
yield conversation_state
print(f"\n=== Final Response ===\n{response_buffer}")
except Exception as e:
print(f"\n=== Error ===\n{str(e)}")
conversation_state.append(
ChatMessage(
role="assistant",
content=f"I apologize, but encountered an error: {str(e)}"
)
)
yield conversation_state
def convert_to_display_tuples(messages: List[ChatMessage]) -> List[Tuple[str, str]]:
"""
ChatMessage 리슀트 -> (user, assistant) νŠœν”Œ 리슀트
"""
result = []
i = 0
while i < len(messages):
if messages[i].role == "user":
user_text = messages[i].content
assistant_text = ""
if i + 1 < len(messages) and messages[i+1].role == "assistant":
assistant_text = messages[i+1].content
i += 2
else:
i += 1
result.append((user_text, assistant_text))
else:
# assistant 단독
result.append(("", messages[i].content))
i += 1
return result
def user_submit_message(msg: str, conversation_state: List[ChatMessage]):
"""
μ‚¬μš©μžκ°€ λ©”μ‹œμ§€λ₯Ό μž…λ ₯ν•  λ•Œ 호좜
"""
conversation_state.append(ChatMessage(role="user", content=msg))
# μž…λ ₯창은 λΉ„μ›Œμ€Œ
return "", conversation_state
def respond_wrapper(message: str, conversation_state: List[ChatMessage], max_tokens, temperature, top_p):
"""
Gemini에 슀트리밍 μš”μ²­ -> λŒ€ν™” 이λ ₯을 κ°±μ‹  -> (user, assistant) νŠœν”Œλ‘œ λ³€ν™˜ν•˜μ—¬ 화면에 ν‘œμ‹œ
"""
for updated_messages in stream_gemini_response(message, conversation_state):
yield "", convert_to_display_tuples(updated_messages)
def create_ui():
"""
Gradio UIλ₯Ό κ΅¬μ„±ν•˜λŠ” ν•¨μˆ˜
"""
try:
css = """
footer {visibility: hidden;}
"""
with gr.Blocks(css=css) as demo:
gr.Markdown("# MOUSE: Space Research Thinking")
with gr.Tabs():
with gr.TabItem("뢄석"):
with gr.Row():
with gr.Column():
url_input = gr.Textbox(label="HuggingFace Space URL")
analyze_button = gr.Button("뢄석")
summary_output = gr.Markdown(label="μš”μ•½")
analysis_output = gr.Markdown(label="뢄석")
usage_output = gr.Markdown(label="μ‚¬μš©λ²•")
tree_view_output = gr.Textbox(label="파일 ꡬ쑰", lines=20)
with gr.Column():
code_tabs = gr.Tabs()
with code_tabs:
with gr.TabItem("app.py"):
app_py_content = gr.Code(
language="python",
label="app.py",
lines=50
)
with gr.TabItem("requirements.txt"):
requirements_content = gr.Textbox(
label="requirements.txt",
lines=50
)
with gr.TabItem("AI μ½”λ“œμ±—"):
gr.Markdown("## 예제λ₯Ό μž…λ ₯ λ˜λŠ” μ†ŒμŠ€ μ½”λ“œλ₯Ό λΆ™μ—¬λ„£κ³  μ§ˆλ¬Έν•˜μ„Έμš”")
# Chatbot에 type="messages"둜 μ„€μ • (ꢌμž₯)
chatbot = gr.Chatbot(
label="λŒ€ν™”",
height=400,
type="messages"
)
msg = gr.Textbox(
label="λ©”μ‹œμ§€",
placeholder="λ©”μ‹œμ§€λ₯Ό μž…λ ₯ν•˜μ„Έμš”..."
)
max_tokens = gr.Slider(
minimum=1, maximum=8000,
value=4000, label="Max Tokens",
visible=False
)
temperature = gr.Slider(
minimum=0, maximum=1,
value=0.7, label="Temperature",
visible=False
)
top_p = gr.Slider(
minimum=0, maximum=1,
value=0.9, label="Top P",
visible=False
)
examples = [
["μƒμ„Έν•œ μ‚¬μš© 방법을 4000 토큰 이상 μƒμ„Ένžˆ μ„€λͺ…"],
["FAQ 20건을 4000 토큰 이상 μž‘μ„±"],
["기술 차별점, 강점을 μ€‘μ‹¬μœΌλ‘œ 4000 토큰 이상 μ„€λͺ…"],
["νŠΉν—ˆ μΆœμ›μ— ν™œμš© κ°€λŠ₯ν•œ ν˜μ‹  아이디어λ₯Ό 4000 토큰 이상 μž‘μ„±"],
["λ…Όλ¬Έ ν˜•μ‹μœΌλ‘œ 4000 토큰 이상 μž‘μ„±"],
["계속 μ΄μ–΄μ„œ λ‹΅λ³€ν•˜λΌ"]
]
gr.Examples(examples, inputs=msg)
# λŒ€ν™” μƒνƒœ(μ±„νŒ… 기둝)λŠ” ChatMessage 객체둜만 관리
conversation_state = gr.State([])
# 1) μœ μ € λ©”μ‹œμ§€ μž…λ ₯ -> user_submit_message
# 2) respond_wrapper -> Gemini 슀트리밍 -> λŒ€ν™” μ—…λ°μ΄νŠΈ -> (user,assistant) λ³€ν™˜ν•˜μ—¬ chatbot ν‘œμ‹œ
msg.submit(
user_submit_message,
inputs=[msg, conversation_state],
outputs=[msg, conversation_state],
queue=False
).then(
respond_wrapper,
inputs=[msg, conversation_state, max_tokens, temperature, top_p],
outputs=[msg, chatbot],
)
with gr.TabItem("Recommended Best"):
gr.Markdown(
"Discover recommended HuggingFace Spaces [here](https://huggingface.co/spaces/openfree/Korean-Leaderboard)."
)
# 뢄석 νƒ­ 둜직
space_id_state = gr.State()
tree_structure_state = gr.State()
app_py_content_lines = gr.State()
analyze_button.click(
analyze_space,
inputs=[url_input],
outputs=[
app_py_content,
tree_view_output,
tree_structure_state,
space_id_state,
summary_output,
analysis_output,
usage_output,
app_py_content_lines
]
).then(
lambda space_id: get_file_content(space_id, "requirements.txt"),
inputs=[space_id_state],
outputs=[requirements_content]
).then(
lambda lines: gr.update(lines=lines),
inputs=[app_py_content_lines],
outputs=[app_py_content]
)
return demo
except Exception as e:
print(f"Error in create_ui: {str(e)}")
print(traceback.format_exc())
raise
if __name__ == "__main__":
try:
print("Starting HuggingFace Space Analyzer...")
demo = create_ui()
print("UI created successfully.")
print("Configuring Gradio queue...")
demo.queue()
print("Gradio queue configured.")
print("Launching Gradio app...")
demo.launch(
server_name="0.0.0.0",
server_port=7860,
share=False,
debug=True,
show_api=False
)
print("Gradio app launched successfully.")
except Exception as e:
print(f"Error in main: {str(e)}")
print("Detailed error information:")
print(traceback.format_exc())
raise