kimhyunwoo commited on
Commit
85f43bb
ยท
verified ยท
1 Parent(s): 5c3b6c3

Create main.py

Browse files
Files changed (1) hide show
  1. main.py +147 -0
main.py ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import httpx
3
+ from fastapi import FastAPI, HTTPException, Request
4
+ from fastapi.staticfiles import StaticFiles
5
+ from fastapi.responses import FileResponse
6
+ from pydantic import BaseModel
7
+ from pathlib import Path
8
+ from dotenv import load_dotenv
9
+
10
+ load_dotenv()
11
+
12
+ app = FastAPI()
13
+
14
+ CODESTRAL_API_KEY = os.getenv("CODESTRAL_API_KEY")
15
+ CODESTRAL_FIM_ENDPOINT = "https://codestral.mistral.ai/v1/fim/completions"
16
+ CODESTRAL_CHAT_ENDPOINT = "https://codestral.mistral.ai/v1/chat/completions"
17
+
18
+ # --- Pydantic ๋ชจ๋ธ (๋ณ€๊ฒฝ ์—†์Œ) ---
19
+ class HtmlGenRequest(BaseModel):
20
+ prompt: str
21
+
22
+ class CodeGenRequest(BaseModel):
23
+ prompt: str
24
+ language: str
25
+
26
+ class CodeCompleteRequest(BaseModel):
27
+ prefix: str
28
+ suffix: str
29
+ language: str
30
+
31
+ # --- ๊ฒฝ๋กœ ์„ค์ • (๋ฃจํŠธ ๋””๋ ‰ํ† ๋ฆฌ ๊ธฐ์ค€) ---
32
+ # main.py์™€ index.html, style.css, script.js๊ฐ€ ๋ชจ๋‘ ๊ฐ™์€ ๋ ˆ๋ฒจ(/app)์— ์œ„์น˜
33
+ CURRENT_DIR = Path(__file__).resolve().parent # /app
34
+
35
+ # --- API ์—”๋“œํฌ์ธํŠธ (๋กœ์ง ๋ณ€๊ฒฝ ์—†์Œ, ์ด์ „๊ณผ ๋™์ผ) ---
36
+ @app.post("/api/generate-html")
37
+ async def generate_html_endpoint(payload: HtmlGenRequest):
38
+ if not CODESTRAL_API_KEY:
39
+ raise HTTPException(status_code=500, detail="API Key not configured on server. Check Hugging Face Space Secrets.")
40
+ async with httpx.AsyncClient(timeout=60.0) as client:
41
+ try:
42
+ response = await client.post(
43
+ CODESTRAL_CHAT_ENDPOINT,
44
+ headers={"Authorization": f"Bearer {CODESTRAL_API_KEY}", "Content-Type": "application/json", "Accept": "application/json"},
45
+ json={"model": "codestral-latest", "messages": [{"role": "system", "content": "You are an expert HTML and web designer..."}, {"role": "user", "content": payload.prompt}], "temperature": 0.3}
46
+ )
47
+ response.raise_for_status()
48
+ data = response.json()
49
+ html_content = data.get("choices", [{}])[0].get("message", {}).get("content", "").strip() or "<!-- AI HTML generation failed -->"
50
+ return {"html": html_content}
51
+ except httpx.HTTPStatusError as e:
52
+ print(f"Codestral API Error (HTML): {e.response.status_code} - {e.response.text}")
53
+ raise HTTPException(status_code=e.response.status_code, detail=f"Codestral API error: {e.response.text}")
54
+ except Exception as e:
55
+ print(f"Server Error (HTML): {e}")
56
+ raise HTTPException(status_code=500, detail=str(e))
57
+
58
+ @app.post("/api/generate-code")
59
+ async def generate_code_endpoint(payload: CodeGenRequest):
60
+ if not CODESTRAL_API_KEY:
61
+ raise HTTPException(status_code=500, detail="API Key not configured on server.")
62
+ async with httpx.AsyncClient(timeout=60.0) as client:
63
+ try:
64
+ response = await client.post(
65
+ CODESTRAL_CHAT_ENDPOINT,
66
+ headers={"Authorization": f"Bearer {CODESTRAL_API_KEY}", "Content-Type": "application/json", "Accept": "application/json"},
67
+ json={"model": "codestral-latest", "messages": [{"role": "system", "content": f"You are an expert {payload.language} programmer..."}, {"role": "user", "content": payload.prompt}], "temperature": 0.2}
68
+ )
69
+ response.raise_for_status()
70
+ data = response.json()
71
+ code_content = data.get("choices", [{}])[0].get("message", {}).get("content", "").strip() or f"// AI {payload.language} code generation failed."
72
+ return {"code": code_content}
73
+ except httpx.HTTPStatusError as e:
74
+ print(f"Codestral API Error (Code): {e.response.status_code} - {e.response.text}")
75
+ raise HTTPException(status_code=e.response.status_code, detail=f"Codestral API error: {e.response.text}")
76
+ except Exception as e:
77
+ print(f"Server Error (Code): {e}")
78
+ raise HTTPException(status_code=500, detail=str(e))
79
+
80
+ @app.post("/api/complete-code")
81
+ async def complete_code_endpoint(payload: CodeCompleteRequest):
82
+ if not CODESTRAL_API_KEY:
83
+ raise HTTPException(status_code=500, detail="API Key not configured on server.")
84
+ if payload.prefix is None or payload.suffix is None:
85
+ raise HTTPException(status_code=400, detail="Prefix and suffix are required.")
86
+ async with httpx.AsyncClient(timeout=30.0) as client:
87
+ try:
88
+ api_payload = {"model": "codestral-latest", "prompt": payload.prefix, "suffix": payload.suffix, "temperature": 0.1}
89
+ response = await client.post(
90
+ CODESTRAL_FIM_ENDPOINT,
91
+ headers={"Authorization": f"Bearer {CODESTRAL_API_KEY}", "Content-Type": "application/json", "Accept": "application/json"},
92
+ json=api_payload
93
+ )
94
+ response.raise_for_status()
95
+ data = response.json()
96
+ completion_text = data.get("choices", [{}])[0].get("text", "")
97
+ return {"completion": completion_text}
98
+ except httpx.HTTPStatusError as e:
99
+ print(f"Codestral API Error (FIM): {e.response.status_code} - {e.response.text}")
100
+ raise HTTPException(status_code=e.response.status_code, detail=f"Codestral API error: {e.response.text}")
101
+ except Exception as e:
102
+ print(f"Server Error (FIM): {e}")
103
+ raise HTTPException(status_code=500, detail=str(e))
104
+
105
+
106
+ # --- ํ”„๋ก ํŠธ์—”๋“œ ์ •์  ํŒŒ์ผ ๋ฐ SPA ๋ผ์šฐํŒ… (๋ฃจํŠธ ๋””๋ ‰ํ† ๋ฆฌ ๊ธฐ์ค€) ---
107
+
108
+ # style.css์™€ script.js๋ฅผ ์œ„ํ•œ StaticFiles ๋งˆ์šดํŠธ
109
+ # '/static' ๊ฒฝ๋กœ๋กœ ์š”์ฒญ์˜ค๋ฉด ํ˜„์žฌ ๋””๋ ‰ํ† ๋ฆฌ์˜ ํŒŒ์ผ๋“ค์„ ์„œ๋น™ (์˜ˆ: /static/style.css -> ./style.css)
110
+ # ํ•˜์ง€๋งŒ HTML์—์„œ <link rel="stylesheet" href="style.css"> ์ฒ˜๋Ÿผ ์ง์ ‘ ์ฐธ์กฐํ•˜๋ฏ€๋กœ,
111
+ # ์•„๋ž˜์˜ index.html์„ ์„œ๋น™ํ•˜๋Š” ๋ผ์šฐํŠธ์™€ ํ•จ๊ป˜, ๊ฐœ๋ณ„ ํŒŒ์ผ ์„œ๋น™ ๋ผ์šฐํŠธ๊ฐ€ ํ•„์š”.
112
+
113
+ # CSS ํŒŒ์ผ ์„œ๋น™
114
+ @app.get("/style.css")
115
+ async def serve_css():
116
+ return FileResponse(CURRENT_DIR / "style.css", media_type="text/css")
117
+
118
+ # JavaScript ํŒŒ์ผ ์„œ๋น™
119
+ @app.get("/script.js")
120
+ async def serve_js():
121
+ return FileResponse(CURRENT_DIR / "script.js", media_type="application/javascript")
122
+
123
+ # ๋ฃจํŠธ ๊ฒฝ๋กœ ("/") ๋ฐ ๋‹ค๋ฅธ ๋ชจ๋“  ๊ฒฝ๋กœ์— ๋Œ€ํ•ด index.html์„ ์ œ๊ณต (SPA ๋™์ž‘)
124
+ # API ๊ฒฝ๋กœ (/api/...)๋Š” ์ด๊ฒƒ๋ณด๋‹ค ๋จผ์ € ์ •์˜๋˜์–ด์•ผ ํ•จ
125
+ @app.get("/{full_path:path}")
126
+ async def serve_spa(request: Request, full_path: str):
127
+ # API ํ˜ธ์ถœ์ด ์•„๋‹Œ ๊ฒฝ์šฐ์—๋งŒ index.html ๋ฐ˜ํ™˜
128
+ if full_path.startswith("api/"): # ์ด๋ฏธ ์œ„์—์„œ ์ฒ˜๋ฆฌ๋จ. ํ˜น์‹œ ๋ชจ๋ฅผ ๊ฒฝ์šฐ ๋Œ€๋น„.
129
+ raise HTTPException(status_code=404, detail="API endpoint not found here.")
130
+ return FileResponse(CURRENT_DIR / "index.html", media_type="text/html")
131
+
132
+ # ๋งŒ์•ฝ ์œ„ ๋ฐฉ์‹์ด ๋ณต์žกํ•˜๋‹ค๋ฉด, StaticFiles๋ฅผ ํ•˜๋‚˜๋กœ ๋งˆ์šดํŠธํ•˜๊ณ  html=True ์‚ฌ์šฉ:
133
+ # app.mount("/", StaticFiles(directory=CURRENT_DIR, html=True), name="static_root")
134
+ # ์ด ๊ฒฝ์šฐ, index.html, style.css, script.js ๋ชจ๋‘ CURRENT_DIR์—์„œ ์ง์ ‘ ์„œ๋น™๋จ.
135
+ # /style.css ์š”์ฒญ ์‹œ ./style.css ํŒŒ์ผ ์„œ๋น™.
136
+ # / ์š”์ฒญ ์‹œ ./index.html ํŒŒ์ผ ์„œ๋น™.
137
+ # ์ด ๋ฐฉ์‹์ด ๋” ๊ฐ„๋‹จํ•˜๋ฉฐ, ์œ„ ๊ฐœ๋ณ„ ๋ผ์šฐํŠธ๋Š” ํ•„์š” ์—†์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
138
+ # ๋‘˜ ์ค‘ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•˜์„ธ์š”. ์—ฌ๊ธฐ์„œ๋Š” ๋” ๋ช…์‹œ์ ์ธ ๊ฐœ๋ณ„ ๋ผ์šฐํŠธ ๋ฐฉ์‹์„ ๋‚จ๊ฒจ๋‘์—ˆ์Šต๋‹ˆ๋‹ค.
139
+ # StaticFiles(directory=".") ๋กœ ํ˜„์žฌ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ์ง€์ •ํ•ด๋„ ๋ฉ๋‹ˆ๋‹ค.
140
+
141
+ if __name__ == "__main__":
142
+ import uvicorn
143
+ # Hugging Face Spaces์—์„œ๋Š” CMD ๋ช…๋ น์–ด๋กœ uvicorn์ด ์‹คํ–‰๋˜๋ฏ€๋กœ ์ด ๋ถ€๋ถ„์€ ๋กœ์ปฌ ํ…Œ์ŠคํŠธ์šฉ์ž…๋‹ˆ๋‹ค.
144
+ # ํฌํŠธ 7860์€ Hugging Face Spaces์˜ ๊ธฐ๋ณธ ํฌํŠธ์ž…๋‹ˆ๋‹ค.
145
+ print(f"๋กœ์ปฌ ํ…Œ์ŠคํŠธ ์„œ๋ฒ„ ์‹œ์ž‘: http://localhost:7860")
146
+ print(f"index.html์€ ๋‹ค์Œ ๊ฒฝ๋กœ์— ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค: {CURRENT_DIR / 'index.html'}")
147
+ uvicorn.run(app, host="0.0.0.0", port=7860)