c
File size: 6,936 Bytes
e0c0af8
f75669b
 
db2f9f4
a5c7603
db2f9f4
c1ec9e7
f75669b
db2f9f4
9ebbf81
db2f9f4
 
fbae41a
a5c7603
9e50a0d
f75669b
a5c7603
 
db2f9f4
a5c7603
c1ec9e7
db2f9f4
a5c7603
 
 
 
c1ec9e7
db2f9f4
 
 
 
 
f75669b
302b980
 
 
 
 
f75669b
 
bb4dd6d
 
 
 
 
f75669b
bb4dd6d
 
302b980
 
 
 
bb4dd6d
c1ec9e7
 
 
 
 
 
 
db2f9f4
c1ec9e7
 
 
 
 
 
 
 
 
 
 
302b980
c1ec9e7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
302b980
c1ec9e7
 
 
 
 
 
 
 
 
b328beb
c1ec9e7
 
 
 
b328beb
c1ec9e7
db2f9f4
b328beb
c1ec9e7
 
b328beb
c1ec9e7
b328beb
 
c1ec9e7
 
 
 
 
 
 
 
 
 
 
 
b328beb
302b980
b328beb
302b980
 
 
e0c0af8
 
9c4c102
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import gradio as gr
import os
import subprocess
import requests
import json
import re
import time

# --- 1. ν™˜κ²½ μ„€μ • 및 API 호좜 ν•¨μˆ˜ ---
API_KEY = os.environ.get("MISTRAL_API_KEY")
CODESTRAL_ENDPOINT = "https://codestral.mistral.ai/v1/chat/completions"
HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}

if not API_KEY:
    print("FATAL: MISTRAL_API_KEY is not set in Space Secrets.")

def call_mistral_api(messages):
    if not API_KEY: return "Error: MISTRAL_API_KEY is not configured."
    data = {"model": "codestral-latest", "messages": messages}
    try:
        response = requests.post(CODESTRAL_ENDPOINT, headers=HEADERS, data=json.dumps(data), timeout=45)
        response.raise_for_status()
        return response.json()["choices"][0]["message"]["content"]
    except requests.exceptions.RequestException as e:
        return f"API Call Error: {e}"

# --- 2. λ°±μ—”λ“œ 핡심 κΈ°λŠ₯ (νŒŒμ„œ 포함) ---
def parse_code_from_response(response_text: str) -> str | None:
    match = re.search(r'```c\n(.*?)\n```', response_text, re.DOTALL)
    if match:
        return match.group(1).strip()
    return None

# --- MCP νƒ­κ³Ό μ—μ΄μ „νŠΈκ°€ λͺ¨λ‘ μ‚¬μš©ν•  μ „μ—­ ν•¨μˆ˜λ‘œ μ •μ˜ ---
def generate_c_code(description: str) -> str:
    prompt = f"Generate a complete, compilable C code for this request: '{description}'. ONLY output the raw C code, without markdown or explanations."
    return call_mistral_api([{"role": "user", "content": prompt}])

def compile_and_run_c_code(code: str) -> str:
    try:
        with open("main.c", "w", encoding='utf-8') as f: f.write(code)
        compile_proc = subprocess.run(["gcc", "main.c", "-o", "main.out", "-lm", "-w"], capture_output=True, text=True, timeout=15)
        if compile_proc.returncode != 0: return f"--- COMPILATION FAILED ---\n{compile_proc.stderr}"
        run_proc = subprocess.run(["./main.out"], capture_output=True, text=True, timeout=15)
        if run_proc.returncode != 0: return f"--- RUNTIME ERROR ---\n{run_proc.stderr}"
        output = run_proc.stdout
        return f"--- EXECUTION SUCCEEDED ---\n{output}" if output.strip() else "--- EXECUTION SUCCEEDED ---\n(No output was produced)"
    except Exception as e: return f"--- SYSTEM ERROR ---\nAn unexpected error occurred: {str(e)}"
    
def analyze_and_refactor_code(code: str, instruction: str) -> str:
    prompt = f"You are a senior C code reviewer. Fulfill this instruction: '{instruction}'. If you refactor or change the code, YOU MUST provide the complete, new code in a ```c code block. \n\nC Code to Analyze:\n```c\n{code}\n```"
    return call_mistral_api([{"role": "user", "content": prompt}])

