Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -1,16 +1,19 @@
|
|
1 |
# /app.py
|
2 |
|
3 |
-
import gradio as gr
|
4 |
import os
|
5 |
import subprocess
|
|
|
|
|
|
|
6 |
from mistralai.client import MistralClient
|
7 |
|
8 |
-
# --- 1.
|
|
|
|
|
9 |
API_KEY = os.environ.get("MISTRAL_API_KEY")
|
10 |
CLIENT = None
|
11 |
if API_KEY:
|
12 |
try:
|
13 |
-
# NOTE: requirements.txt에 mistralai==0.4.2 버전을 명시해야 합니다.
|
14 |
CLIENT = MistralClient(api_key=API_KEY)
|
15 |
print("Mistral API Client initialized successfully.")
|
16 |
except Exception as e:
|
@@ -18,27 +21,18 @@ if API_KEY:
|
|
18 |
else:
|
19 |
print("FATAL: MISTRAL_API_KEY is not set in Space Secrets.")
|
20 |
|
21 |
-
# --- 2. 핵심 기능 함수
|
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 = [{"role": "user", "content": prompt}]
|
32 |
-
|
33 |
response = CLIENT.chat(model="codestral-latest", messages=messages)
|
34 |
code = response.choices[0].message.content
|
35 |
-
|
36 |
-
# 코드 블록에서 순수 코드만 추출
|
37 |
if "```c" in code:
|
38 |
code = code.split("```c")[1].split("```")[0].strip()
|
39 |
elif "```" in code:
|
40 |
code = code.split("```")[1].split("```")[0].strip()
|
41 |
-
|
42 |
return code
|
43 |
|
44 |
def compile_and_run_c_code(code: str) -> str:
|
@@ -46,99 +40,162 @@ def compile_and_run_c_code(code: str) -> str:
|
|
46 |
try:
|
47 |
with open("main.c", "w", encoding='utf-8') as f:
|
48 |
f.write(code)
|
49 |
-
|
50 |
compile_proc = subprocess.run(
|
51 |
["gcc", "main.c", "-o", "main.out", "-lm", "-w"],
|
52 |
capture_output=True, text=True, timeout=15
|
53 |
)
|
54 |
if compile_proc.returncode != 0:
|
55 |
return f"--- COMPILATION FAILED ---\n{compile_proc.stderr}"
|
56 |
-
|
57 |
run_proc = subprocess.run(
|
58 |
["./main.out"], capture_output=True, text=True, timeout=15
|
59 |
)
|
60 |
if run_proc.returncode != 0:
|
61 |
return f"--- RUNTIME ERROR ---\n{run_proc.stderr}"
|
62 |
-
|
63 |
output = run_proc.stdout
|
64 |
if not output.strip():
|
65 |
return "--- EXECUTION SUCCEEDED ---\n(No output was produced)"
|
66 |
return f"--- EXECUTION SUCCEEDED ---\n{output}"
|
67 |
-
|
68 |
-
except subprocess.TimeoutExpired:
|
69 |
-
return "--- ERROR ---\nProcess timed out. Check for infinite loops."
|
70 |
except Exception as e:
|
71 |
return f"--- SYSTEM ERROR ---\nAn unexpected error occurred: {str(e)}"
|
72 |
|
73 |
-
# --- 3.
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
86 |
lower_message = message.lower()
|
87 |
-
|
|
|
|
|
88 |
if "compile" in lower_message or "run" in lower_message or "실행" in lower_message:
|
89 |
code_to_run = ""
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
if "```c" in content_to_check:
|
94 |
-
code_to_run = content_to_check.split("```c")[1].split("```")[0].strip()
|
95 |
break
|
96 |
-
|
97 |
if not code_to_run:
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
yield f"코드를 컴파일하고 실행합니다...\n\n```c\n{code_to_run}\n```"
|
102 |
-
result = compile_and_run_c_code(code_to_run)
|
103 |
-
yield result
|
104 |
|
105 |
elif "generate" in lower_message or "create" in lower_message or "만들어줘" in lower_message or "짜줘" in lower_message:
|
106 |
-
yield "C 코드를 생성하는 중..."
|
107 |
generated_code = generate_c_code(message)
|
108 |
-
|
109 |
|
110 |
else:
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
gr.Markdown("# 🚀 C-Codestral Agent")
|
118 |
-
with gr.Tabs():
|
119 |
-
with gr.TabItem("🤖 Agent Chat"):
|
120 |
-
chatbot = gr.Chatbot(label="C-Agent", height=600, bubble_full_width=False, render_markdown=True)
|
121 |
-
msg = gr.Textbox(placeholder="Ask me to generate or run C code...", label="Your Request", scale=4)
|
122 |
-
|
123 |
-
def clear_and_submit(message, history):
|
124 |
-
# 메시지 전송 후 입력창을 비우는 효과를 위해 래퍼 함수 사용
|
125 |
-
yield from agent_chat(message, history)
|
126 |
|
127 |
-
|
128 |
-
msg.submit(lambda: "", None, msg) # 입력창 클리어
|
129 |
-
|
130 |
-
gr.Examples(
|
131 |
-
["Generate a C program to calculate the factorial of 10.", "run the code"],
|
132 |
-
inputs=msg
|
133 |
-
)
|
134 |
-
|
135 |
-
with gr.TabItem("🛠️ MCP Tools API"):
|
136 |
-
gr.Markdown("## Available MCP Tools\nThese APIs can be used by any MCP-compliant client.")
|
137 |
-
with gr.Accordion("Tool: Generate C Code", open=False):
|
138 |
-
# 최신 Gradio에서는 language='c'를 지원합니다.
|
139 |
-
gr.Interface(fn=generate_c_code, inputs="text", outputs=gr.Code(language="c"), allow_flagging="never")
|
140 |
-
with gr.Accordion("Tool: Compile & Run C Code", open=False):
|
141 |
-
gr.Interface(fn=compile_and_run_c_code, inputs=gr.Code(language="c"), outputs="text", allow_flagging="never")
|
142 |
-
|
143 |
-
if __name__ == "__main__":
|
144 |
-
demo.queue().launch()
|
|
|
1 |
# /app.py
|
2 |
|
|
|
3 |
import os
|
4 |
import subprocess
|
5 |
+
from fastapi import FastAPI, Request
|
6 |
+
from fastapi.responses import HTMLResponse
|
7 |
+
from pydantic import BaseModel
|
8 |
from mistralai.client import MistralClient
|
9 |
|
10 |
+
# --- 1. FastAPI 앱 및 Mistral 클라이언트 초기화 ---
|
11 |
+
app = FastAPI()
|
12 |
+
|
13 |
API_KEY = os.environ.get("MISTRAL_API_KEY")
|
14 |
CLIENT = None
|
15 |
if API_KEY:
|
16 |
try:
|
|
|
17 |
CLIENT = MistralClient(api_key=API_KEY)
|
18 |
print("Mistral API Client initialized successfully.")
|
19 |
except Exception as e:
|
|
|
21 |
else:
|
22 |
print("FATAL: MISTRAL_API_KEY is not set in Space Secrets.")
|
23 |
|
24 |
+
# --- 2. 백엔드 핵심 기능 함수 ---
|
25 |
def generate_c_code(description: str) -> str:
|
26 |
"""Generates C code based on a natural language description."""
|
27 |
if not CLIENT: return "Error: Mistral API key not configured."
|
28 |
+
prompt = f"You are a C programming expert. Generate a complete, compilable C code for this request: '{description}'. ONLY output the raw C code within a single ```c code block."
|
|
|
|
|
|
|
|
|
|
|
29 |
messages = [{"role": "user", "content": prompt}]
|
|
|
30 |
response = CLIENT.chat(model="codestral-latest", messages=messages)
|
31 |
code = response.choices[0].message.content
|
|
|
|
|
32 |
if "```c" in code:
|
33 |
code = code.split("```c")[1].split("```")[0].strip()
|
34 |
elif "```" in code:
|
35 |
code = code.split("```")[1].split("```")[0].strip()
|
|
|
36 |
return code
|
37 |
|
38 |
def compile_and_run_c_code(code: str) -> str:
|
|
|
40 |
try:
|
41 |
with open("main.c", "w", encoding='utf-8') as f:
|
42 |
f.write(code)
|
|
|
43 |
compile_proc = subprocess.run(
|
44 |
["gcc", "main.c", "-o", "main.out", "-lm", "-w"],
|
45 |
capture_output=True, text=True, timeout=15
|
46 |
)
|
47 |
if compile_proc.returncode != 0:
|
48 |
return f"--- COMPILATION FAILED ---\n{compile_proc.stderr}"
|
|
|
49 |
run_proc = subprocess.run(
|
50 |
["./main.out"], capture_output=True, text=True, timeout=15
|
51 |
)
|
52 |
if run_proc.returncode != 0:
|
53 |
return f"--- RUNTIME ERROR ---\n{run_proc.stderr}"
|
|
|
54 |
output = run_proc.stdout
|
55 |
if not output.strip():
|
56 |
return "--- EXECUTION SUCCEEDED ---\n(No output was produced)"
|
57 |
return f"--- EXECUTION SUCCEEDED ---\n{output}"
|
|
|
|
|
|
|
58 |
except Exception as e:
|
59 |
return f"--- SYSTEM ERROR ---\nAn unexpected error occurred: {str(e)}"
|
60 |
|
61 |
+
# --- 3. HTML, CSS, JavaScript 프론트엔드 ---
|
62 |
+
HTML_TEMPLATE = """
|
63 |
+
<!DOCTYPE html>
|
64 |
+
<html lang="en">
|
65 |
+
<head>
|
66 |
+
<meta charset="UTF-8">
|
67 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
68 |
+
<title>C-Codestral Agent</title>
|
69 |
+
<style>
|
70 |
+
body { font-family: sans-serif; background-color: #f5f5f5; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; }
|
71 |
+
#app-container { width: 100%; max-width: 800px; height: 90vh; background: white; border-radius: 8px; box-shadow: 0 0 20px rgba(0,0,0,0.1); display: flex; flex-direction: column; }
|
72 |
+
h1 { text-align: center; color: #333; padding: 20px; margin: 0; border-bottom: 1px solid #eee; }
|
73 |
+
#chat-box { flex-grow: 1; padding: 20px; overflow-y: auto; }
|
74 |
+
.message { margin-bottom: 15px; }
|
75 |
+
.user-msg { text-align: right; }
|
76 |
+
.bot-msg { text-align: left; }
|
77 |
+
.msg-bubble { display: inline-block; padding: 10px 15px; border-radius: 18px; max-width: 80%; }
|
78 |
+
.user-msg .msg-bubble { background-color: #007bff; color: white; }
|
79 |
+
.bot-msg .msg-bubble { background-color: #e9e9eb; color: black; }
|
80 |
+
pre { background-color: #2d2d2d; color: #f8f8f2; padding: 15px; border-radius: 5px; white-space: pre-wrap; word-wrap: break-word; }
|
81 |
+
#input-area { display: flex; padding: 20px; border-top: 1px solid #eee; }
|
82 |
+
#user-input { flex-grow: 1; padding: 10px; border: 1px solid #ccc; border-radius: 20px; outline: none; }
|
83 |
+
#send-btn { background: #007bff; color: white; border: none; padding: 10px 20px; margin-left: 10px; border-radius: 20px; cursor: pointer; }
|
84 |
+
</style>
|
85 |
+
</head>
|
86 |
+
<body>
|
87 |
+
<div id="app-container">
|
88 |
+
<h1>🚀 C-Codestral Agent</h1>
|
89 |
+
<div id="chat-box"></div>
|
90 |
+
<div id="input-area">
|
91 |
+
<input type="text" id="user-input" placeholder="Ask me to generate or run C code...">
|
92 |
+
<button id="send-btn">Send</button>
|
93 |
+
</div>
|
94 |
+
</div>
|
95 |
+
<script>
|
96 |
+
const chatBox = document.getElementById('chat-box');
|
97 |
+
const userInput = document.getElementById('user-input');
|
98 |
+
const sendBtn = document.getElementById('send-btn');
|
99 |
+
let chatHistory = [];
|
100 |
+
|
101 |
+
function addMessage(sender, text) {
|
102 |
+
const msgDiv = document.createElement('div');
|
103 |
+
msgDiv.classList.add('message', sender === 'user' ? 'user-msg' : 'bot-msg');
|
104 |
+
|
105 |
+
const bubble = document.createElement('div');
|
106 |
+
bubble.classList.add('msg-bubble');
|
107 |
+
|
108 |
+
// Convert ```c ... ``` to <pre><code>...</code>
|
109 |
+
if (text.includes('```')) {
|
110 |
+
const parts = text.split(/```c|```/);
|
111 |
+
parts.forEach((part, index) => {
|
112 |
+
if (index % 2 === 1) { // Code block
|
113 |
+
const pre = document.createElement('pre');
|
114 |
+
const code = document.createElement('code');
|
115 |
+
code.textContent = part.trim();
|
116 |
+
pre.appendChild(code);
|
117 |
+
bubble.appendChild(pre);
|
118 |
+
} else { // Regular text
|
119 |
+
const textNode = document.createTextNode(part);
|
120 |
+
bubble.appendChild(textNode);
|
121 |
+
}
|
122 |
+
});
|
123 |
+
} else {
|
124 |
+
bubble.textContent = text;
|
125 |
+
}
|
126 |
+
|
127 |
+
msgDiv.appendChild(bubble);
|
128 |
+
chatBox.appendChild(msgDiv);
|
129 |
+
chatBox.scrollTop = chatBox.scrollHeight;
|
130 |
+
}
|
131 |
+
|
132 |
+
async function sendMessage() {
|
133 |
+
const message = userInput.value.trim();
|
134 |
+
if (!message) return;
|
135 |
+
|
136 |
+
addMessage('user', message);
|
137 |
+
chatHistory.push({ "role": "user", "content": message });
|
138 |
+
userInput.value = '';
|
139 |
+
|
140 |
+
const response = await fetch('/chat', {
|
141 |
+
method: 'POST',
|
142 |
+
headers: { 'Content-Type': 'application/json' },
|
143 |
+
body: JSON.stringify({ message: message, history: chatHistory })
|
144 |
+
});
|
145 |
+
const data = await response.json();
|
146 |
+
const botResponse = data.response;
|
147 |
+
|
148 |
+
addMessage('bot', botResponse);
|
149 |
+
chatHistory.push({ "role": "assistant", "content": botResponse });
|
150 |
+
}
|
151 |
+
|
152 |
+
sendBtn.addEventListener('click', sendMessage);
|
153 |
+
userInput.addEventListener('keypress', (e) => {
|
154 |
+
if (e.key === 'Enter') sendMessage();
|
155 |
+
});
|
156 |
+
</script>
|
157 |
+
</body>
|
158 |
+
</html>
|
159 |
+
"""
|
160 |
+
|
161 |
+
# --- 4. FastAPI 엔드포인트 ---
|
162 |
+
class ChatRequest(BaseModel):
|
163 |
+
message: str
|
164 |
+
history: list
|
165 |
+
|
166 |
+
@app.get("/", response_class=HTMLResponse)
|
167 |
+
async def get_home():
|
168 |
+
return HTML_TEMPLATE
|
169 |
+
|
170 |
+
@app.post("/chat")
|
171 |
+
async def handle_chat(request: ChatRequest):
|
172 |
+
message = request.message
|
173 |
+
history = request.history
|
174 |
lower_message = message.lower()
|
175 |
+
|
176 |
+
response_text = ""
|
177 |
+
|
178 |
if "compile" in lower_message or "run" in lower_message or "실행" in lower_message:
|
179 |
code_to_run = ""
|
180 |
+
for item in reversed(history):
|
181 |
+
if item['role'] == 'assistant' and "```c" in item['content']:
|
182 |
+
code_to_run = item['content'].split("```c")[1].split("```")[0].strip()
|
|
|
|
|
183 |
break
|
|
|
184 |
if not code_to_run:
|
185 |
+
response_text = "컴파일할 코드를 찾을 수 없습니다. 먼저 코드를 생성해주세요."
|
186 |
+
else:
|
187 |
+
response_text = compile_and_run_c_code(code_to_run)
|
|
|
|
|
|
|
188 |
|
189 |
elif "generate" in lower_message or "create" in lower_message or "만들어줘" in lower_message or "짜줘" in lower_message:
|
|
|
190 |
generated_code = generate_c_code(message)
|
191 |
+
response_text = f"코드가 생성되었습니다. 'run this code'라고 입력하여 실행할 수 있습니다.\n\n```c\n{generated_code}\n```"
|
192 |
|
193 |
else:
|
194 |
+
if not CLIENT:
|
195 |
+
response_text = "MISTRAL_API_KEY가 설정되지 않았습니다."
|
196 |
+
else:
|
197 |
+
messages = history + [{"role": "user", "content": message}]
|
198 |
+
response = CLIENT.chat(model="codestral-latest", messages=messages)
|
199 |
+
response_text = response.choices[0].message.content
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
200 |
|
201 |
+
return {"response": response_text}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|