kimhyunwoo commited on
Commit
9ebbf81
·
verified ·
1 Parent(s): 9e50a0d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +91 -86
app.py CHANGED
@@ -3,139 +3,144 @@
3
  import gradio as gr
4
  import os
5
  import subprocess
6
- from langchain_mistralai.chat_models import ChatMistralAI
7
- from langchain_core.messages import HumanMessage, AIMessage
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. 환경 설정 및 LLM 초기화 ---
13
- API_KEY = os.environ.get("MISTRAL_API_KEY", None)
14
- llm = None
15
  if API_KEY:
16
  try:
17
- llm = ChatMistralAI(model="codestral-latest", temperature=0, api_key=API_KEY, streaming=True)
 
18
  except Exception as e:
19
- print(f"Failed to initialize LLM: {e}")
20
  else:
21
  print("FATAL: MISTRAL_API_KEY is not set in Space Secrets.")
22
 
23
- # --- 2. 핵심 도구(Tools) 정의 ---
24
- # LangChain 에이전트가 내부적으로 사용할 @tool 데코레이터
25
- @tool
26
  def generate_c_code(description: str) -> str:
27
- """Generates complete, compilable C code based on a natural language description. Use this when the user wants to create new code."""
28
- if not llm: return "Error: LLM not initialized."
29
- code_generation_prompt = f"""You are a world-class C programming expert.
30
- Generate a single, complete, and compilable C code file based on this request: '{description}'.
31
- The code must be clean, efficient, and include necessary headers.
32
- ONLY output the raw C code within a single C code block. No explanations, no extra text."""
33
- response = llm.invoke([HumanMessage(content=code_generation_prompt)])
34
- code = response.content
 
 
 
 
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. Use this to verify or test code."""
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
- @tool
66
- def refactor_or_analyze_c_code(code: str, request: str) -> str:
67
- """Analyzes, refactors, or explains a given C code based on a user's specific request. Use this for code improvement or understanding."""
68
- if not llm: return "Error: LLM not initialized."
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
- chat_history = []
 
 
103
  for human, ai in history:
104
- chat_history.append(HumanMessage(content=human))
105
- chat_history.append(AIMessage(content=ai))
106
- response_stream = agent_executor.stream({"input": user_input, "chat_history": chat_history})
107
- full_response = ""
108
- for chunk in response_stream:
109
- if "output" in chunk:
110
- full_response += chunk["output"]
111
- yield full_response
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
- # --- 5. Gradio UI/UX 디자인 ---
114
- with gr.Blocks(theme=gr.themes.Monochrome(), css=".gradio-container{max-width: 800px !important; margin: auto;}") as demo:
115
  gr.Markdown("# 🚀 C-Codestral Agent")
116
  with gr.Tabs():
 
117
  with gr.TabItem("🤖 Agent Chat"):
118
- chatbot = gr.Chatbot(label="C-Agent", bubble_full_width=False, height=600, avatar_images=(None, "https://huggingface.co/datasets/huggingface/brand-assets/resolve/main/hf-logo-with-title.png"))
119
- with gr.Row():
120
- txt = gr.Textbox(show_label=False, placeholder="Enter your request and press enter...", container=False, scale=7)
121
- submit_btn = gr.Button("Submit", variant="primary", scale=1)
122
- txt.submit(agent_chat, [txt, chatbot], chatbot)
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 a number.", "Now, compile and run the code you just created.", "Refactor the factorial code to use recursion instead of a loop."],
128
- inputs=txt, label="Example Prompts"
129
  )
130
- # MCP 도구들을 별도의 탭에 API로 노출
 
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
- generate_api.render()
135
  with gr.Accordion("Tool: Compile & Run C Code", open=False):
136
- compile_api.render()
137
- with gr.Accordion("Tool: Refactor & Analyze C Code", open=False):
138
- refactor_api.render()
139
 
140
  if __name__ == "__main__":
141
- demo.queue().launch(debug=True)
 
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()