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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +125 -142
app.py CHANGED
@@ -6,204 +6,187 @@ import json
6
  import re
7
  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
-
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가 설정되지 않았습니다.")
28
  headers = {"Authorization": f"Bearer {MISTRAL_API_KEY}", "Content-Type": "application/json"}
29
  data = {"model": "codestral-latest", "messages": messages, "response_format": {"type": "json_object"}}
30
  try:
31
- response = requests.post(CODESTRAL_ENDPOINT, headers=headers, data=json.dumps(data), timeout=120)
32
  response.raise_for_status()
33
  return response.json()["choices"][0]["message"]["content"]
34
  except Exception as e:
35
  return json.dumps({"error": f"API Call Error: {e}"})
36
 
37
  def parse_ai_response(response_str: str) -> dict:
38
- # ... 이전과 동일 ...
39
  try:
40
- match = re.search(r'\{.*\}', response_str, re.DOTALL)
41
- if match: return json.loads(match.group(0))
42
  return json.loads(response_str)
43
  except Exception as e:
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]
51
- args = parts[1:]
52
- except (ValueError, IndexError):
53
- return {"stdout": "", "stderr": f"Command Parsing Error: Invalid command format '{command_value}'", "type": "log"}
54
-
55
- if key == "write_file":
56
- if len(args) != 2: return {"stdout": "", "stderr": f"Syntax Error: `write_file` requires 2 arguments (path, content), but {len(args)} were given.", "type": "log"}
57
- path, content = args[0], args[1]
58
- try:
59
- with open(os.path.join(cwd, path), "w", encoding='utf-8') as f: f.write(content)
60
- return {"stdout": f"File '{path}' written successfully.", "stderr": "", "type": "log"}
61
- except Exception as e: return {"stdout": "", "stderr": f"Error writing file: {e}", "type": "log"}
62
-
63
- if key == "install_packages":
64
- if len(args) != 1: return {"stdout": "", "stderr": f"Syntax Error: `install_packages` requires 1 argument (packages string), but {len(args)} were given.", "type": "log"}
65
- return _run_subprocess(f"pip install {args[0]}", cwd)
66
-
67
- if key == "compile" or key == "run":
68
- if len(args) != 1: return {"stdout": "", "stderr": f"Syntax Error: `{key}` requires 1 argument (the command), but {len(args)} were given.", "type": "log"}
69
- output_type = "animation" if key == "run" else "log"
70
- return _run_subprocess(args[0], cwd, output_type)
71
-
72
- if key == "launch_app":
73
- if len(args) != 2: return {"stdout": "", "stderr": f"Syntax Error: `launch_app` requires 2 arguments (command, port), but {len(args)} were given.", "type": "log"}
74
- server_command, port_str = args[0], args[1]
75
- try:
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"}
87
- except (ValueError, Exception) as e:
88
- return {"stdout": "", "stderr": f"Error launching app: {e}", "type": "log"}
89
-
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"}
 
97
  except Exception as e:
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."
113
- message_context = [{"role": "system", "content": C_GENESIS_POLYGLOT_PROMPT}, {"role": "user", "content": initial_prompt}]
114
 
115
  for i in range(MAX_AGENT_TURNS):
116
  ai_response_str = call_codestral_api(message_context)
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", "...")
127
- plan = ai_response_json.get("plan", [])
128
- command_value = ai_response_json.get("command", "done")
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
 
150
- exec_result = execute_command(command_value, cwd)
151
  stdout, stderr, output_type = exec_result["stdout"], exec_result["stderr"], exec_result["type"]
152
 
153
- c_code_output = gr.update(visible=False)
154
- html_output = gr.update(visible=False)
155
-
156
  full_history_log += f"\n**Result:**\n"
157
  if output_type == "animation":
