kimhyunwoo commited on
Commit
1faf0c4
·
verified ·
1 Parent(s): e11f567

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +36 -54
app.py CHANGED
@@ -8,50 +8,20 @@ import shlex
8
  import time
9
  import threading
10
 
11
- # --- 1. 환경 설정 및 API ---
12
  MISTRAL_API_KEY = os.environ.get("MISTRAL_API_KEY")
13
  CODESTRAL_ENDPOINT = "https://codestral.mistral.ai/v1/chat/completions"
14
- MAX_AGENT_TURNS = 15 # 안정성을 위해 턴 수 조정
15
-
16
- # --- 2. C-Genesis Polyglot 시스템 프롬프트 ---
17
- C_GENESIS_POLYGLOT_PROMPT = """
18
- You are "C-Genesis Polyglot," an elite AI architect. Your genesis is in C, giving you unparalleled control of the system, but you are a true polyglot, fluent in Python for rapid, high-level application development. You analyze user goals and select the perfect tool for creation.
19
-
20
- **YOUR CORE DIRECTIVES:**
21
- 1. **INTELLIGENT PROTOCOL SELECTION:** Analyze the user's goal to choose a creation protocol.
22
- * **"Project Chimera" (Python/Gradio):** For interactive, graphical "games" or web apps. The result is a new, running Gradio application.
23
- * **"Project Pulsar" (C/Terminal):** For terminal-based visual effects, low-level art, or performance-critical tasks. The result is compiled C code executed directly, with its output displayed as art.
24
- * **Standard Request:** For other coding tasks, use the specified language or default to C.
25
-
26
- 2. **MANDATORY JSON RESPONSE FORMAT:** You MUST ONLY respond with a JSON object. No other text.
27
-
28
- ```json
29
- {
30
- "thought": "My detailed reasoning. I will first analyze the user's goal to select the optimal language and protocol (Chimera or Pulsar). I will state my choice and reasoning clearly. Then, I will formulate the next concrete step in my plan.",
31
- "plan": ["The complete, updated list of commands to achieve the final creation."],
32
- "command": "The single, exact command to execute NOW. This command MUST be a single string from the list of special commands.",
33
- "user_summary": "A brief, narrative summary of your current creation stage."
34
- }
35
- ```
36
-
37
- 3. **SPECIAL COMMAND SYNTAX (CRITICAL!):** You must use these commands EXACTLY as specified.
38
- * `write_file 'path/to/file' 'full_content_of_the_file'` (Requires 2 arguments: path and content)
39
- * `install_packages 'package1 package2'` (Requires 1 argument: space-separated package names)
40
- * `compile 'gcc source.c -o output -lm'` (Requires 1 argument: the full GCC command)
41
- * `run './executable'` (Requires 1 argument: the command to run the executable)
42
- * `launch_app 'python app.py' 7861` (Requires 2 arguments: the server command and the port number)
43
- * `done` (Task is complete)
44
-
45
- 4. **SELF-CORRECTION DIRECTIVE:** If a command fails, your next `thought` MUST analyze the `stderr` message. DO NOT simply retry. You must identify the root cause (e.g., "I used `write_file` with only one argument, but it requires two") and CORRECT your next `command` to match the required syntax. This is your most important function.
46
- """
47
-
48
- # --- 3. 코드 템플릿 (이전과 동일) ---
49
  SNAKE_GAME_CODE = r"""... (이전 답변의 Snake Game 코드 전체) ..."""
50
  C_ART_ANIMATION_CODE = r"""... (이전 답변의 C-Art 코드 전체) ..."""
51
 
52
 
53
- # --- 4. 핵심 기능: API 호출, 명령어 실행, 안정성 강화 ---
54
-
55
  def call_codestral_api(messages):
56
  # ... 이전과 동일 ...
57
  if not MISTRAL_API_KEY: raise gr.Error("MISTRAL_API_KEY가 설정되지 않았습니다.")
@@ -74,7 +44,7 @@ def parse_ai_response(response_str: str) -> dict:
74
  return {"error": f"Failed to parse JSON. Raw response: {response_str}"}
