kimhyunwoo commited on
Commit
6a21a24
·
verified ·
1 Parent(s): 07d3ed8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +138 -81
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. 핵심 기능 함수 (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 = [{"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. Gradio 챗봇 로직 (Agent 역할) ---
74
- def agent_chat(message, history):
75
- if not CLIENT:
76
- yield "MISTRAL_API_KEY를 Space Secrets에 설정해주세요."
77
- return
78
-
79
- history_messages = []
80
- for human, ai in history:
81
- history_messages.append({"role": "user", "content": human})
82
- history_messages.append({"role": "assistant", "content": ai})
83
-
84
- history_messages.append({"role": "user", "content": message})
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
- # 대화 기록에서 마지막 C 코드 블록 찾기
91
- for h_user, h_ai in reversed(history + [[message, ""]]):
92
- content_to_check = h_ai or h_user
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
- yield "컴파일할 코드를 찾을 수 없습니다. 먼저 코드를 생성하거나, 메시지에 코드를 포함시켜 주세요."
99
- return
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
- yield f"코드가 생성되었습니다. 'run this code'라고 입력���여 실행할 수 있습니다.\n\n```c\n{generated_code}\n```"
109
 
110
  else:
111
- # 일반적인 질문은 LLM에게 직접 전달
112
- response = CLIENT.chat(model="codestral-latest", messages=history_messages)
113
- yield response.choices[0].message.content
114
-
115
- # --- 4. Gradio UI 구성 ---
116
- with gr.Blocks(theme=gr.themes.Soft(), css=".gradio-container{max-width: 800px !important; margin: auto;}") as demo:
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
- msg.submit(clear_and_submit, [msg, chatbot], chatbot)
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}