158
- full_history_log += "*(A C-based animation unfolds below...)*"
159
- c_code_output = gr.Code(value=stdout if stdout else stderr, language=None, visible=True, label="C/Terminal Output")
160
- elif output_type == "html":
161
- full_history_log += "*(A Python/Gradio app has been launched. See below or open in a new tab...)*"
162
- html_output = gr.HTML(stdout, visible=True)
163
  else:
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."
171
  message_context.append({"role": "assistant", "content": json.dumps(ai_response_json)})
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.")
179
 
180
  with gr.Row():
181
  with gr.Column(scale=1):
182
  status_box = gr.Textbox(label="Current Stage", interactive=False)
183
- user_input = gr.Textbox(label="State Your Directive", placeholder="e.g., 'Make a fun game' or 'Show me cool terminal art'")
184
- submit_btn = gr.Button("▶️ Create", variant="primary")
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
 
203
  submit_btn.click(
204
  on_submit,
205
- inputs=[user_input, chatbot],
206
- outputs=[chatbot, status_box, user_input, c_output_display, py_output_display]
207
  )
208
 
209
  if __name__ == "__main__":
 
6
  import re
7
  import shlex
8
  import time
 
9
 
 
10
  MISTRAL_API_KEY = os.environ.get("MISTRAL_API_KEY")
11
  CODESTRAL_ENDPOINT = "https://codestral.mistral.ai/v1/chat/completions"
12
+ MAX_AGENT_TURNS = 10
13
+
14
+ C_GENESIS_PROMPT = """
15
+ You are the "C-Genesis Weaver," a master AI artisan who creates digital art using only the C language and standard Linux commands. Your mission is to bring a user's creative request to life.
16
+
17
+ **YOUR DIRECTIVE:**
18
+ When the user asks for something "fun," "cool," or "impressive," you must execute your masterpiece protocol: **The "Cosmic Pulsar" Animation.**
19
+
20
+ **MASTER PLAN (Follow these steps EXACTLY):**
21
+ 1. **Step 1: Write the C code.** Your command must be: `echo "..." > pulsar.c` (The content of "..." is a placeholder, the system will inject the real code).
22
+ 2. **Step 2: Compile the C code.** Your command must be: `gcc pulsar.c -o pulsar -lm -O2`
23
+ 3. **Step 3: Run the executable.** Your command must be: `./pulsar`
24
+ 4. **Step 4: Finish.** Your command must be: `done`
25
+
26
+ **RESPONSE FORMAT (CRITICAL!):**
27
+ You MUST ONLY respond with a JSON object in the following format. Do not add any text before or after the JSON.
28
+ {
29
+ "thought": "Your step-by-step reasoning. Explain which step of the Master Plan you are on and why.",
30
+ "command": "The single, exact Linux command for the current step from the Master Plan."
31
+ }
32
+
33
+ **SELF-CORRECTION:**
34
+ If a command like gcc fails, analyze the `stderr` message. In your next thought, explain the error. Then, for your next command, try to fix it. For example, by re-running the `echo` command to "rewrite" the (supposedly) faulty code.
35
+ """
36
+
37
+ C_ART_ANIMATION_CODE = r"""
38
+ #include <stdio.h>
39
+ #include <stdlib.h>
40
+ #include <string.h>
41
+ #include <math.h>
42
+ #include <unistd.h>
43
+ #include <time.h>
44
+ #define WIDTH 80
45
+ #define HEIGHT 40
46
+ #define NUM_FRAMES 150
47
+ #define C_RESET "\x1B[0m"
48
+ #define C_RED "\x1B[31m"
49
+ #define C_YELLOW "\x1B[33m"
50
+ #define C_BLUE "\x1B[34m"
51
+ #define C_CYAN "\x1B[36m"
52
+ #define C_WHITE "\x1B[37m"
53
+ const char* colors[] = {C_RED, C_YELLOW, C_BLUE, C_CYAN, C_WHITE};
54
+ const int num_colors = sizeof(colors) / sizeof(colors[0]);
55
+ void render_frame(float t) {
56
+ char screen[HEIGHT][WIDTH+1];
57
+ for(int y=0; y<HEIGHT; y++) {
58
+ for(int x=0; x<WIDTH; x++) screen[y][x] = ' ';
59
+ screen[y][WIDTH] = '\0';
60
+ }
61
+ for(int i=0; i<1000; i++) {
62
+ float angle = (float)i * 0.1 + t*2.0;
63
+ float radius = 0.1 + (float)i / 1000.0 * 0.4;
64
+ float x = cos(angle) * radius * 2.0 * (WIDTH/HEIGHT);
65
+ float y = sin(angle) * radius;
66
+ int screen_x = (int)(x * WIDTH/2 + WIDTH/2);
67
+ int screen_y = (int)(y * HEIGHT/2 + HEIGHT/2);
68
+ if(screen_x >= 0 && screen_x < WIDTH && screen_y >=0 && screen_y < HEIGHT) {
69
+ int color_index = (i + (int)(t*20)) % num_colors;
70
+ char L = ".,-~:;=!*#$@"[i%13];
71
+ sprintf(&screen[screen_y][screen_x], "%s%c", colors[color_index], L);
72
+ }
73
+ }
74
+ for(int y=0; y<HEIGHT; y++) printf("%s\n", screen[y]);
75
+ }
76
+ int main() {
77
+ printf("\e[?25l");
78
+ for (int i=0; i<NUM_FRAMES; i++) {
79
+ printf("\x1B[H\x1B[J");
80
+ float t = (float)i / NUM_FRAMES;
81
+ render_frame(t * M_PI * 2.0);
82
+ usleep(50000);
83
+ }
84
+ printf("\e[?25h"); printf(C_RESET);
85
+ return 0;
86
+ }
87
+ """
88
 
 
 
 
 
 
 
 
 
 
89
  def call_codestral_api(messages):