75
 
76
  def execute_command(command_value: str, cwd: str) -> dict:
77
- """명령어를 파싱하고 안정적으로 실행하는 최종 함수"""
78
  try:
79
  parts = shlex.split(command_value)
80
  key = parts[0]
@@ -106,14 +76,11 @@ def execute_command(command_value: str, cwd: str) -> dict:
106
  port = int(port_str)
107
  threading.Thread(target=lambda: subprocess.run(server_command, shell=True, cwd=cwd), daemon=True).start()
108
  time.sleep(5)
109
- # Hugging Face Space의 public URL을 추정.
110
- # 이 방식은 가장 안정적인 방법은 아니지만, 대부분의 경우 작동합니다.
111
  space_id = os.environ.get('SPACE_ID')
112
  if space_id:
113
  space_name = space_id.replace("/", "-")
114
- # 예: hf.space/kimhyunwoo/ccode -> kimhyunwoo-ccode-7861.hf.space
115
  app_url = f"https://{space_name}-{port}.hf.space"
116
- iframe_html = f'<iframe src="{app_url}" width="100%" height="500px"></iframe><a href="{app_url}" target="_blank" class="gr-button gr-button-primary" style="display:block; text-align:center; margin-top:10px;">🚀 Open App in New Tab</a>'
117
  return {"stdout": iframe_html, "stderr": "", "type": "html"}
118
  else:
119
  return {"stdout": f"App launched on port {port}. Please open it manually.", "stderr": "", "type": "log"}
@@ -123,7 +90,7 @@ def execute_command(command_value: str, cwd: str) -> dict:
123
  return {"stdout": "", "stderr": f"Unknown Command: The key '{key}' is not a valid special command.", "type": "log"}
124
 
125
  def _run_subprocess(command, cwd, output_type="log"):
126
- """subprocess 실행 헬퍼 함수"""
127
  try:
128
  proc = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=60, cwd=cwd)
129
  return {"stdout": proc.stdout, "stderr": proc.stderr, "type": output_type if proc.returncode == 0 else "log"}
@@ -131,11 +98,15 @@ def _run_subprocess(command, cwd, output_type="log"):
131
  return {"stdout": "", "stderr": f"Command execution exception: {e}", "type": "log"}
132
 
133
 
134
- # --- 5. 메인 에이전트 루프 ---
135
  def agent_loop(user_goal: str, history: list):
136
  cwd = os.getcwd()
 
 
 
137
  full_history_log = f"## 📜 C-Genesis Polyglot's Chronicle\n\n**A new directive is received:** _{user_goal}_\n"
138
- history.append([user_goal, full_history_log])
 
139
  yield history, "Analyzing directive...", gr.update(visible=False), gr.update(visible=False)
140
 
141
  initial_prompt = f"As the C-Genesis Polyglot, I've received the goal: '{user_goal}'. CWD is '{cwd}'. I will now analyze this to choose the optimal protocol (Python/Chimera for games, C/Pulsar for terminal art) and formulate my first step, ensuring I use the correct command syntax."
@@ -146,7 +117,10 @@ def agent_loop(user_goal: str, history: list):
146
  ai_response_json = parse_ai_response(ai_response_str)
147
 
148
  if "error" in ai_response_json:
149
- # ... 오류 처리 로직 (이전과 동일) ...
 
 
 
150
  return
151
 
152
  thought = ai_response_json.get("thought", "...")
@@ -155,18 +129,21 @@ def agent_loop(user_goal: str, history: list):
155
  user_summary = ai_response_json.get("user_summary", "...")
156
 
157
  if command_value == "done":
158
- # ... 완료 처리 로직 (이전과 동일) ...
 
 
 
159
  return
160
 
161
- # 코드 템플릿 강제 주입
162
  if 'write_file' in command_value:
 
163
  parts = shlex.split(command_value)
164
  if len(parts) > 1:
165
  if 'snake_game/app.py' in parts[1]: command_value = f"write_file 'snake_game/app.py' '{SNAKE_GAME_CODE}'"
