Hariprasath5128 commited on
Commit
8a5fc23
Β·
1 Parent(s): 0f95125

Prepare Syntax AI for Hugging Face Space

Browse files
Files changed (5) hide show
  1. .dockerignore +7 -0
  2. Dockerfile +17 -52
  3. README.md +22 -10
  4. requirements.txt +2 -1
  5. server.py +304 -16
.dockerignore ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ .git
2
+ __pycache__
3
+ *.pyc
4
+ .env
5
+ frontend/node_modules
6
+ frontend/build
7
+ output.txt
Dockerfile CHANGED
@@ -1,66 +1,31 @@
1
- # --- Stage 1: Build the React Frontend ---
2
- FROM node:18 AS build-stage
3
  WORKDIR /app/frontend
4
  COPY frontend/package*.json ./
5
- RUN npm install
6
  COPY frontend/ ./
7
  RUN npm run build
8
 
9
- # --- Stage 2: Final Monolithic Image ---
10
  FROM python:3.11-slim
11
 
12
- # Install Node.js and Nginx
13
- RUN apt-get update && apt-get install -y \
14
- curl \
15
- nginx \
16
- && curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \
17
- && apt-get install -y nodejs \
18
- && rm -rf /var/lib/apt/lists/*
19
-
20
- WORKDIR /app
21
-
22
- # 1. Setup Backend (Node.js)
23
- COPY frontend/package*.json ./backend/
24
- RUN cd backend && npm install --production
25
- COPY frontend/src/server.js ./backend/src/
26
-
27
- # 2. Setup AI Agent (Python)
28
- COPY requirements.txt .
29
- RUN pip install --no-cache-dir -r requirements.txt
30
- COPY ai_agent.py server.py ./
31
 
32
- # 3. Setup Frontend (Static Files)
33
- COPY --from=build-stage /app/frontend/build /usr/share/nginx/html
 
34
 
35
- # 4. Nginx Configuration
36
- RUN echo 'server { \
37
- listen 7860; \
38
- location / { \
39
- root /usr/share/nginx/html; \
40
- index index.html; \
41
- try_files $uri /index.html; \
42
- } \
43
- location /api/ { \
44
- proxy_pass http://127.0.0.1:5003/; \
45
- proxy_set_header Host $host; \
46
- } \
47
- location /process-request/ { \
48
- proxy_pass http://127.0.0.1:8000/process-request; \
49
- proxy_set_header Host $host; \
50
- } \
51
- }' > /etc/nginx/sites-available/default
52
 
53
- # 5. Startup Script
54
- RUN echo '#!/bin/bash\n\
55
- python3 server.py & \n\
56
- node backend/src/server.js & \n\
57
- nginx -g "daemon off;"' > /app/start.sh
58
- RUN chmod +x /app/start.sh
59
 
60
- # Hugging Face Requirement: Non-root user with Port 7860
61
- # (HFS provides this user by default, but we'll ensure Nginx is ready)
62
- RUN chmod -R 777 /var/log/nginx /var/lib/nginx /run
63
 
64
  EXPOSE 7860
65
 
66
- CMD ["/app/start.sh"]
 
1
+ FROM node:18 AS frontend-build
2
+
3
  WORKDIR /app/frontend
4
  COPY frontend/package*.json ./
5
+ RUN npm ci
6
  COPY frontend/ ./
7
  RUN npm run build
8
 
 
9
  FROM python:3.11-slim
10
 
11
+ ENV PYTHONDONTWRITEBYTECODE=1 \
12
+ PYTHONUNBUFFERED=1 \
13
+ PORT=7860
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
+ RUN useradd -m -u 1000 user
16
+ USER user
17
+ ENV PATH="/home/user/.local/bin:$PATH"
18
 
19
+ WORKDIR /app
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
+ COPY --chown=user requirements.txt ./
22
+ RUN pip install --no-cache-dir --upgrade pip && \
23
+ pip install --no-cache-dir -r requirements.txt
 
 
 
24
 
25
+ COPY --chown=user ai_agent.py server.py ./
26
+ COPY --chown=user frontend ./frontend
27
+ COPY --from=frontend-build --chown=user /app/frontend/build ./frontend/build
28
 
29
  EXPOSE 7860
30
 
31
+ CMD ["uvicorn", "server:app", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -40,10 +40,10 @@ Syntax AI includes a sophisticated reporting engine that visualizes the impact o
40
  The user interface is built on a custom design system that prioritizes clarity and visual depth. The "Galaxy Glass" aesthetic utilizes dynamic starfield simulations, advanced glassmorphism components, and subtle micro-animations to create a focused, premium workspace.
41
 
42
  ### Backend Infrastructure
43
- - **Python (FastAPI)**: Orchestrates AI inference and core logic.
44
  - **Phidata**: Manages multi-agent task distribution and state.
45
- - **Node.js (Express)**: Handles secure authentication and database persistence.
46
  - **Mistral AI**: Provides the underlying transformer architecture for code intelligence.
 
47
 
48
  ### Frontend Implementation
49
  - **React.js**: Facilitates a responsive and component-driven user experience.
@@ -60,13 +60,13 @@ The user interface is built on a custom design system that prioritizes clarity a
60
  ### 1. Prerequisites
61
  - Python 3.10 or higher
62
  - Node.js (v18+) and npm
63
- - MySQL Server instance
64
 
65
  ### 2. Environment Configuration
66
  Define local environment variables in a `.env` file at the root directory (see `.env.example` for details):
67
  ```env
68
  MISTRAL_API_KEY=your_secured_key
69
- DB_PASSWORD=your_mysql_credentials
70
  ```
71
 
72
  ### 3. Dependency Installation
@@ -81,16 +81,28 @@ npm install
81
 
82
  ### 4. System Launch
83
  ```bash
84
- # Start AI Logic Server
85
  python server.py
86
 
87
- # Start Node Backend (located in /frontend/src)
88
- node server.js
89
-
90
- # Initialize React Application
91
  npm start
92
  ```
93
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  ---
95
 
96
  ## License
@@ -98,4 +110,4 @@ This project is released under the MIT License.
98
 
99
  ---
100
 
101
- Developed by [Hariprasath](https://github.com/Hariprasath-5128)
 
40
  The user interface is built on a custom design system that prioritizes clarity and visual depth. The "Galaxy Glass" aesthetic utilizes dynamic starfield simulations, advanced glassmorphism components, and subtle micro-animations to create a focused, premium workspace.
41
 
42
  ### Backend Infrastructure
43
+ - **Python (FastAPI)**: Serves the React UI, orchestrates AI inference, and exposes the API routes.
44
  - **Phidata**: Manages multi-agent task distribution and state.
 
45
  - **Mistral AI**: Provides the underlying transformer architecture for code intelligence.
46
+ - **PostgreSQL / Supabase (optional)**: Stores users and activity history when `DATABASE_URL` is configured.
47
 
48
  ### Frontend Implementation
49
  - **React.js**: Facilitates a responsive and component-driven user experience.
 
60
  ### 1. Prerequisites
61
  - Python 3.10 or higher
62
  - Node.js (v18+) and npm
63
+ - Optional PostgreSQL / Supabase database
64
 
65
  ### 2. Environment Configuration
66
  Define local environment variables in a `.env` file at the root directory (see `.env.example` for details):
67
  ```env
68
  MISTRAL_API_KEY=your_secured_key
69
+ DATABASE_URL=postgresql://username:password@host:port/database
70
  ```
71
 
72
  ### 3. Dependency Installation
 
81
 
82
  ### 4. System Launch
83
  ```bash
84
+ # Start the FastAPI server
85
  python server.py
86
 
87
+ # In a separate terminal, start the React dev server
88
+ cd frontend
 
 
89
  npm start
90
  ```
91
 
92
+ ### 5. Hugging Face Spaces
93
+ This repository is configured for a Docker Space.
94
+
95
+ Required secret:
96
+ - `MISTRAL_API_KEY`
97
+
98
+ Optional secret:
99
+ - `DATABASE_URL`
100
+
101
+ Notes:
102
+ - The container exposes port `7860`, which matches the Space metadata.
103
+ - If `DATABASE_URL` is not set, the app falls back to in-memory login and activity storage so the demo still runs.
104
+ - The React app is built inside the Docker image and served by FastAPI from the same container.
105
+
106
  ---
107
 
108
  ## License
 
110
 
111
  ---
112
 
113
+ Developed by [Hariprasath](https://github.com/Hariprasath-5128)
requirements.txt CHANGED
@@ -1,7 +1,8 @@
1
  fastapi
2
- uvicorn
3
  pydantic
4
  phidata==2.7.10
5
  mistralai==1.1.0
6
  python-dotenv
7
  requests
 
 
1
  fastapi
2
+ uvicorn[standard]
3
  pydantic
4
  phidata==2.7.10
5
  mistralai==1.1.0
6
  python-dotenv
7
  requests
8
+ psycopg[binary]
server.py CHANGED
@@ -1,37 +1,325 @@
1
  import logging
 
 
 
 
 
 
2
  from fastapi import FastAPI, HTTPException
 
 
 
3
  from pydantic import BaseModel
 
 
 
 
 
 
4
  from ai_agent import handle_task
5
 
 
 
 
6
  logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
 
 
 
 
 
7
 
8
  app = FastAPI(title="Syntax AI Server")
 
 
 
 
 
 
 
 
9
 
10
  class RequestModel(BaseModel):
11
  taskType: str
12
  prompt: str
13
 
14
- # --- API Endpoint ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
  @app.post("/process-request")
 
17
  async def process_request(request: RequestModel):
18
  try:
19
- # Get the result from AI
20
- result = handle_task(request.prompt)
21
-
22
- # Convert result to string if it's not already
23
- if hasattr(result, 'content'):
24
- result_str = result.content
25
- elif isinstance(result, dict):
26
- result_str = str(result)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  else:
28
- result_str = str(result)
29
-
30
- return {"status": "success", "result": result_str}
31
- except Exception as e:
32
- logging.exception("Error")
33
- raise HTTPException(status_code=500, detail=str(e))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
  if __name__ == "__main__":
36
  import uvicorn
37
- uvicorn.run(app, host="0.0.0.0", port=8000)
 
 
1
  import logging
2
+ import os
3
+ from contextlib import contextmanager
4
+ from pathlib import Path
5
+ from typing import Any
6
+
7
+ from dotenv import load_dotenv
8
  from fastapi import FastAPI, HTTPException
9
+ from fastapi.middleware.cors import CORSMiddleware
10
+ from fastapi.responses import FileResponse
11
+ from fastapi.staticfiles import StaticFiles
12
  from pydantic import BaseModel
13
+
14
+ try:
15
+ import psycopg
16
+ except ImportError: # pragma: no cover
17
+ psycopg = None
18
+
19
  from ai_agent import handle_task
20
 
21
+
22
+ load_dotenv()
23
+
24
  logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
25
+ logger = logging.getLogger(__name__)
26
+
27
+ BASE_DIR = Path(__file__).resolve().parent
28
+ FRONTEND_BUILD_DIR = BASE_DIR / "frontend" / "build"
29
+ DATABASE_URL = os.getenv("DATABASE_URL", "").strip()
30
 
31
  app = FastAPI(title="Syntax AI Server")
32
+ app.add_middleware(
33
+ CORSMiddleware,
34
+ allow_origins=["*"],
35
+ allow_credentials=True,
36
+ allow_methods=["*"],
37
+ allow_headers=["*"],
38
+ )
39
+
40
 
41
  class RequestModel(BaseModel):
42
  taskType: str
43
  prompt: str
44
 
45
+
46
+ class AuthRequest(BaseModel):
47
+ username: str
48
+ password: str
49
+ action: str
50
+
51
+
52
+ class StoreOptionRequest(BaseModel):
53
+ userId: int | None = None
54
+ option: str
55
+ language: str | None = None
56
+ codePrompt: str | None = None
57
+ modifyCode: str | None = None
58
+ modifyLogic: str | None = None
59
+
60
+
61
+ class AnalyzeCodeRequest(BaseModel):
62
+ userId: int | None = None
63
+ originalCode: str
64
+ modifiedCode: str
65
+
66
+
67
+ memory_users: dict[str, dict[str, Any]] = {}
68
+ memory_activity: list[dict[str, Any]] = []
69
+ memory_user_id = 0
70
+
71
+
72
+ def has_database() -> bool:
73
+ return bool(DATABASE_URL and psycopg is not None)
74
+
75
+
76
+ @contextmanager
77
+ def get_db_connection():
78
+ if not has_database():
79
+ raise RuntimeError("DATABASE_URL is not configured.")
80
+ conn = psycopg.connect(DATABASE_URL)
81
+ try:
82
+ yield conn
83
+ finally:
84
+ conn.close()
85
+
86
+
87
+ def setup_database() -> None:
88
+ if not has_database():
89
+ logger.warning("DATABASE_URL not set. Using in-memory auth and activity storage.")
90
+ return
91
+
92
+ try:
93
+ with get_db_connection() as conn:
94
+ with conn.cursor() as cur:
95
+ cur.execute(
96
+ """
97
+ CREATE TABLE IF NOT EXISTS users (
98
+ id SERIAL PRIMARY KEY,
99
+ username TEXT NOT NULL UNIQUE,
100
+ password TEXT NOT NULL
101
+ )
102
+ """
103
+ )
104
+ cur.execute(
105
+ """
106
+ CREATE TABLE IF NOT EXISTS user_activity (
107
+ id SERIAL PRIMARY KEY,
108
+ user_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
109
+ user_option TEXT,
110
+ language TEXT,
111
+ qn TEXT,
112
+ modify_code_input TEXT,
113
+ modify_code_logic TEXT,
114
+ output TEXT,
115
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
116
+ )
117
+ """
118
+ )
119
+ conn.commit()
120
+ logger.info("Database tables are ready.")
121
+ except Exception:
122
+ logger.exception("Database initialization failed. Falling back to in-memory mode.")
123
+
124
+
125
+ @app.on_event("startup")
126
+ def on_startup() -> None:
127
+ setup_database()
128
+
129
+
130
+ def run_ai_task(prompt: str) -> str:
131
+ result = handle_task(prompt)
132
+ if hasattr(result, "content"):
133
+ return str(result.content)
134
+ if isinstance(result, dict):
135
+ return str(result)
136
+ return str(result)
137
+
138
+
139
+ def create_memory_user(username: str, password: str) -> int:
140
+ global memory_user_id
141
+ if username in memory_users:
142
+ raise ValueError("Username already exists.")
143
+ memory_user_id += 1
144
+ memory_users[username] = {"id": memory_user_id, "password": password}
145
+ return memory_user_id
146
+
147
 
148
  @app.post("/process-request")
149
+ @app.post("/api/process-request")
150
  async def process_request(request: RequestModel):
151
  try:
152
+ return {"status": "success", "result": run_ai_task(request.prompt)}
153
+ except Exception as exc:
154
+ logger.exception("Error while processing AI request.")
155
+ raise HTTPException(status_code=500, detail=str(exc)) from exc
156
+
157
+
158
+ @app.post("/api/auth")
159
+ async def auth(request: AuthRequest):
160
+ try:
161
+ if has_database():
162
+ with get_db_connection() as conn:
163
+ with conn.cursor() as cur:
164
+ if request.action == "signup":
165
+ cur.execute(
166
+ "INSERT INTO users (username, password) VALUES (%s, %s) RETURNING id",
167
+ (request.username, request.password),
168
+ )
169
+ user_id = cur.fetchone()[0]
170
+ conn.commit()
171
+ return {"success": True, "message": "Signup successful!", "userId": user_id}
172
+
173
+ cur.execute(
174
+ "SELECT id FROM users WHERE username = %s AND password = %s",
175
+ (request.username, request.password),
176
+ )
177
+ row = cur.fetchone()
178
+ if row:
179
+ return {"success": True, "message": "Login successful!", "userId": row[0]}
180
+ return {"success": False, "message": "Invalid credentials."}
181
+
182
+ if request.action == "signup":
183
+ user_id = create_memory_user(request.username, request.password)
184
+ return {"success": True, "message": "Signup successful!", "userId": user_id}
185
+
186
+ user = memory_users.get(request.username)
187
+ if user and user["password"] == request.password:
188
+ return {"success": True, "message": "Login successful!", "userId": user["id"]}
189
+ return {"success": False, "message": "Invalid credentials."}
190
+ except Exception as exc:
191
+ logger.exception("Authentication error.")
192
+ return {"success": False, "message": f"DB Error: {exc}"}
193
+
194
+
195
+ @app.post("/api/store-option")
196
+ async def store_option(request: StoreOptionRequest):
197
+ try:
198
+ final_prompt = ""
199
+
200
+ if request.option == "Generate Code":
201
+ final_prompt = request.codePrompt or ""
202
+ elif request.option == "Modify Code":
203
+ final_prompt = request.modifyCode or ""
204
+
205
+ ai_output = run_ai_task(final_prompt)
206
+
207
+ if has_database():
208
+ with get_db_connection() as conn:
209
+ with conn.cursor() as cur:
210
+ cur.execute(
211
+ """
212
+ INSERT INTO user_activity (
213
+ user_id, user_option, language, qn,
214
+ modify_code_input, modify_code_logic, output
215
+ )
216
+ VALUES (%s, %s, %s, %s, %s, %s, %s)
217
+ """,
218
+ (
219
+ request.userId,
220
+ request.option,
221
+ request.language,
222
+ final_prompt,
223
+ request.modifyCode if request.option == "Modify Code" else None,
224
+ request.modifyLogic if request.option == "Modify Code" else None,
225
+ ai_output,
226
+ ),
227
+ )
228
+ conn.commit()
229
  else:
230
+ memory_activity.append(
231
+ {
232
+ "user_id": request.userId,
233
+ "user_option": request.option,
234
+ "language": request.language,
235
+ "qn": final_prompt,
236
+ "modify_code_input": request.modifyCode if request.option == "Modify Code" else None,
237
+ "modify_code_logic": request.modifyLogic if request.option == "Modify Code" else None,
238
+ "output": ai_output,
239
+ }
240
+ )
241
+
242
+ return {
243
+ "success": True,
244
+ "message": f"{request.option} data stored successfully!",
245
+ "aiOutput": ai_output,
246
+ }
247
+ except Exception:
248
+ logger.exception("Error in store-option.")
249
+ return {"success": False, "message": "Error processing request."}
250
+
251
+
252
+ @app.post("/api/analyze-code")
253
+ async def analyze_code(request: AnalyzeCodeRequest):
254
+ analysis_prompt = f"""You are a code efficiency analyzer. Compare the ORIGINAL and MODIFIED code below and provide a detailed efficiency analysis.
255
+
256
+ ORIGINAL CODE:
257
+ {request.originalCode}
258
+
259
+ MODIFIED CODE:
260
+ {request.modifiedCode}
261
+
262
+ IMPORTANT: You MUST include numerical scores in your analysis. Use the exact format "Label: X/10" for each metric. Include ALL of the following scored sections:
263
+
264
+ TIME COMPLEXITY:
265
+ - Time Original: [score]/10 β€” [analysis]
266
+ - Time Modified: [score]/10 β€” [analysis]
267
+ - Best/Average/Worst case analysis
268
+
269
+ SPACE COMPLEXITY:
270
+ - Space Original: [score]/10 β€” [analysis]
271
+ - Space Modified: [score]/10 β€” [analysis]
272
+
273
+ EXECUTION SPEED:
274
+ - Speed Original: [score]/10 β€” [assessment]
275
+ - Speed Modified: [score]/10 β€” [assessment]
276
+ - Bottlenecks identified
277
+
278
+ CODE READABILITY:
279
+ - Readability Original: [score]/10 β€” [assessment]
280
+ - Readability Modified: [score]/10 β€” [assessment]
281
+
282
+ MAINTAINABILITY:
283
+ - Maintainability Original: [score]/10 β€” [assessment]
284
+ - Maintainability Modified: [score]/10 β€” [assessment]
285
+
286
+ BEST PRACTICES:
287
+ - Practices Original: [score]/10 β€” [compliance]
288
+ - Practices Modified: [score]/10 β€” [compliance]
289
+
290
+ SUMMARY: [Overall comparative summary]
291
+
292
+ OVERALL EFFICIENCY SCORE: [score]/10 β€” [brief justification]
293
+
294
+ Higher scores = better efficiency. Be precise with your /10 ratings."""
295
+
296
+ try:
297
+ result = run_ai_task(analysis_prompt)
298
+ return {"success": True, "message": "Analysis complete!", "analysisResult": result}
299
+ except Exception:
300
+ logger.exception("Error analyzing code.")
301
+ return {"success": False, "message": "Error analyzing code. Please try again later."}
302
+
303
+
304
+ if FRONTEND_BUILD_DIR.exists():
305
+ app.mount("/assets", StaticFiles(directory=FRONTEND_BUILD_DIR / "static"), name="static")
306
+
307
+
308
+ @app.get("/{full_path:path}")
309
+ async def serve_frontend(full_path: str):
310
+ if not FRONTEND_BUILD_DIR.exists():
311
+ raise HTTPException(
312
+ status_code=404,
313
+ detail="Frontend build not found. Run the React build or deploy with the Dockerfile.",
314
+ )
315
+
316
+ candidate = FRONTEND_BUILD_DIR / full_path
317
+ if full_path and candidate.exists() and candidate.is_file():
318
+ return FileResponse(candidate)
319
+ return FileResponse(FRONTEND_BUILD_DIR / "index.html")
320
+
321
 
322
  if __name__ == "__main__":
323
  import uvicorn
324
+
325
+ uvicorn.run(app, host="0.0.0.0", port=int(os.getenv("PORT", "7860")))