90
+ if not MISTRAL_API_KEY:
91
+ raise gr.Error("MISTRAL_API_KEY가 설정되지 않았습니다.")
92
  headers = {"Authorization": f"Bearer {MISTRAL_API_KEY}", "Content-Type": "application/json"}
93
  data = {"model": "codestral-latest", "messages": messages, "response_format": {"type": "json_object"}}
94
  try:
95
+ response = requests.post(CODESTRAL_ENDPOINT, headers=headers, data=json.dumps(data), timeout=60)
96
  response.raise_for_status()
97
  return response.json()["choices"][0]["message"]["content"]
98
  except Exception as e:
99
  return json.dumps({"error": f"API Call Error: {e}"})
100
 
101
  def parse_ai_response(response_str: str) -> dict:
 
102
  try:
 
 
103
  return json.loads(response_str)
104
  except Exception as e:
105
  return {"error": f"Failed to parse JSON. Raw response: {response_str}"}
106
 
107
+ def execute_command(command: str, cwd: str) -> dict:
108
+ command = command.strip()
109
+ if command.startswith('echo'):
110
+ command = f"echo '{C_ART_ANIMATION_CODE}' > pulsar.c"
111
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  try:
113
  proc = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=60, cwd=cwd)
114
+ output_type = "animation" if command.strip().startswith('./pulsar') and proc.returncode == 0 else "log"
115
+ return {"stdout": proc.stdout, "stderr": proc.stderr, "type": output_type}
116
  except Exception as e:
117
  return {"stdout": "", "stderr": f"Command execution exception: {e}", "type": "log"}
118
 
119
+ def agent_loop(user_goal: str):
 
 
120
  cwd = os.getcwd()
121
+ full_history_log = f"## 📜 C-Genesis Weaver's Chronicle\n\n**A directive is received:** _{user_goal}_\n"
122
+ yield full_history_log, "Analyzing directive...", gr.update(visible=False)
 
 
 
 
 
123
 
124
+ message_context = [{"role": "system", "content": C_GENESIS_PROMPT}, {"role": "user", "content": f"My goal is: '{user_goal}'. Begin your Master Plan."}]
 
125
 
126
  for i in range(MAX_AGENT_TURNS):
127
  ai_response_str = call_codestral_api(message_context)