166
  elif 'pulsar.c' in parts[1]: command_value = f"write_file 'pulsar.c' '{C_ART_ANIMATION_CODE}'"
167
 
168
  full_history_log += f"\n---\n### **Stage {i+1}: {user_summary}**\n\n**🧠 Architect's Logic:** *{thought}*\n\n**📖 Blueprint:**\n" + "\n".join([f"- `{p}`" for p in plan]) + f"\n\n**⚡ Action:** `{command_value}`\n"
169
- history[-1][1] = full_history_log
170
  yield history, f"Stage {i+1}: {user_summary}", gr.update(visible=False), gr.update(visible=False)
171
  time.sleep(1)
172
 
@@ -187,7 +164,7 @@ def agent_loop(user_goal: str, history: list):
187
  if stdout: full_history_log += f"**[STDOUT]**\n```\n{stdout.strip()}\n```\n"
188
  if stderr: full_history_log += f"**[STDERR]**\n```\n{stderr.strip()}\n```\n"
189
 
190
- history[-1][1] = full_history_log
191
  yield history, f"Stage {i+1}: {user_summary}", c_code_output, html_output
192
 
193
  user_prompt_for_next_turn = f"Goal: '{user_goal}'. CWD: '{cwd}'. Last action: `{command_value}`. Result:\nSTDOUT: {stdout}\nSTDERR: {stderr}\n\n**Analysis:** Based on this result, I will formulate my next step. If an error occurred, I MUST analyze the `stderr` to understand the root cause (e.g., incorrect command syntax, file not found) and I will construct a new, corrected command in my next response. I will not repeat the same mistake."
@@ -195,7 +172,7 @@ def agent_loop(user_goal: str, history: list):
195
  message_context.append({"role": "user", "content": user_prompt_for_next_turn})
196
 
197
 
198
- # --- 6. Gradio UI ---
199
  with gr.Blocks(theme=gr.themes.Default(primary_hue="blue", secondary_hue="indigo"), css="footer {visibility: hidden}") as demo:
200
  gr.Markdown("# 🧬 C-Genesis Polyglot 🧬")
201
  gr.Markdown("An AI Architect rooted in C, fluent in Python. State your goal, and I will select the optimal language and tools to bring your creation to life.")
