gpt-code-free / Dockerfile
Carter-123's picture
Update Dockerfile
ea05f54 verified
Raw
History Blame Contribute Delete
14 kB
# ==========================================
# Stage 1: Download & cache the model weights
# ==========================================
FROM python:3.9-slim AS model-builder
RUN pip install --no-cache-dir transformers torch
# Pre-download the gpt2-mini model weights so the Space boots instantly
RUN python -c " \
from transformers import AutoTokenizer, AutoModelForCausalLM; \
AutoTokenizer.from_pretrained('erwanf/gpt2-mini'); \
AutoModelForCausalLM.from_pretrained('erwanf/gpt2-mini') \
"
# ==========================================
# Stage 2: Final Runtime Environment
# ==========================================
FROM python:3.9-slim
WORKDIR /app
# Install system dependencies and Python packages
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
&& rm -rf /var/lib/apt/lists/* \
&& pip install --no-cache-dir fastapi uvicorn transformers torch
# Copy pre-downloaded model weights from the builder stage
COPY --from=model-builder /root/.cache/huggingface /root/.cache/huggingface
# Use Docker Heredoc to write main.py cleanly without shell escaping issues
COPY <<EOF /app/main.py
import os
import torch
from fastapi import FastAPI, HTTPException
from fastapi.responses import HTMLResponse
from pydantic import BaseModel
from transformers import AutoTokenizer, AutoModelForCausalLM
app = FastAPI()
# Initialize tokenizer and model from local cache
print("Loading gpt2-mini model into memory...")
tokenizer = AutoTokenizer.from_pretrained("erwanf/gpt2-mini")
model = AutoModelForCausalLM.from_pretrained("erwanf/gpt2-mini")
model.eval()
print("Model loaded successfully.")
class GenerationRequest(BaseModel):
prompt: str
max_tokens: int = 128
temperature: float = 0.7
@app.post("/api/generate")
async def generate_code(req: GenerationRequest):
try:
# Structure the prompt slightly to guide the raw text-generation model
structured_prompt = f"# Python Script\n# Description: {req.prompt}\n\n"
inputs = tokenizer.encode(structured_prompt, return_tensors="pt")
with torch.no_grad():
outputs = model.generate(
inputs,
max_length=inputs.shape[1] + req.max_tokens,
temperature=req.temperature,
do_sample=True if req.temperature > 0.0 else False,
pad_token_id=tokenizer.eos_token_id,
no_repeat_ngram_size=3
)
generated_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
# Clean up output to isolate code
if generated_text.startswith(structured_prompt):
generated_text = generated_text[len(structured_prompt):]
return {"code": generated_text.strip()}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.get("/", response_class=HTMLResponse)
async def serve_ui():
return """<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GPT2-Mini Python Workspace</title>
<style>
:root {
--bg-main: #05060a;
--bg-surface: #0d1117;
--accent-cyan: #00f0ff;
--accent-orange: #ff6b00;
--text-muted: #8b949e;
--text-light: #c9d1d9;
--border-color: #1f242c;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
background-color: var(--bg-main);
color: var(--text-light);
font-family: "Courier New", Courier, monospace;
display: flex;
flex-direction: column;
height: 100vh;
overflow: hidden;
}
header {
background-color: var(--bg-surface);
border-bottom: 2px solid var(--accent-cyan);
padding: 15px 20px;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 0 15px rgba(0, 240, 255, 0.15);
}
header h1 {
font-size: 1.2rem;
color: var(--accent-cyan);
text-transform: uppercase;
letter-spacing: 2px;
text-shadow: 0 0 8px rgba(0, 240, 255, 0.5);
}
.status-badge {
color: var(--accent-orange);
font-size: 0.85rem;
border: 1px solid var(--accent-orange);
padding: 3px 8px;
border-radius: 3px;
text-shadow: 0 0 5px rgba(255, 107, 0, 0.4);
}
main {
display: flex;
flex: 1;
overflow: hidden;
}
.control-panel {
width: 350px;
background-color: var(--bg-surface);
border-right: 1px solid var(--border-color);
display: flex;
flex-direction: column;
padding: 20px;
gap: 20px;
}
.workspace-panel {
flex: 1;
display: flex;
flex-direction: column;
background-color: var(--bg-main);
}
label {
color: var(--accent-cyan);
font-size: 0.85rem;
text-transform: uppercase;
letter-spacing: 1px;
}
textarea, input, select {
background-color: var(--bg-main);
border: 1px solid var(--border-color);
color: var(--text-light);
padding: 10px;
font-family: inherit;
font-size: 0.9rem;
border-radius: 4px;
outline: none;
transition: border-color 0.2s;
}
textarea:focus, input:focus, select:focus {
border-color: var(--accent-cyan);
}
textarea#prompt-input {
height: 120px;
resize: none;
}
.param-group {
display: flex;
flex-direction: column;
gap: 8px;
}
button {
background-color: transparent;
border: 1px solid var(--accent-orange);
color: var(--accent-orange);
padding: 12px;
font-family: inherit;
font-weight: bold;
text-transform: uppercase;
cursor: pointer;
letter-spacing: 1px;
transition: all 0.2s;
margin-top: auto;
}
button:hover {
background-color: var(--accent-orange);
color: var(--bg-main);
box-shadow: 0 0 12px rgba(255, 107, 0, 0.6);
}
button:disabled {
border-color: var(--text-muted);
color: var(--text-muted);
cursor: not-allowed;
box-shadow: none;
}
.tab-bar {
background-color: var(--bg-surface);
display: flex;
border-bottom: 1px solid var(--border-color);
}
.tab {
padding: 12px 24px;
cursor: pointer;
border-right: 1px solid var(--border-color);
font-size: 0.85rem;
text-transform: uppercase;
color: var(--text-muted);
}
.tab.active {
background-color: var(--bg-main);
color: var(--accent-cyan);
border-bottom: 2px solid var(--accent-cyan);
}
.content-area {
flex: 1;
position: relative;
overflow: auto;
}
.view-pane {
display: none;
width: 100%;
height: 100%;
padding: 20px;
font-family: "Courier New", Courier, monospace;
font-size: 0.95rem;
white-space: pre-wrap;
outline: none;
border: none;
background-color: transparent;
color: var(--text-light);
}
.view-pane.active { display: block; }
#preview-pane {
background-color: #0b0d13;
color: #a5d6ff;
}
</style>
</head>
<body>
<header>
<h1>GPT2-CODE // WORKSPACE</h1>
<div class="status-badge" id="app-status">ENGINE READY</div>
</header>
<main>
<div class="control-panel">
<div class="param-group">
<label for="prompt-input">Target Objective</label>
<textarea id="prompt-input" placeholder="e.g., function to parse json strings and extract keys..."></textarea>
</div>
<div class="param-group">
<label for="token-count">Max Generation Length</label>
<input type="number" id="token-count" value="128" min="16" max="384">
</div>
<div class="param-group">
<label for="temp-select">Inference Creativity</label>
<select id="temp-select">
<option value="0.2">0.2 - Strict/Deterministic</option>
<option value="0.6" selected>0.6 - Balanced Code</option>
<option value="0.9">0.9 - Wild Speculation</option>
</select>
</div>
<button id="generate-btn" onclick="triggerInference()">Execute Synthesis</button>
</div>
<div class="workspace-panel">
<div class="tab-bar">
<div class="tab active" id="tab-editor" onclick="switchTab('editor')">Source Output</div>
<div class="tab" id="tab-preview" onclick="switchTab('preview')">Structure Preview</div>
</div>
<div class="content-area">
<textarea class="view-pane active" id="editor-pane" readonly placeholder="# Generated script architecture will compile here..."></textarea>
<div class="view-pane" id="preview-pane"></div>
</div>
</div>
</main>
<script>
function switchTab(type) {
document.querySelectorAll(".tab").forEach(t => t.classList.remove("active"));
document.querySelectorAll(".view-pane").forEach(p => p.classList.remove("active"));
if (type === "editor") {
document.getElementById("tab-editor").classList.add("active");
document.getElementById("editor-pane").classList.add("active");
} else {
document.getElementById("tab-preview").classList.add("active");
document.getElementById("preview-pane").classList.add("active");
renderPreview();
}
}
function renderPreview() {
const rawCode = document.getElementById("editor-pane").value;
const previewElement = document.getElementById("preview-pane");
if (!rawCode.trim()) {
previewElement.innerHTML = `<span style="color:var(--text-muted)">[No raw structures to analyze]</span>`;
return;
}
const lines = rawCode.split("\\n");
let structuralMap = lines.map((line) => {
const clean = line.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
if (line.trim().startsWith("def ") || line.trim().startsWith("class ")) {
return `<span style="color: var(--accent-cyan); font-weight: bold;">\${clean}</span>`;
} else if (line.trim().startsWith("#")) {
return `<span style="color: var(--text-muted); font-style: italic;">\${clean}</span>`;
} else if (line.trim().startsWith("import ") || line.trim().startsWith("from ")) {
return `<span style="color: var(--accent-orange);">\${clean}</span>`;
}
return clean;
}).join("\\n");
previewElement.innerHTML = `<h4>[PARSED AST BLUEPRINT]</h4><br>\${structuralMap}`;
}
async function triggerInference() {
const promptInput = document.getElementById("prompt-input").value;
const tokenCount = document.getElementById("token-count").value;
const temperature = document.getElementById("temp-select").value;
const btn = document.getElementById("generate-btn");
const status = document.getElementById("app-status");
if (!promptInput.trim()) return;
btn.disabled = true;
btn.innerText = "SYNTHESIZING...";
status.innerText = "COMPUTING ENGINE PATHS";
status.style.color = "var(--accent-cyan)";
status.style.borderColor = "var(--accent-cyan)";
try {
const res = await fetch("/api/generate", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
prompt: promptInput,
max_tokens: parseInt(tokenCount, 10),
temperature: parseFloat(temperature)
})
});
if (!res.ok) throw new Error("Inference pipeline failure.");
const data = await res.json();
document.getElementById("editor-pane").value = data.code;
renderPreview();
} catch(err) {
document.getElementById("editor-pane").value = `# Pipeline Exception:\\n\${err.message}`;
} finally {
btn.disabled = false;
btn.innerText = "Execute Synthesis";
status.innerText = "ENGINE READY";
status.style.color = "var(--accent-orange)";
status.style.borderColor = "var(--accent-orange)";
}
}
</script>
</body>
</html>
"""
EOF
# Expose default Hugging Face space port 7860
EXPOSE 7860
# Run with Uvicorn, bound to all network interfaces on port 7860
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]