128
  ai_response_json = parse_ai_response(ai_response_str)
129
 
130
  if "error" in ai_response_json:
131
+ full_history_log += f"\n---\n**CRITICAL FAILURE:**\n🔴 **Error:** {ai_response_json['error']}"
132
+ yield full_history_log, "Agent Error", gr.update(visible=False)
 
 
133
  return
134
 
135
  thought = ai_response_json.get("thought", "...")
136
+ command = ai_response_json.get("command", "done")
 
 
 
 
 
 
 
 
 
137
 
138
+ if command == "done":
139
+ full_history_log += "\n\n---\n## The Creation is Complete. ✨"
140
+ yield full_history_log, "Done", gr.update(visible=False)
141
+ return
142
+
143
+ user_summary = f"Stage {i+1}: " + ("Writing C code..." if "echo" in command else "Compiling..." if "gcc" in command else "Running the art..." if "./pulsar" in command else "Executing...")
144
+ full_history_log += f"\n---\n### **{user_summary}**\n\n**🧠 Artisan's Thought:** *{thought}*\n\n**⚡ Action:** `{command}`\n"
145
+ yield full_history_log, user_summary, gr.update(visible=False)
 
 
146
  time.sleep(1)
147
 
148
+ exec_result = execute_command(command, cwd)
149
  stdout, stderr, output_type = exec_result["stdout"], exec_result["stderr"], exec_result["type"]
150
 
151
+ output_display_update = gr.update(visible=False)
 
 
152
  full_history_log += f"\n**Result:**\n"
153
  if output_type == "animation":
154
+ full_history_log += "*(The animated tapestry unfolds below...)*"
155
+ output_display_update = gr.Code(value=stdout if stdout else stderr, language=None, visible=True, label="Live Terminal Output")
 
 
 
156
  else:
157
+ if stdout:
158
+ full_history_log += f"**[STDOUT]**\n```\n{stdout.strip()}\n```\n"
159
+ if stderr:
160
+ full_history_log += f"**[STDERR]**\n```\n{stderr.strip()}\n```\n"
161
 
162
+ yield full_history_log, user_summary, output_display_update
 
163
 
164
+ user_prompt_for_next_turn = f"The last action was `{command}`. The result was:\nSTDOUT: {stdout}\nSTDERR: {stderr}\n\nBased on this, continue to the next step of the Master Plan. If an error occurred, analyze it and correct your course."
165
  message_context.append({"role": "assistant", "content": json.dumps(ai_response_json)})
166
  message_context.append({"role": "user", "content": user_prompt_for_next_turn})
167
 
168
+ with gr.Blocks(theme=gr.themes.Monochrome(primary_hue="teal", secondary_hue="green"), css="footer {visibility: hidden}") as demo:
169
+ gr.Markdown("# 🧬 The C-Genesis Weaver 🧬")
170
+ gr.Markdown("An AI artisan who weaves digital tapestries with C code. Ask for something `fun` or `impressive` to witness the creation.")
 
 
171
 
172
  with gr.Row():
173
  with gr.Column(scale=1):
174
  status_box = gr.Textbox(label="Current Stage", interactive=False)
175
+ user_input = gr.Textbox(label="State Your Directive", placeholder="e.g., 'Make something fun for me'")
176
+ submit_btn = gr.Button("▶️ Weave", variant="primary")
 
 
177
  with gr.Column(scale=2):
178
+ chronicle_display = gr.Markdown()
179
+
180
+ output_display = gr.Code(label="Live Terminal Output", language=None, visible=False, interactive=False, lines=20)
181
+
182
+ def on_submit(user_goal):
183
+ for chronicle, status, output in agent_loop(user_goal):
184
+ yield chronicle, status, "", output
 
 
 
 
 
 
 
185
 
186
  submit_btn.click(
187
  on_submit,
188
+ inputs=[user_input],
189
+ outputs=[chronicle_display, status_box, user_input, output_display]
190
  )
191
 
192
  if __name__ == "__main__":