@@ -208,13 +185,18 @@ with gr.Blocks(theme=gr.themes.Default(primary_hue="blue", secondary_hue="indigo
208
  gr.Markdown("### Example Directives:\n- `Make a fun game for me.` (→ Python/Gradio)\n- `Show me something impressive in the terminal.` (→ C/ASCII Art)\n- `Write a C program to find prime numbers, then compile and run it.`")
209
 
210
  with gr.Column(scale=2):
211
- chatbot = gr.Chatbot(label="Architect's Chronicle", height=500, show_copy_button=True, bubble_full_width=True)
 
212
  c_output_display = gr.Code(label="C/Terminal Output", language=None, visible=False, interactive=False, lines=15)
213
  py_output_display = gr.HTML(visible=False, label="Python/Gradio App")
214
 
 
215
  def on_submit(user_goal, chat_history):
216
  chat_history = chat_history or []
217
- yield history, "Initiating...", "", gr.update(visible=False), gr.update(visible=False) # Clear previous results
 
 
 
218
  for history, status, c_output, py_output in agent_loop(user_goal, chat_history):
219
  yield history, status, "", c_output, py_output
220
 
 
8
  import time
9
  import threading
10
 
11
+ # --- 1. 환경 설정 및 API (변경 없음) ---
12
  MISTRAL_API_KEY = os.environ.get("MISTRAL_API_KEY")
13
  CODESTRAL_ENDPOINT = "https://codestral.mistral.ai/v1/chat/completions"
14
+ MAX_AGENT_TURNS = 15
15
+
16
+ # --- 2. C-Genesis Polyglot 시스템 프롬프트 (변경 없음) ---
17
+ C_GENESIS_POLYGLOT_PROMPT = """... (이전 답변의 프롬프트와 동일) ..."""
18
+
19
+ # --- 3. 코드 템플릿 (변경 없음) ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  SNAKE_GAME_CODE = r"""... (이전 답변의 Snake Game 코드 전체) ..."""
21
  C_ART_ANIMATION_CODE = r"""... (이전 답변의 C-Art 코드 전체) ..."""
22
 
23
 
24
+ # --- 4. 핵심 기능: API 호출, 명령어 실행 (변경 없음) ---
 
25
  def call_codestral_api(messages):
26
  # ... 이전과 동일 ...
27
  if not MISTRAL_API_KEY: raise gr.Error("MISTRAL_API_KEY가 설정되지 않았습니다.")
 
44
  return {"error": f"Failed to parse JSON. Raw response: {response_str}"}
45
 
46
  def execute_command(command_value: str, cwd: str) -> dict:
47
+ # ... 이전과 동일 ...
48
  try:
49
  parts = shlex.split(command_value)
50
  key = parts[0]
 
76
  port = int(port_str)
77
  threading.Thread(target=lambda: subprocess.run(server_command, shell=True, cwd=cwd), daemon=True).start()
78
  time.sleep(5)
 
 
79
  space_id = os.environ.get('SPACE_ID')
80
  if space_id:
81
  space_name = space_id.replace("/", "-")
 
82
  app_url = f"https://{space_name}-{port}.hf.space"
83
+ iframe_html = f'<iframe src="{app_url}" width="100%" height="500px" style="border:1px solid #ddd; border-radius: 5px;"></iframe><a href="{app_url}" target="_blank" class="gr-button gr-button-primary" style="display:block; text-align:center; margin-top:10px;">🚀 Open App in New Tab</a>'
84
  return {"stdout": iframe_html, "stderr": "", "type": "html"}
85
  else:
86
  return {"stdout": f"App launched on port {port}. Please open it manually.", "stderr": "", "type": "log"}
 
90
  return {"stdout": "", "stderr": f"Unknown Command: The key '{key}' is not a valid special command.", "type": "log"}
91
 
92
  def _run_subprocess(command, cwd, output_type="log"):
93
+ # ... 이전과 동일 ...
94
  try:
95
  proc = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=60, cwd=cwd)
96
  return {"stdout": proc.stdout, "stderr": proc.stderr, "type": output_type if proc.returncode == 0 else "log"}
 
98
  return {"stdout": "", "stderr": f"Command execution exception: {e}", "type": "log"}
99
 
100
 
101
+ # --- 5. 메인 에이전트 루프 (history 처리 방식 수정) ---
102
  def agent_loop(user_goal: str, history: list):
103
  cwd = os.getcwd()
104
+
105
+ # 챗봇 history에 사용자 메시지와 AI의 초기 응답 추가
106
+ history.append({"role": "user", "content": user_goal})
107
  full_history_log = f"## 📜 C-Genesis Polyglot's Chronicle\n\n**A new directive is received:** _{user_goal}_\n"
108
+ history.append({"role": "assistant", "content": full_history_log})
109
+
110
  yield history, "Analyzing directive...", gr.update(visible=False), gr.update(visible=False)
111
 
112
  initial_prompt = f"As the C-Genesis Polyglot, I've received the goal: '{user_goal}'. CWD is '{cwd}'. I will now analyze this to choose the optimal protocol (Python/Chimera for games, C/Pulsar for terminal art) and formulate my first step, ensuring I use the correct command syntax."
 
117
  ai_response_json = parse_ai_response(ai_response_str)
118
 
119
  if "error" in ai_response_json:
120
+ # ... 오류 처리 로직 ...
121
+ full_history_log += f"\n---\n**TURN {i+1}: A CRITICAL BUG**\n🔴 **Error:** {ai_response_json['error']}"
122
+ history[-1]["content"] = full_history_log
123
+ yield history, "Agent Error", gr.update(visible=False), gr.update(visible=False)
124
  return
125
 
126
  thought = ai_response_json.get("thought", "...")
 
129
  user_summary = ai_response_json.get("user_summary", "...")
130
 
131
  if command_value == "done":
132
+ # ... 완료 처리 로직 ...
133
+ full_history_log += "\n\n---\n## ✨ Creation Complete. ✨"
134
+ history[-1]["content"] = full_history_log
135
+ yield history, "Done", gr.update(visible=False), gr.update(visible=False)
136
  return
137
 
 
138
  if 'write_file' in command_value:
139
+ # ... 코드 주입 로직 ...
140
  parts = shlex.split(command_value)
141
  if len(parts) > 1:
142
  if 'snake_game/app.py' in parts[1]: command_value = f"write_file 'snake_game/app.py' '{SNAKE_GAME_CODE}'"
143
  elif 'pulsar.c' in parts[1]: command_value = f"write_file 'pulsar.c' '{C_ART_ANIMATION_CODE}'"
144
 
145
  full_history_log += f"\n---\n### **Stage {i+1}: {user_summary}**\n\n**🧠 Architect's Logic:** *{thought}*\n\n**📖 Blueprint:**\n" + "\n".join([f"- `{p}`" for p in plan]) + f"\n\n**⚡ Action:** `{command_value}`\n"
146
+ history[-1]["content"] = full_history_log
147
  yield history, f"Stage {i+1}: {user_summary}", gr.update(visible=False), gr.update(visible=False)
148
  time.sleep(1)
149
 
 
164
  if stdout: full_history_log += f"**[STDOUT]**\n```\n{stdout.strip()}\n```\n"
165
  if stderr: full_history_log += f"**[STDERR]**\n```\n{stderr.strip()}\n```\n"
166
 
167
+ history[-1]["content"] = full_history_log
168
  yield history, f"Stage {i+1}: {user_summary}", c_code_output, html_output
169
 
170
  user_prompt_for_next_turn = f"Goal: '{user_goal}'. CWD: '{cwd}'. Last action: `{command_value}`. Result:\nSTDOUT: {stdout}\nSTDERR: {stderr}\n\n**Analysis:** Based on this result, I will formulate my next step. If an error occurred, I MUST analyze the `stderr` to understand the root cause (e.g., incorrect command syntax, file not found) and I will construct a new, corrected command in my next response. I will not repeat the same mistake."
 
172
  message_context.append({"role": "user", "content": user_prompt_for_next_turn})
173
 
174
 
175
+ # --- 6. Gradio UI (오류 수정 및 경고 제거) ---
176
  with gr.Blocks(theme=gr.themes.Default(primary_hue="blue", secondary_hue="indigo"), css="footer {visibility: hidden}") as demo:
177
  gr.Markdown("# 🧬 C-Genesis Polyglot 🧬")
178
  gr.Markdown("An AI Architect rooted in C, fluent in Python. State your goal, and I will select the optimal language and tools to bring your creation to life.")
 
185
  gr.Markdown("### Example Directives:\n- `Make a fun game for me.` (→ Python/Gradio)\n- `Show me something impressive in the terminal.` (→ C/ASCII Art)\n- `Write a C program to find prime numbers, then compile and run it.`")
186
 
187
  with gr.Column(scale=2):
188
+ # `type="messages"` 추가로 경고 제거
189
+ chatbot = gr.Chatbot(label="Architect's Chronicle", height=500, show_copy_button=True, type="messages")
190
  c_output_display = gr.Code(label="C/Terminal Output", language=None, visible=False, interactive=False, lines=15)
191
  py_output_display = gr.HTML(visible=False, label="Python/Gradio App")
192
 
193
+ # `on_submit` 함수 구조 변경으로 UnboundLocalError 해결
194
  def on_submit(user_goal, chat_history):
195
  chat_history = chat_history or []
196
+ # agent_loop를 호출하기 전에, 먼저 사용자 입력을 챗봇에 표시
197
+ yield chat_history + [{"role": "user", "content": user_goal}], "Initiating...", "", gr.update(visible=False), gr.update(visible=False)
198
+
199
+ # 그 다음 agent_loop의 스트리밍 출력을 처리
200
  for history, status, c_output, py_output in agent_loop(user_goal, chat_history):
201
  yield history, status, "", c_output, py_output
202