Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -3,139 +3,144 @@
|
|
3 |
import gradio as gr
|
4 |
import os
|
5 |
import subprocess
|
6 |
-
from
|
7 |
-
from
|
8 |
-
from langchain.agents import AgentExecutor, create_tool_calling_agent
|
9 |
-
from langchain_core.prompts import ChatPromptTemplate
|
10 |
-
from langchain.tools import tool
|
11 |
|
12 |
-
# --- 1. 환경 설정 및
|
13 |
-
API_KEY = os.environ.get("MISTRAL_API_KEY"
|
14 |
-
|
15 |
if API_KEY:
|
16 |
try:
|
17 |
-
|
|
|
18 |
except Exception as e:
|
19 |
-
print(f"
|
20 |
else:
|
21 |
print("FATAL: MISTRAL_API_KEY is not set in Space Secrets.")
|
22 |
|
23 |
-
# --- 2. 핵심
|
24 |
-
# LangChain 에이전트가 내부적으로 사용할 @tool 데코레이터
|
25 |
-
@tool
|
26 |
def generate_c_code(description: str) -> str:
|
27 |
-
"""Generates
|
28 |
-
if not
|
29 |
-
|
30 |
-
|
31 |
-
The code
|
32 |
-
|
33 |
-
|
34 |
-
|
|
|
|
|
|
|
|
|
35 |
if "```c" in code:
|
36 |
code = code.split("```c")[1].split("```")[0].strip()
|
|
|
|
|
|
|
37 |
return code
|
38 |
|
39 |
-
@tool
|
40 |
def compile_and_run_c_code(code: str) -> str:
|
41 |
-
"""Compiles and runs a given C code snippet and returns its output or any errors.
|
42 |
try:
|
43 |
with open("main.c", "w", encoding='utf-8') as f:
|
44 |
f.write(code)
|
|
|
45 |
compile_proc = subprocess.run(
|
46 |
["gcc", "main.c", "-o", "main.out", "-lm", "-w"],
|
47 |
capture_output=True, text=True, timeout=15
|
48 |
)
|
49 |
if compile_proc.returncode != 0:
|
50 |
return f"--- COMPILATION FAILED ---\n{compile_proc.stderr}"
|
|
|
51 |
run_proc = subprocess.run(
|
52 |
["./main.out"], capture_output=True, text=True, timeout=15
|
53 |
)
|
54 |
if run_proc.returncode != 0:
|
55 |
return f"--- RUNTIME ERROR ---\n{run_proc.stderr}"
|
|
|
56 |
output = run_proc.stdout
|
57 |
if not output.strip():
|
58 |
return "--- EXECUTION SUCCEEDED ---\n(No output was produced)"
|
59 |
return f"--- EXECUTION SUCCEEDED ---\n{output}"
|
|
|
60 |
except subprocess.TimeoutExpired:
|
61 |
return "--- ERROR ---\nProcess timed out. Check for infinite loops."
|
62 |
except Exception as e:
|
63 |
return f"--- SYSTEM ERROR ---\nAn unexpected error occurred: {str(e)}"
|
64 |
|
65 |
-
|
66 |
-
def
|
67 |
-
|
68 |
-
|
69 |
-
analysis_prompt = f"""You are a senior C code reviewer. Analyze the following C code based on the user's request.
|
70 |
-
Provide a clear, concise, and helpful response. If refactoring, provide the complete improved code in a C code block.
|
71 |
-
User's Request: '{request}'
|
72 |
-
C Code to Analyze:
|
73 |
-
```c
|
74 |
-
{code}
|
75 |
-
```"""
|
76 |
-
response = llm.invoke([HumanMessage(content=analysis_prompt)])
|
77 |
-
return response.content
|
78 |
-
|
79 |
-
# --- 3. MCP 도구 API 생성 ---
|
80 |
-
# 각 도구를 gr.Interface를 사용해 API로 만듭니다. 이것이 외부 MCP 클라이언트에 노출됩니다.
|
81 |
-
generate_api = gr.Interface(fn=generate_c_code, inputs="text", outputs="code", title="C Code Generator", description="Generates C code from a description.")
|
82 |
-
compile_api = gr.Interface(fn=compile_and_run_c_code, inputs="code", outputs="text", title="C Code Compiler & Runner", description="Compiles and runs C code.")
|
83 |
-
refactor_api = gr.Interface(fn=refactor_or_analyze_c_code, inputs=["code", "text"], outputs="markdown", title="C Code Refactor & Analyzer", description="Refactors or analyzes C code.")
|
84 |
-
|
85 |
-
# --- 4. LangChain 에이전트 설정 ---
|
86 |
-
tools = [generate_c_code, compile_and_run_c_code, refactor_or_analyze_c_code]
|
87 |
-
agent_executor = None
|
88 |
-
if llm:
|
89 |
-
agent_prompt = ChatPromptTemplate.from_messages([
|
90 |
-
("system", "You are a powerful C-language assistant agent. You can generate, compile, and analyze C code. Think step-by-step and use your tools to fulfill the user's request. If you generate code, you should almost always offer to compile and run it for the user as the next step."),
|
91 |
-
("placeholder", "{chat_history}"),
|
92 |
-
("human", "{input}"),
|
93 |
-
("placeholder", "{agent_scratchpad}"),
|
94 |
-
])
|
95 |
-
agent = create_tool_calling_agent(llm, tools, agent_prompt)
|
96 |
-
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, handle_parsing_errors=True)
|
97 |
-
|
98 |
-
def agent_chat(user_input, history):
|
99 |
-
if agent_executor is None:
|
100 |
-
yield "Error: MISTRAL_API_KEY is not configured. Please set it in the Space secrets."
|
101 |
return
|
102 |
-
|
|
|
|
|
103 |
for human, ai in history:
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
112 |
|
113 |
-
# ---
|
114 |
-
with gr.Blocks(theme=gr.themes.
|
115 |
gr.Markdown("# 🚀 C-Codestral Agent")
|
116 |
with gr.Tabs():
|
|
|
117 |
with gr.TabItem("🤖 Agent Chat"):
|
118 |
-
chatbot = gr.Chatbot(label="C-Agent",
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
submit_btn.click(agent_chat, [txt, chatbot], chatbot)
|
124 |
-
txt.submit(lambda: "", None, txt)
|
125 |
-
submit_btn.click(lambda: "", None, txt)
|
126 |
gr.Examples(
|
127 |
-
["Generate a C program to calculate the factorial of
|
128 |
-
inputs=
|
129 |
)
|
130 |
-
|
|
|
131 |
with gr.TabItem("🛠️ MCP Tools API"):
|
132 |
gr.Markdown("## Available MCP Tools\nThese APIs can be used by any MCP-compliant client.")
|
133 |
with gr.Accordion("Tool: Generate C Code", open=False):
|
134 |
-
|
135 |
with gr.Accordion("Tool: Compile & Run C Code", open=False):
|
136 |
-
|
137 |
-
with gr.Accordion("Tool: Refactor & Analyze C Code", open=False):
|
138 |
-
refactor_api.render()
|
139 |
|
140 |
if __name__ == "__main__":
|
141 |
-
demo.queue().launch(
|
|
|
3 |
import gradio as gr
|
4 |
import os
|
5 |
import subprocess
|
6 |
+
from mistralai.client import MistralClient
|
7 |
+
from mistralai.models.chat_completion import ChatMessage
|
|
|
|
|
|
|
8 |
|
9 |
+
# --- 1. 환경 설정 및 클라이언트 초기화 ---
|
10 |
+
API_KEY = os.environ.get("MISTRAL_API_KEY")
|
11 |
+
CLIENT = None
|
12 |
if API_KEY:
|
13 |
try:
|
14 |
+
CLIENT = MistralClient(api_key=API_KEY)
|
15 |
+
print("Mistral API Client initialized successfully.")
|
16 |
except Exception as e:
|
17 |
+
print(f"Error initializing Mistral Client: {e}")
|
18 |
else:
|
19 |
print("FATAL: MISTRAL_API_KEY is not set in Space Secrets.")
|
20 |
|
21 |
+
# --- 2. 핵심 기능 함수 (MCP 도구로 사용될 함수들) ---
|
|
|
|
|
22 |
def generate_c_code(description: str) -> str:
|
23 |
+
"""Generates C code based on a natural language description."""
|
24 |
+
if not CLIENT: return "Error: Mistral API key not configured."
|
25 |
+
|
26 |
+
prompt = f"""You are a C programming expert. Your task is to generate a complete, compilable C code based on the user's request.
|
27 |
+
The code should be clean, efficient, and well-commented.
|
28 |
+
Please only output the raw C code inside a single C code block and nothing else.
|
29 |
+
User's request: {description}"""
|
30 |
+
|
31 |
+
messages = [ChatMessage(role="user", content=prompt)]
|
32 |
+
response = CLIENT.chat(model="codestral-latest", messages=messages)
|
33 |
+
code = response.choices[0].message.content
|
34 |
+
|
35 |
if "```c" in code:
|
36 |
code = code.split("```c")[1].split("```")[0].strip()
|
37 |
+
elif "```" in code:
|
38 |
+
code = code.split("```")[1].split("```")[0].strip()
|
39 |
+
|
40 |
return code
|
41 |
|
|
|
42 |
def compile_and_run_c_code(code: str) -> str:
|
43 |
+
"""Compiles and runs a given C code snippet and returns its output or any errors."""
|
44 |
try:
|
45 |
with open("main.c", "w", encoding='utf-8') as f:
|
46 |
f.write(code)
|
47 |
+
|
48 |
compile_proc = subprocess.run(
|
49 |
["gcc", "main.c", "-o", "main.out", "-lm", "-w"],
|
50 |
capture_output=True, text=True, timeout=15
|
51 |
)
|
52 |
if compile_proc.returncode != 0:
|
53 |
return f"--- COMPILATION FAILED ---\n{compile_proc.stderr}"
|
54 |
+
|
55 |
run_proc = subprocess.run(
|
56 |
["./main.out"], capture_output=True, text=True, timeout=15
|
57 |
)
|
58 |
if run_proc.returncode != 0:
|
59 |
return f"--- RUNTIME ERROR ---\n{run_proc.stderr}"
|
60 |
+
|
61 |
output = run_proc.stdout
|
62 |
if not output.strip():
|
63 |
return "--- EXECUTION SUCCEEDED ---\n(No output was produced)"
|
64 |
return f"--- EXECUTION SUCCEEDED ---\n{output}"
|
65 |
+
|
66 |
except subprocess.TimeoutExpired:
|
67 |
return "--- ERROR ---\nProcess timed out. Check for infinite loops."
|
68 |
except Exception as e:
|
69 |
return f"--- SYSTEM ERROR ---\nAn unexpected error occurred: {str(e)}"
|
70 |
|
71 |
+
# --- 3. Gradio 챗봇 로직 (Agent 역할) ---
|
72 |
+
def agent_chat(message, history):
|
73 |
+
if not CLIENT:
|
74 |
+
yield "MISTRAL_API_KEY를 Space Secrets에 설정해주세요."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
return
|
76 |
+
|
77 |
+
# 대화 기록을 포함하여 LLM에게 전달
|
78 |
+
history_messages = []
|
79 |
for human, ai in history:
|
80 |
+
history_messages.append(ChatMessage(role="user", content=human))
|
81 |
+
history_messages.append(ChatMessage(role="assistant", content=ai))
|
82 |
+
|
83 |
+
history_messages.append(ChatMessage(role="user", content=message))
|
84 |
+
|
85 |
+
# 시스템 프롬프트: 에이전트의 역할을 정의
|
86 |
+
system_prompt = """You are a helpful C programming assistant.
|
87 |
+
- If the user asks to generate code, call the `generate_c_code` function by outputting a JSON object like: `{"tool": "generate_c_code", "description": "user's request"}`.
|
88 |
+
- If the user asks to compile or run code, find the last C code block in the conversation and call the `compile_and_run_c_code` function by outputting a JSON object like: `{"tool": "compile_and_run_c_code", "code": "the c code"}`.
|
89 |
+
- For anything else, answer as a helpful assistant."""
|
90 |
+
|
91 |
+
# 이 예제에서는 LLM이 도구를 호출하는 대신, 간단한 키워드 기반 라우팅으로 안정성을 확보합니다.
|
92 |
+
# 복잡한 에이전트 로직을 제거하여 라이브러리 충돌을 원천 차단합니다.
|
93 |
+
lower_message = message.lower()
|
94 |
+
|
95 |
+
if "compile" in lower_message or "run" in lower_message or "실행" in lower_message:
|
96 |
+
# 히스토리에서 코드 찾기
|
97 |
+
code_to_run = ""
|
98 |
+
for h_user, h_ai in reversed(history):
|
99 |
+
if "```c" in h_ai:
|
100 |
+
code_to_run = h_ai.split("```c")[1].split("```")[0].strip()
|
101 |
+
break
|
102 |
+
if not code_to_run:
|
103 |
+
yield "컴파일할 코드를 찾을 수 없습니다. 먼저 코드를 생성해주세요."
|
104 |
+
return
|
105 |
+
|
106 |
+
yield f"코드를 컴파일하고 실행합니다...\n\n```c\n{code_to_run}\n```"
|
107 |
+
result = compile_and_run_c_code(code_to_run)
|
108 |
+
yield result
|
109 |
+
|
110 |
+
elif "generate" in lower_message or "create" in lower_message or "만들어줘" in lower_message or "짜줘" in lower_message:
|
111 |
+
yield "C 코드를 생성하는 중..."
|
112 |
+
generated_code = generate_c_code(message)
|
113 |
+
yield f"코드가 생성되었습니다. 컴파일하거나 실행하려면 'run this code'라고 입력하세요.\n\n```c\n{generated_code}\n```"
|
114 |
+
|
115 |
+
else:
|
116 |
+
# 일반 대화
|
117 |
+
response = CLIENT.chat(model="codestral-latest", messages=history_messages)
|
118 |
+
yield response.choices[0].message.content
|
119 |
+
|
120 |
|
121 |
+
# --- 4. Gradio UI 구성 ---
|
122 |
+
with gr.Blocks(theme=gr.themes.Soft(), css=".gradio-container{max-width: 800px !important; margin: auto;}") as demo:
|
123 |
gr.Markdown("# 🚀 C-Codestral Agent")
|
124 |
with gr.Tabs():
|
125 |
+
# 메인 챗봇 UI
|
126 |
with gr.TabItem("🤖 Agent Chat"):
|
127 |
+
chatbot = gr.Chatbot(label="C-Agent", height=600, bubble_full_width=False)
|
128 |
+
msg = gr.Textbox(placeholder="Ask me to generate or run C code...", label="Your Request")
|
129 |
+
msg.submit(agent_chat, [msg, chatbot], chatbot)
|
130 |
+
msg.submit(lambda: "", None, msg) # Clear input after submit
|
131 |
+
|
|
|
|
|
|
|
132 |
gr.Examples(
|
133 |
+
["Generate a C program to calculate the factorial of 10.", "run the code"],
|
134 |
+
inputs=msg
|
135 |
)
|
136 |
+
|
137 |
+
# MCP 도구 API 탭
|
138 |
with gr.TabItem("🛠️ MCP Tools API"):
|
139 |
gr.Markdown("## Available MCP Tools\nThese APIs can be used by any MCP-compliant client.")
|
140 |
with gr.Accordion("Tool: Generate C Code", open=False):
|
141 |
+
gr.Interface(fn=generate_c_code, inputs="text", outputs=gr.Code(language="c"), allow_flagging="never")
|
142 |
with gr.Accordion("Tool: Compile & Run C Code", open=False):
|
143 |
+
gr.Interface(fn=compile_and_run_c_code, inputs=gr.Code(language="c"), outputs="text", allow_flagging="never")
|
|
|
|
|
144 |
|
145 |
if __name__ == "__main__":
|
146 |
+
demo.queue().launch()
|