# --- 3. μ§„μ§œ 'μ§€λŠ₯ν˜•' μ—μ΄μ „νŠΈ 둜직 ---
def intelligent_agent_ide(initial_code: str, full_instruction: str):
    if not full_instruction:
        yield initial_code, "Error: Instruction cannot be empty."
        return

    tasks = [task.strip() for task in re.split(r'\s+and\s+|\s*,\s*then\s*|\s*그리고\s*', full_instruction, flags=re.IGNORECASE)]
    
    current_code = initial_code
    output_log = []
    step = 1

    for task in tasks:
        lower_task = task.lower()
        output_log.append(f"β–Ά Step {step}: Executing '{task}'...")
        yield current_code, "\n".join(output_log)
        time.sleep(0.5)

        if "generate" in lower_task or "create" in lower_task or "λ§Œλ“€μ–΄μ€˜" in lower_task:
            new_code = generate_c_code(task)
            current_code = new_code if new_code and "Error" not in new_code else current_code
            output_log.append(f"βœ… Code generated and updated in the editor.")
            yield current_code, "\n".join(output_log)
        
        elif "compile" in lower_task or "run" in lower_task or "μ‹€ν–‰" in lower_task:
            if not current_code:
                output_log.append("❌ Error: Code is empty. Cannot compile.")
                yield current_code, "\n".join(output_log)
                break
            result = compile_and_run_c_code(current_code)
            output_log.append(result)
            yield current_code, "\n".join(output_log)

        else:
            if not current_code:
                output_log.append("❌ Error: Code is empty. Cannot analyze.")
                yield current_code, "\n".join(output_log)
                break
            analysis_result = analyze_and_refactor_code(current_code, task)
            
            refactored_code = parse_code_from_response(analysis_result)
            if refactored_code:
                current_code = refactored_code
                output_log.append(f"βœ… Code refactored and updated in the editor.")
            output_log.append(f"πŸ”Ž Analysis Result:\n{analysis_result}")
            yield current_code, "\n".join(output_log)

        step += 1
    
    output_log.append("\n--- All tasks complete. ---")
    yield current_code, "\n".join(output_log)

# --- 4. ν†΅ν•©λœ Gradio UI ---
with gr.Blocks(theme=gr.themes.Monochrome(primary_hue="indigo", secondary_hue="blue"), css="footer {visibility: hidden}") as demo:
    gr.Markdown("# πŸ’» The True C-Codestral IDE Agent")

    with gr.Tabs():
        with gr.TabItem("πŸ‘¨β€πŸ’» IDE Agent"):
            with gr.Row(equal_height=True):
                with gr.Column(scale=2):
                    code_editor = gr.Code(label="C Code Editor", language="c", lines=28, interactive=True, value='#include <stdio.h>\n\nint main() {\n    printf("Hello, World!\\n");\n    return 0;\n}')
                
                with gr.Column(scale=1):
                    instruction_box = gr.Textbox(label="Instruction", placeholder="e.g., 'Refactor this code for readability, and then compile it'", lines=4)
                    execute_btn = gr.Button("Execute", variant="primary", size="lg")
                    output_box = gr.Textbox(label="Console / Output", lines=21, interactive=False, show_copy_button=True, max_lines=50)

            execute_btn.click(
                fn=intelligent_agent_ide, 
                inputs=[code_editor, instruction_box], 
                outputs=[code_editor, output_box]
            )

        with gr.TabItem("πŸ› οΈ MCP Tools API"):
            gr.Markdown("## Available MCP Tools for other Agents\nThese APIs are the building blocks of our IDE agent.")
            with gr.Accordion("Tool: Generate C Code", open=False):
                gr.Interface(fn=generate_c_code, inputs="text", outputs=gr.Code(language="c", label="Generated C Code"))
            with gr.Accordion("Tool: Compile & Run C Code", open=False):
                gr.Interface(fn=compile_and_run_c_code, inputs=gr.Code(language="c"), outputs=gr.Textbox(label="Output"))
            with gr.Accordion("Tool: Analyze & Refactor C Code", open=False):
                gr.Interface(fn=analyze_and_refactor_code, inputs=[gr.Code(language="c"), "text"], outputs=gr.Markdown())

if __name__ == "__main__":
    demo.queue().launch()