girish00 commited on
Commit
07a91a1
·
verified ·
1 Parent(s): dfbac4e

Upload folder using huggingface_hub

Browse files
Files changed (48) hide show
  1. .env +11 -0
  2. .env.example +11 -0
  3. Dockerfile +12 -0
  4. Makefile +22 -0
  5. README.md +192 -12
  6. README_hf_space.md +14 -0
  7. __pycache__/app.cpython-312.pyc.2197825028720 +0 -0
  8. __pycache__/upload_to_hf.cpython-312.pyc.2197825028720 +0 -0
  9. api/__init__.py +1 -0
  10. api/__pycache__/__init__.cpython-312.pyc.2197825028720 +0 -0
  11. api/__pycache__/main.cpython-312.pyc.2197825028720 +0 -0
  12. api/main.py +44 -0
  13. api/security.py +33 -0
  14. app.py +45 -0
  15. client_example.py +29 -0
  16. data/sample_snippets.json +11 -0
  17. docker-compose.yml +11 -0
  18. instruction.md +187 -0
  19. requirements.txt +16 -0
  20. run_api.bat +6 -0
  21. run_space.bat +6 -0
  22. run_tasks.bat +2 -0
  23. smoke_test.py +112 -0
  24. specification_file.md +159 -0
  25. src/__init__.py +6 -0
  26. src/__pycache__/__init__.cpython-312.pyc.2197825028720 +0 -0
  27. src/__pycache__/config.cpython-312.pyc.2197825028720 +0 -0
  28. src/__pycache__/generator.cpython-312.pyc.2197825028720 +0 -0
  29. src/__pycache__/hallucination.cpython-312.pyc.2197822599920 +0 -0
  30. src/__pycache__/lora_prepare.cpython-312.pyc.2197825028720 +0 -0
  31. src/__pycache__/model_loader.cpython-312.pyc.2197825028720 +0 -0
  32. src/__pycache__/pipeline.cpython-312.pyc.2197825028720 +0 -0
  33. src/__pycache__/prompts.cpython-312.pyc.2197825028720 +0 -0
  34. src/__pycache__/rag.cpython-312.pyc.2197825028720 +0 -0
  35. src/__pycache__/relevancy.cpython-312.pyc.2197825028720 +0 -0
  36. src/__pycache__/schemas.cpython-312.pyc.2197825028720 +0 -0
  37. src/config.py +28 -0
  38. src/generator.py +86 -0
  39. src/hallucination.py +58 -0
  40. src/lora_prepare.py +18 -0
  41. src/model_loader.py +64 -0
  42. src/pipeline.py +58 -0
  43. src/prompts.py +19 -0
  44. src/rag.py +57 -0
  45. src/relevancy.py +17 -0
  46. src/schemas.py +18 -0
  47. tasks.py +111 -0
  48. upload_to_hf.py +40 -0
.env ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MODEL_NAME=Qwen/Qwen2.5-Coder-1.5B-Instruct
2
+ FALLBACK_MODEL_NAME=Qwen/Qwen2.5-Coder-0.5B-Instruct
3
+ FINAL_FALLBACK_MODEL_NAME=sshleifer/tiny-gpt2
4
+ EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2
5
+ MAX_NEW_TOKENS=256
6
+ TEMPERATURE=0.2
7
+ TOP_P=0.95
8
+ USE_RAG=true
9
+ FORCE_MOCK_MODE=false
10
+ API_KEY=gk_zxcvbnmlkjhgfdsaq1w2e3r4t5y6u7i8o9p0
11
+ RATE_LIMIT_PER_MINUTE=30
.env.example ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MODEL_NAME=Qwen/Qwen2.5-Coder-1.5B-Instruct
2
+ FALLBACK_MODEL_NAME=Qwen/Qwen2.5-Coder-0.5B-Instruct
3
+ FINAL_FALLBACK_MODEL_NAME=sshleifer/tiny-gpt2
4
+ EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2
5
+ MAX_NEW_TOKENS=256
6
+ TEMPERATURE=0.2
7
+ TOP_P=0.95
8
+ USE_RAG=true
9
+ FORCE_MOCK_MODE=false
10
+ API_KEY=change-me
11
+ RATE_LIMIT_PER_MINUTE=30
Dockerfile ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY requirements.txt /app/requirements.txt
6
+ RUN pip install --no-cache-dir -r /app/requirements.txt
7
+
8
+ COPY . /app
9
+
10
+ EXPOSE 8000
11
+
12
+ CMD ["uvicorn", "api.main:app", "--host", "0.0.0.0", "--port", "8000"]
Makefile ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .PHONY: install run space smoke compile docker-up docker-down
2
+
3
+ install:
4
+ python tasks.py install
5
+
6
+ run:
7
+ python tasks.py run
8
+
9
+ space:
10
+ python tasks.py space
11
+
12
+ smoke:
13
+ python tasks.py smoke
14
+
15
+ compile:
16
+ python tasks.py compile
17
+
18
+ docker-up:
19
+ python tasks.py docker-up
20
+
21
+ docker-down:
22
+ python tasks.py docker-down
README.md CHANGED
@@ -1,12 +1,192 @@
1
- ---
2
- title: Coding Llm Space
3
- emoji: 📉
4
- colorFrom: indigo
5
- colorTo: red
6
- sdk: gradio
7
- sdk_version: 6.12.0
8
- app_file: app.py
9
- pinned: false
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Advanced Coding LLM (Production-Ready Starter)
2
+
3
+ This project provides a deployable coding assistant API built on a free Hugging Face coding model.
4
+
5
+ ## Model Strategy
6
+
7
+ - Primary model: `Qwen/Qwen2.5-Coder-1.5B-Instruct` (free/open on Hugging Face).
8
+ - Fallback model: `Qwen/Qwen2.5-Coder-0.5B-Instruct` if primary load fails.
9
+ - Final emergency fallback: `sshleifer/tiny-gpt2` (for guaranteed startup).
10
+ - No heavy training required.
11
+ - LoRA-ready architecture included in `src/lora_prepare.py`.
12
+
13
+ ## Features
14
+
15
+ - Code generation
16
+ - Debugging / buggy code fixing
17
+ - Code explanation
18
+ - Instruction following
19
+ - Confidence estimation (from token probabilities)
20
+ - Important token extraction (low-confidence tokens)
21
+ - Relevancy score (embedding cosine similarity)
22
+ - Hallucination checks:
23
+ - Syntax validation
24
+ - Runtime smoke test
25
+ - Optional RAG with FAISS from `data/sample_snippets.json`
26
+
27
+ ## Project Structure
28
+
29
+ ```text
30
+ coding-llm/
31
+ │── data/
32
+ │── src/
33
+ │── api/
34
+ │── requirements.txt
35
+ │── README.md
36
+ ```
37
+
38
+ ## API Output Format
39
+
40
+ `POST /generate` returns:
41
+
42
+ ```json
43
+ {
44
+ "code": "...",
45
+ "explanation": "...",
46
+ "confidence": 0.0,
47
+ "important_tokens": ["..."],
48
+ "relevancy_score": 0.0,
49
+ "hallucination": false,
50
+ "latency_ms": 0
51
+ }
52
+ ```
53
+
54
+ If hallucination is detected, the reason is appended inside `explanation`.
55
+
56
+ ## Local Run
57
+
58
+ 1. Create environment and install:
59
+
60
+ ```bash
61
+ pip install -r requirements.txt
62
+ ```
63
+
64
+ Optional: create `.env` from `.env.example` and set values:
65
+
66
+ ```bash
67
+ copy .env.example .env
68
+ ```
69
+
70
+ 2. Run API:
71
+
72
+ ```bash
73
+ uvicorn api.main:app --host 0.0.0.0 --port 8000
74
+ ```
75
+
76
+ 3. Test request:
77
+
78
+ ```bash
79
+ curl -X POST "http://127.0.0.1:8000/generate" ^
80
+ -H "Content-Type: application/json" ^
81
+ -d "{\"instruction\":\"Fix this Python function\",\"input\":\"def add(a,b) return a+b\"}"
82
+ ```
83
+
84
+ 4. Optional client:
85
+
86
+ ```bash
87
+ python client_example.py
88
+ ```
89
+
90
+ ## Hugging Face Deployment (Space)
91
+
92
+ This repo includes:
93
+
94
+ - `app.py` (Gradio app for HF Space)
95
+ - `upload_to_hf.py` (upload helper script)
96
+ - `README_hf_space.md` (Space metadata template)
97
+
98
+ Steps:
99
+
100
+ 1. Create a HF access token with write permission.
101
+ 2. Run:
102
+
103
+ ```bash
104
+ python upload_to_hf.py --repo-id <your-username/coding-llm-space> --token <HF_TOKEN>
105
+ ```
106
+
107
+ 3. Your Space launches with public UI and can be called by Hugging Face API key.
108
+
109
+ ## Security and Ops
110
+
111
+ - API key auth enabled when `API_KEY` is set.
112
+ - In-memory per-IP rate limiting via `RATE_LIMIT_PER_MINUTE`.
113
+ - Dockerized API included (`Dockerfile`).
114
+ - Model is loaded lazily on first `/generate` request (faster boot, fewer startup failures).
115
+ - Set `FORCE_MOCK_MODE=true` to run instantly without downloading models.
116
+ - Windows quick-start scripts:
117
+ - `run_api.bat`
118
+ - `run_space.bat`
119
+
120
+ ## Docker Compose
121
+
122
+ ```bash
123
+ copy .env.example .env
124
+ docker compose up --build
125
+ ```
126
+
127
+ API is available at `http://127.0.0.1:8000`.
128
+
129
+ ## Automated Smoke Test
130
+
131
+ Run this after API starts:
132
+
133
+ ```bash
134
+ python smoke_test.py
135
+ ```
136
+
137
+ This validates:
138
+ - `GET /health`
139
+ - `POST /generate`
140
+ - required JSON output keys
141
+
142
+ ## One-command Task Runner
143
+
144
+ Cross-platform:
145
+
146
+ ```bash
147
+ python tasks.py install
148
+ python tasks.py run
149
+ python tasks.py smoke
150
+ python tasks.py serve-smoke
151
+ python tasks.py docker-up
152
+ python tasks.py docker-down
153
+ python tasks.py hf-upload --repo-id <user/space-name> --token <HF_TOKEN>
154
+ ```
155
+
156
+ Windows shortcut:
157
+
158
+ ```bat
159
+ run_tasks.bat install
160
+ run_tasks.bat run
161
+ run_tasks.bat smoke
162
+ run_tasks.bat serve-smoke
163
+ ```
164
+
165
+ Makefile (Linux/macOS/WSL):
166
+
167
+ ```bash
168
+ make install
169
+ make run
170
+ make smoke
171
+ make docker-up
172
+ ```
173
+
174
+ ## FastAPI Endpoint
175
+
176
+ ### `POST /generate`
177
+
178
+ Input JSON:
179
+
180
+ ```json
181
+ {
182
+ "instruction": "Explain this code and improve it",
183
+ "input": "def f(x): return x*x"
184
+ }
185
+ ```
186
+
187
+ ## Notes for Production
188
+
189
+ - Keep `max_new_tokens` modest for low latency.
190
+ - Add request auth/rate limiting before exposing public endpoint.
191
+ - For stronger quality, add curated retrieval corpus in `data/`.
192
+ - For robust hallucination checks, extend tests per language/framework.
README_hf_space.md ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Advanced Coding LLM
3
+ emoji: 🤖
4
+ colorFrom: indigo
5
+ colorTo: blue
6
+ sdk: gradio
7
+ sdk_version: 4.44.0
8
+ app_file: app.py
9
+ pinned: false
10
+ ---
11
+
12
+ # Hugging Face Space Metadata
13
+
14
+ This file can be copied to your Space `README.md` to configure Space UI metadata.
__pycache__/app.cpython-312.pyc.2197825028720 ADDED
Binary file (1.34 kB). View file
 
__pycache__/upload_to_hf.cpython-312.pyc.2197825028720 ADDED
Binary file (2.01 kB). View file
 
api/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ """API package."""
api/__pycache__/__init__.cpython-312.pyc.2197825028720 ADDED
Binary file (150 Bytes). View file
 
api/__pycache__/main.cpython-312.pyc.2197825028720 ADDED
Binary file (1.21 kB). View file
 
api/main.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """FastAPI entrypoint for coding model."""
2
+
3
+ from fastapi import Depends, FastAPI, Request
4
+
5
+ from api.security import check_rate_limit, verify_api_key
6
+ from src.pipeline import CodingLLMPipeline
7
+ from src.schemas import GenerateRequest, GenerateResponse
8
+
9
+ app = FastAPI(title="Advanced Coding LLM API", version="1.0.0")
10
+ pipeline = CodingLLMPipeline()
11
+
12
+
13
+ @app.get("/health")
14
+ def health():
15
+ model_name = pipeline.model_bundle.active_model_name if pipeline.model_bundle else "not_loaded_yet"
16
+ return {"status": "ok", "model": model_name}
17
+
18
+
19
+ @app.post("/generate", response_model=GenerateResponse)
20
+ def generate(
21
+ payload: GenerateRequest,
22
+ request: Request,
23
+ _: None = Depends(verify_api_key),
24
+ ):
25
+ client_host = request.client.host if request.client else "unknown"
26
+ check_rate_limit(client_host)
27
+ try:
28
+ result = pipeline.run(payload.instruction, payload.input)
29
+ return GenerateResponse(**result)
30
+ except Exception as exc:
31
+ # Keep response shape consistent with GenerateResponse model.
32
+ return GenerateResponse(
33
+ code="",
34
+ explanation=(
35
+ "Generation failed.\n\n"
36
+ f"Error: {exc}\n"
37
+ "Hint: Check model name/access and internet connection for first model download."
38
+ ),
39
+ confidence=0.0,
40
+ important_tokens=[],
41
+ relevancy_score=0.0,
42
+ hallucination=True,
43
+ latency_ms=0,
44
+ )
api/security.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Simple API key and in-memory rate limiting."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import time
6
+ from collections import defaultdict, deque
7
+
8
+ from fastapi import Header, HTTPException
9
+
10
+ from src.config import settings
11
+
12
+ _request_windows: dict[str, deque[float]] = defaultdict(deque)
13
+
14
+
15
+ def verify_api_key(x_api_key: str = Header(default="")):
16
+ """Enforce API key when configured."""
17
+ if settings.api_key and x_api_key != settings.api_key:
18
+ raise HTTPException(status_code=401, detail="Invalid API key")
19
+
20
+
21
+ def check_rate_limit(client_id: str) -> None:
22
+ """Allow at most N requests per minute for each client id."""
23
+ now = time.time()
24
+ window_start = now - 60
25
+ q = _request_windows[client_id]
26
+
27
+ while q and q[0] < window_start:
28
+ q.popleft()
29
+
30
+ if len(q) >= settings.rate_limit_per_minute:
31
+ raise HTTPException(status_code=429, detail="Rate limit exceeded")
32
+
33
+ q.append(now)
app.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Gradio app for Hugging Face Spaces deployment."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+
7
+ import gradio as gr
8
+
9
+ from src.pipeline import CodingLLMPipeline
10
+
11
+ pipeline = CodingLLMPipeline()
12
+
13
+
14
+ def generate(instruction: str, user_input: str):
15
+ try:
16
+ result = pipeline.run(instruction, user_input)
17
+ return json.dumps(result, indent=2)
18
+ except Exception as exc:
19
+ return json.dumps(
20
+ {
21
+ "code": "",
22
+ "explanation": f"Generation failed: {exc}",
23
+ "confidence": 0.0,
24
+ "important_tokens": [],
25
+ "relevancy_score": 0.0,
26
+ "hallucination": True,
27
+ "latency_ms": 0,
28
+ },
29
+ indent=2,
30
+ )
31
+
32
+
33
+ demo = gr.Interface(
34
+ fn=generate,
35
+ inputs=[
36
+ gr.Textbox(label="Instruction", placeholder="Explain, generate or debug code..."),
37
+ gr.Textbox(label="Input", lines=12, placeholder="Paste code or context"),
38
+ ],
39
+ outputs=gr.Code(label="JSON Output", language="json"),
40
+ title="Advanced Coding LLM",
41
+ description="Fast coding assistant with confidence, relevancy, and hallucination checks.",
42
+ )
43
+
44
+ if __name__ == "__main__":
45
+ demo.launch()
client_example.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Small client example to call POST /generate."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+
8
+ import requests
9
+ from dotenv import load_dotenv
10
+
11
+ load_dotenv()
12
+
13
+ BASE_URL = os.getenv("CODING_LLM_URL", "http://127.0.0.1:8000")
14
+ API_KEY = os.getenv("API_KEY", "")
15
+
16
+ payload = {
17
+ "instruction": "Fix this function and explain",
18
+ "input": "def add(a,b) return a+b",
19
+ }
20
+
21
+ headers = {"Content-Type": "application/json"}
22
+ if API_KEY:
23
+ headers["x-api-key"] = API_KEY
24
+
25
+ resp = requests.post(f"{BASE_URL}/generate", headers=headers, json=payload, timeout=60)
26
+ if resp.status_code == 401:
27
+ raise PermissionError("Unauthorized (401). Ensure API_KEY matches server configuration.")
28
+ resp.raise_for_status()
29
+ print(json.dumps(resp.json(), indent=2))
data/sample_snippets.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "snippet": "def binary_search(arr, target):\n lo, hi = 0, len(arr) - 1\n while lo <= hi:\n mid = (lo + hi) // 2\n if arr[mid] == target:\n return mid\n if arr[mid] < target:\n lo = mid + 1\n else:\n hi = mid - 1\n return -1"
4
+ },
5
+ {
6
+ "snippet": "def is_palindrome(s):\n clean = ''.join(ch.lower() for ch in s if ch.isalnum())\n return clean == clean[::-1]"
7
+ },
8
+ {
9
+ "snippet": "def safe_div(a, b):\n if b == 0:\n raise ValueError('Division by zero')\n return a / b"
10
+ }
11
+ ]
docker-compose.yml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: "3.9"
2
+
3
+ services:
4
+ coding-llm-api:
5
+ build: .
6
+ container_name: coding-llm-api
7
+ env_file:
8
+ - .env
9
+ ports:
10
+ - "8000:8000"
11
+ restart: unless-stopped
instruction.md ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Advanced Coding LLM - Complete Instructions
2
+
3
+ This document provides full setup, run, validation, optimization, and deployment steps for the `coding-llm` project.
4
+
5
+ ## 1) Prerequisites
6
+
7
+ - Python 3.10+ (recommended 3.11/3.12)
8
+ - Git
9
+ - Internet access for first model download
10
+ - Optional: Docker Desktop
11
+ - Optional: Hugging Face account and access token
12
+
13
+ ## 2) Project Setup
14
+
15
+ From project root:
16
+
17
+ ```bash
18
+ cd "C:\Users\GIRISH\OneDrive\Desktop\AI model_14_04_26\coding-llm"
19
+ ```
20
+
21
+ Create environment file:
22
+
23
+ ```bash
24
+ copy .env.example .env
25
+ ```
26
+
27
+ Install dependencies:
28
+
29
+ ```bash
30
+ python tasks.py install
31
+ ```
32
+
33
+ ## 3) Configure `.env`
34
+
35
+ Open `.env` and set values:
36
+
37
+ - `MODEL_NAME=Qwen/Qwen2.5-Coder-1.5B-Instruct`
38
+ - `FALLBACK_MODEL_NAME=Qwen/Qwen2.5-Coder-0.5B-Instruct`
39
+ - `FINAL_FALLBACK_MODEL_NAME=sshleifer/tiny-gpt2` (optional emergency fallback)
40
+ - `FORCE_MOCK_MODE=false` (true for instant test mode)
41
+ - `API_KEY=<your_secret_key>`
42
+ - `RATE_LIMIT_PER_MINUTE=30`
43
+ - `USE_RAG=true`
44
+
45
+ ## 4) Run API Locally
46
+
47
+ ```bash
48
+ python tasks.py run
49
+ ```
50
+
51
+ Server runs at:
52
+
53
+ - `http://127.0.0.1:8000`
54
+
55
+ Health endpoint:
56
+
57
+ - `GET http://127.0.0.1:8000/health`
58
+
59
+ ## 5) Run Smoke Tests
60
+
61
+ ### Full smoke test
62
+
63
+ ```bash
64
+ python smoke_test.py
65
+ ```
66
+
67
+ ### Health-only smoke test
68
+
69
+ ```bash
70
+ set SMOKE_SKIP_GENERATE=true
71
+ python smoke_test.py
72
+ ```
73
+
74
+ ### Combined run-and-test command
75
+
76
+ ```bash
77
+ python tasks.py serve-smoke
78
+ ```
79
+
80
+ This starts server, executes smoke test, and shuts server down automatically.
81
+
82
+ ## 6) If Generation Is Slow on First Run
83
+
84
+ First `/generate` may take long due to model download/warmup.
85
+
86
+ Options:
87
+
88
+ - Increase timeout:
89
+ - `set SMOKE_TIMEOUT=900`
90
+ - Use mock mode for quick validation:
91
+ - set `FORCE_MOCK_MODE=true`
92
+ - Run full mode after model cache is ready.
93
+
94
+ ## 7) API Usage
95
+
96
+ ### Endpoint
97
+
98
+ - `POST /generate`
99
+
100
+ ### Input JSON
101
+
102
+ ```json
103
+ {
104
+ "instruction": "Fix this code",
105
+ "input": "def add(a,b) return a+b"
106
+ }
107
+ ```
108
+
109
+ ### Required Header (if API key enabled)
110
+
111
+ - `x-api-key: <API_KEY>`
112
+
113
+ ### Output JSON
114
+
115
+ ```json
116
+ {
117
+ "code": "...",
118
+ "explanation": "...",
119
+ "confidence": 0.0,
120
+ "important_tokens": ["..."],
121
+ "relevancy_score": 0.0,
122
+ "hallucination": false,
123
+ "latency_ms": 0
124
+ }
125
+ ```
126
+
127
+ ## 8) Docker Deployment
128
+
129
+ ```bash
130
+ copy .env.example .env
131
+ docker compose up --build -d
132
+ ```
133
+
134
+ Validate:
135
+
136
+ ```bash
137
+ python smoke_test.py
138
+ ```
139
+
140
+ Stop:
141
+
142
+ ```bash
143
+ docker compose down
144
+ ```
145
+
146
+ ## 9) Hugging Face Space Deployment
147
+
148
+ Create HF token (write permission), then:
149
+
150
+ ```bash
151
+ python tasks.py hf-upload --repo-id <username/coding-llm-space> --token <HF_TOKEN>
152
+ ```
153
+
154
+ After upload, configure Space variables/secrets:
155
+
156
+ - `MODEL_NAME`
157
+ - `FALLBACK_MODEL_NAME`
158
+ - `FORCE_MOCK_MODE`
159
+ - `API_KEY` (if needed in your architecture)
160
+
161
+ ## 10) Production Hardening Checklist
162
+
163
+ - Keep `API_KEY` enabled
164
+ - Keep rate limiting enabled (`RATE_LIMIT_PER_MINUTE`)
165
+ - Put API behind HTTPS reverse proxy
166
+ - Add logging and monitoring
167
+ - Pin model versions if strict reproducibility required
168
+ - Use `FORCE_MOCK_MODE=false` in production
169
+
170
+ ## 11) Common Troubleshooting
171
+
172
+ - `WinError 10061`:
173
+ - API server is not running. Start with `python tasks.py run`.
174
+ - `401 Unauthorized`:
175
+ - `x-api-key` does not match server `API_KEY`.
176
+ - Health works but generate times out:
177
+ - model is still downloading/warming up.
178
+ - Low-quality gibberish output:
179
+ - likely fallback model path used; verify `.env` model names.
180
+
181
+ ## 12) Recommended Daily Commands
182
+
183
+ - Install/update: `python tasks.py install`
184
+ - Run API: `python tasks.py run`
185
+ - Smoke: `python tasks.py smoke`
186
+ - Run+smoke: `python tasks.py serve-smoke`
187
+ - Docker up/down: `python tasks.py docker-up` / `python tasks.py docker-down`
requirements.txt ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ fastapi>=0.115.0
2
+ uvicorn>=0.30.0
3
+ transformers>=4.44.0
4
+ torch>=2.3.0
5
+ accelerate>=0.33.0
6
+ sentence-transformers>=3.0.0
7
+ faiss-cpu>=1.8.0
8
+ numpy>=1.26.0
9
+ scikit-learn>=1.5.0
10
+ pydantic>=2.8.0
11
+ huggingface-hub>=0.24.0
12
+ gradio>=4.40.0
13
+ datasets>=2.20.0
14
+ peft>=0.12.0
15
+ requests>=2.32.0
16
+ python-dotenv>=1.0.1
run_api.bat ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ @echo off
2
+ setlocal
3
+ if exist .env (
4
+ for /f "usebackq delims=" %%a in (".env") do set "%%a"
5
+ )
6
+ uvicorn api.main:app --host 0.0.0.0 --port 8000
run_space.bat ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ @echo off
2
+ setlocal
3
+ if exist .env (
4
+ for /f "usebackq delims=" %%a in (".env") do set "%%a"
5
+ )
6
+ python app.py
run_tasks.bat ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ @echo off
2
+ python tasks.py %*
smoke_test.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Automated smoke test for /health and /generate endpoints."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ import sys
8
+ import time
9
+
10
+ import requests
11
+ from dotenv import load_dotenv
12
+
13
+ load_dotenv()
14
+
15
+ BASE_URL = os.getenv("CODING_LLM_URL", "http://127.0.0.1:8000")
16
+ API_KEY = os.getenv("API_KEY", "")
17
+ TIMEOUT = int(os.getenv("SMOKE_TIMEOUT", "300"))
18
+ SKIP_GENERATE = os.getenv("SMOKE_SKIP_GENERATE", "false").lower() == "true"
19
+
20
+
21
+ def _headers() -> dict[str, str]:
22
+ headers = {"Content-Type": "application/json"}
23
+ if API_KEY:
24
+ headers["x-api-key"] = API_KEY
25
+ return headers
26
+
27
+
28
+ def wait_for_health() -> dict:
29
+ candidate_urls = [BASE_URL]
30
+ if "127.0.0.1" in BASE_URL:
31
+ candidate_urls.append(BASE_URL.replace("127.0.0.1", "localhost"))
32
+ elif "localhost" in BASE_URL:
33
+ candidate_urls.append(BASE_URL.replace("localhost", "127.0.0.1"))
34
+
35
+ deadline = time.time() + TIMEOUT
36
+ last_errors: list[str] = []
37
+ while time.time() < deadline:
38
+ for url in candidate_urls:
39
+ try:
40
+ resp = requests.get(f"{url}/health", timeout=10)
41
+ if resp.status_code == 200:
42
+ return resp.json()
43
+ last_errors.append(f"{url}/health -> HTTP {resp.status_code}")
44
+ except requests.RequestException as exc:
45
+ last_errors.append(f"{url}/health -> {exc}")
46
+ time.sleep(2)
47
+ preview = "; ".join(last_errors[-5:]) if last_errors else "No response details captured."
48
+ raise TimeoutError(f"Health check timeout. API did not become ready. Recent errors: {preview}")
49
+
50
+
51
+ def test_generate() -> dict:
52
+ payload = {
53
+ "instruction": "Fix this function and explain briefly",
54
+ "input": "def add(a,b) return a+b",
55
+ }
56
+ resp = requests.post(
57
+ f"{BASE_URL}/generate",
58
+ headers=_headers(),
59
+ json=payload,
60
+ timeout=TIMEOUT,
61
+ )
62
+ if resp.status_code == 401:
63
+ raise PermissionError(
64
+ "Unauthorized (401). Set API_KEY in .env or environment before running smoke_test.py."
65
+ )
66
+ resp.raise_for_status()
67
+ body = resp.json()
68
+
69
+ required_keys = [
70
+ "code",
71
+ "explanation",
72
+ "confidence",
73
+ "important_tokens",
74
+ "relevancy_score",
75
+ "hallucination",
76
+ "latency_ms",
77
+ ]
78
+ missing = [k for k in required_keys if k not in body]
79
+ if missing:
80
+ raise ValueError(f"Missing keys in /generate response: {missing}")
81
+ return body
82
+
83
+
84
+ def main():
85
+ print(f"[smoke] waiting for {BASE_URL}/health ...")
86
+ health = wait_for_health()
87
+ print("[smoke] health ok:", json.dumps(health))
88
+
89
+ if SKIP_GENERATE:
90
+ print("[smoke] skipping /generate (SMOKE_SKIP_GENERATE=true)")
91
+ print("[smoke] SUCCESS")
92
+ return
93
+
94
+ print("[smoke] running /generate ... (first run may download model)")
95
+ result = test_generate()
96
+ print("[smoke] /generate ok")
97
+ print(json.dumps(result, indent=2)[:2000])
98
+ print("[smoke] SUCCESS")
99
+
100
+
101
+ if __name__ == "__main__":
102
+ try:
103
+ main()
104
+ except Exception as exc:
105
+ msg = str(exc)
106
+ if "Read timed out" in msg:
107
+ msg = (
108
+ f"{msg}\nHint: model warmup is still running. "
109
+ "Wait longer, increase SMOKE_TIMEOUT, or restart API with FORCE_MOCK_MODE=true for instant checks."
110
+ )
111
+ print(f"[smoke] FAILED: {msg}")
112
+ sys.exit(1)
specification_file.md ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Advanced Coding LLM - Technical Specification
2
+
3
+ ## 1) Objective
4
+
5
+ Build a production-ready coding assistant API deployable locally and on Hugging Face, supporting:
6
+
7
+ - Code generation
8
+ - Debugging/fixing buggy code
9
+ - Code explanation
10
+ - Instruction following
11
+ - Explainability signals
12
+ - Relevancy scoring
13
+ - Hallucination checks
14
+ - Optional RAG
15
+
16
+ ## 2) Core Functional Requirements
17
+
18
+ ### 2.1 Model
19
+
20
+ - Primary model: `Qwen/Qwen2.5-Coder-1.5B-Instruct`
21
+ - Fallback model: `Qwen/Qwen2.5-Coder-0.5B-Instruct`
22
+ - Emergency fallback mode supported (mock path available)
23
+ - Architecture compatible with future LoRA integration (`src/lora_prepare.py`)
24
+
25
+ ### 2.2 API
26
+
27
+ - Framework: FastAPI
28
+ - Endpoint: `POST /generate`
29
+ - Health: `GET /health`
30
+ - Input schema:
31
+ - `instruction: str`
32
+ - `input: str`
33
+ - Output schema:
34
+ - `code: str`
35
+ - `explanation: str`
36
+ - `confidence: float`
37
+ - `important_tokens: list[str]`
38
+ - `relevancy_score: float`
39
+ - `hallucination: bool`
40
+ - `latency_ms: int`
41
+
42
+ ### 2.3 Explainability
43
+
44
+ - Confidence from token probabilities over generated tokens
45
+ - Important tokens extracted from low-probability tokens
46
+
47
+ ### 2.4 Relevancy
48
+
49
+ - Query-to-output semantic score using TF-IDF + cosine similarity
50
+
51
+ ### 2.5 Hallucination Checks
52
+
53
+ - Python syntax validation (`ast.parse`)
54
+ - Runtime smoke execution for Python-like outputs
55
+ - Skip runtime execution for non-Python-like outputs
56
+
57
+ ### 2.6 RAG
58
+
59
+ - Basic retrieval from local snippets dataset
60
+ - FAISS index over normalized TF-IDF vectors
61
+ - Inject top-k snippets into prompt context
62
+
63
+ ## 3) Non-Functional Requirements
64
+
65
+ - Runnable on local workstation
66
+ - Supports no-training initial deployment
67
+ - Lazy-load model to reduce startup failures
68
+ - Graceful fallback response when model unavailable
69
+ - Windows-compatible developer workflow
70
+
71
+ ## 4) Security Requirements
72
+
73
+ - API key auth via `x-api-key` (if configured)
74
+ - Per-IP in-memory rate limiting
75
+ - No secrets committed to repository (`.env` ignored)
76
+
77
+ ## 5) Performance Requirements
78
+
79
+ - Lazy model initialization
80
+ - Runtime checks bounded by timeout
81
+ - Optional mock mode (`FORCE_MOCK_MODE=true`) for fast operational checks
82
+
83
+ ## 6) Deployment Requirements
84
+
85
+ ### Local
86
+
87
+ - `python tasks.py install`
88
+ - `python tasks.py run`
89
+
90
+ ### Docker
91
+
92
+ - `docker compose up --build -d`
93
+
94
+ ### Hugging Face Space
95
+
96
+ - `python tasks.py hf-upload --repo-id <id> --token <token>`
97
+ - Gradio entrypoint in `app.py`
98
+
99
+ ## 7) Project Structure
100
+
101
+ ```text
102
+ coding-llm/
103
+ │── data/
104
+ │── src/
105
+ │── api/
106
+ │── requirements.txt
107
+ │── README.md
108
+ │── instruction.md
109
+ │── specification_file.md
110
+ ```
111
+
112
+ ## 8) Module Responsibilities
113
+
114
+ - `api/main.py`: API routes and response wiring
115
+ - `api/security.py`: API key + rate limiting
116
+ - `src/config.py`: environment-driven settings
117
+ - `src/model_loader.py`: model/fallback loading
118
+ - `src/generator.py`: generation + confidence extraction
119
+ - `src/pipeline.py`: orchestration layer
120
+ - `src/rag.py`: snippet retrieval
121
+ - `src/relevancy.py`: relevancy score computation
122
+ - `src/hallucination.py`: syntax/runtime checks
123
+ - `src/lora_prepare.py`: LoRA adapter hook
124
+ - `app.py`: Gradio UI for HF Spaces
125
+ - `upload_to_hf.py`: HF deployment uploader
126
+ - `tasks.py`: command runner
127
+ - `smoke_test.py`: runtime integration validation
128
+
129
+ ## 9) Operational Modes
130
+
131
+ - **Real Model Mode**
132
+ - `FORCE_MOCK_MODE=false`
133
+ - Uses HF model loading and generation
134
+ - **Mock Mode**
135
+ - `FORCE_MOCK_MODE=true`
136
+ - Returns deterministic fallback output for reliability testing
137
+
138
+ ## 10) Validation and QA
139
+
140
+ - Static compile check with `python -m compileall`
141
+ - Lint diagnostics via editor/tooling
142
+ - Smoke checks:
143
+ - health endpoint reachable
144
+ - generate endpoint returns full schema
145
+
146
+ ## 11) Known Constraints
147
+
148
+ - First generation may be slow due to model download/warmup
149
+ - Quality depends on available model and decoding configuration
150
+ - In-memory rate limiter is single-process only
151
+
152
+ ## 12) Future Enhancements
153
+
154
+ - Redis-backed distributed rate limiting
155
+ - Better language-aware hallucination tests
156
+ - Prompt templates per task type
157
+ - Streaming token responses
158
+ - Persistent vector store (Chroma/FAISS on-disk)
159
+ - CI/CD workflow for automated deploy/test
src/__init__.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ """Source package for coding-llm."""
2
+
3
+ import os
4
+
5
+ # Avoid TensorFlow/Keras import path conflicts in mixed Python environments.
6
+ os.environ.setdefault("TRANSFORMERS_NO_TF", "1")
src/__pycache__/__init__.cpython-312.pyc.2197825028720 ADDED
Binary file (168 Bytes). View file
 
src/__pycache__/config.cpython-312.pyc.2197825028720 ADDED
Binary file (1.02 kB). View file
 
src/__pycache__/generator.cpython-312.pyc.2197825028720 ADDED
Binary file (3.11 kB). View file
 
src/__pycache__/hallucination.cpython-312.pyc.2197822599920 ADDED
Binary file (2.66 kB). View file
 
src/__pycache__/lora_prepare.cpython-312.pyc.2197825028720 ADDED
Binary file (708 Bytes). View file
 
src/__pycache__/model_loader.cpython-312.pyc.2197825028720 ADDED
Binary file (1.69 kB). View file
 
src/__pycache__/pipeline.cpython-312.pyc.2197825028720 ADDED
Binary file (2.81 kB). View file
 
src/__pycache__/prompts.cpython-312.pyc.2197825028720 ADDED
Binary file (878 Bytes). View file
 
src/__pycache__/rag.cpython-312.pyc.2197825028720 ADDED
Binary file (3.2 kB). View file
 
src/__pycache__/relevancy.cpython-312.pyc.2197825028720 ADDED
Binary file (1.41 kB). View file
 
src/__pycache__/schemas.cpython-312.pyc.2197825028720 ADDED
Binary file (1.04 kB). View file
 
src/config.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Project-level configuration values."""
2
+
3
+ from dataclasses import dataclass
4
+ import os
5
+
6
+ from dotenv import load_dotenv
7
+
8
+ load_dotenv()
9
+
10
+
11
+ @dataclass(frozen=True)
12
+ class Settings:
13
+ """Central settings object for model and runtime controls."""
14
+
15
+ model_name: str = os.getenv("MODEL_NAME", "Qwen/Qwen2.5-Coder-1.5B-Instruct")
16
+ fallback_model_name: str = os.getenv("FALLBACK_MODEL_NAME", "Qwen/Qwen2.5-Coder-0.5B-Instruct")
17
+ final_fallback_model_name: str = os.getenv("FINAL_FALLBACK_MODEL_NAME", "sshleifer/tiny-gpt2")
18
+ embedding_model: str = os.getenv("EMBEDDING_MODEL", "sentence-transformers/all-MiniLM-L6-v2")
19
+ max_new_tokens: int = int(os.getenv("MAX_NEW_TOKENS", "256"))
20
+ temperature: float = float(os.getenv("TEMPERATURE", "0.2"))
21
+ top_p: float = float(os.getenv("TOP_P", "0.95"))
22
+ use_rag: bool = os.getenv("USE_RAG", "true").lower() == "true"
23
+ force_mock_mode: bool = os.getenv("FORCE_MOCK_MODE", "false").lower() == "true"
24
+ api_key: str = os.getenv("API_KEY", "")
25
+ rate_limit_per_minute: int = int(os.getenv("RATE_LIMIT_PER_MINUTE", "30"))
26
+
27
+
28
+ settings = Settings()
src/generator.py ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Text generation and output parsing."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from typing import List, Tuple
7
+
8
+ from torch.nn.functional import softmax
9
+
10
+ from src.config import settings
11
+
12
+
13
+ @dataclass
14
+ class GenerationResult:
15
+ code: str
16
+ explanation: str
17
+ confidence: float
18
+ important_tokens: List[str]
19
+
20
+
21
+ def _split_code_and_explanation(text: str) -> Tuple[str, str]:
22
+ marker = "Explanation:"
23
+ if marker in text:
24
+ code, explanation = text.split(marker, 1)
25
+ return code.strip(), explanation.strip()
26
+ return text.strip(), "Model did not provide explicit explanation."
27
+
28
+
29
+ def generate_response(model_bundle, prompt: str) -> GenerationResult:
30
+ """Generate model response with token-level confidence signals."""
31
+ if getattr(model_bundle, "is_mock", False):
32
+ # Keep API runnable even when model download/loading is unavailable.
33
+ fallback_code = (
34
+ "def solve_task(input_data):\n"
35
+ " \"\"\"Fallback implementation when model is unavailable.\"\"\"\n"
36
+ " return input_data\n"
37
+ )
38
+ fallback_explanation = (
39
+ "Running in mock fallback mode because no pretrained model could be loaded. "
40
+ "Set MODEL_NAME/FALLBACK_MODEL_NAME and ensure network/model access."
41
+ )
42
+ load_error = getattr(model_bundle, "load_error", "")
43
+ if load_error:
44
+ fallback_explanation = f"{fallback_explanation}\n\nLoader error: {load_error}"
45
+ return GenerationResult(
46
+ code=fallback_code,
47
+ explanation=fallback_explanation,
48
+ confidence=0.15,
49
+ important_tokens=["<mock-fallback>"],
50
+ )
51
+
52
+ tokenizer = model_bundle.tokenizer
53
+ model = model_bundle.model
54
+
55
+ inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
56
+ outputs = model.generate(
57
+ **inputs,
58
+ max_new_tokens=settings.max_new_tokens,
59
+ temperature=settings.temperature,
60
+ top_p=settings.top_p,
61
+ do_sample=True,
62
+ return_dict_in_generate=True,
63
+ output_scores=True,
64
+ )
65
+
66
+ generated_ids = outputs.sequences[0][inputs["input_ids"].shape[1] :]
67
+ generated_text = tokenizer.decode(generated_ids, skip_special_tokens=True)
68
+
69
+ token_probs = []
70
+ important = []
71
+ for step_scores, token_id in zip(outputs.scores, generated_ids):
72
+ probs = softmax(step_scores[0], dim=-1)
73
+ p = probs[token_id].item()
74
+ token_probs.append(p)
75
+ if p < 0.30:
76
+ important.append(tokenizer.decode([token_id]))
77
+
78
+ confidence = float(sum(token_probs) / max(len(token_probs), 1))
79
+ code, explanation = _split_code_and_explanation(generated_text)
80
+
81
+ return GenerationResult(
82
+ code=code,
83
+ explanation=explanation,
84
+ confidence=confidence,
85
+ important_tokens=important[:20],
86
+ )
src/hallucination.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Simple hallucination checks for generated code."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import ast
6
+ import subprocess
7
+ import sys
8
+ import tempfile
9
+ from dataclasses import dataclass
10
+
11
+
12
+ @dataclass
13
+ class HallucinationCheckResult:
14
+ hallucination: bool
15
+ reason: str
16
+
17
+
18
+ def _python_syntax_check(code: str) -> HallucinationCheckResult:
19
+ try:
20
+ ast.parse(code)
21
+ return HallucinationCheckResult(False, "Syntax is valid.")
22
+ except SyntaxError as exc:
23
+ return HallucinationCheckResult(True, f"Syntax error: {exc}")
24
+
25
+
26
+ def _python_runtime_smoke_test(code: str) -> HallucinationCheckResult:
27
+ try:
28
+ with tempfile.NamedTemporaryFile("w", suffix=".py", delete=False, encoding="utf-8") as tmp:
29
+ tmp.write(code)
30
+ tmp_path = tmp.name
31
+ proc = subprocess.run(
32
+ [sys.executable, tmp_path],
33
+ capture_output=True,
34
+ text=True,
35
+ timeout=4,
36
+ check=False,
37
+ )
38
+ if proc.returncode != 0:
39
+ stderr = proc.stderr.strip()[:300]
40
+ return HallucinationCheckResult(True, f"Runtime test failed: {stderr}")
41
+ return HallucinationCheckResult(False, "Runtime smoke test passed.")
42
+ except subprocess.TimeoutExpired:
43
+ return HallucinationCheckResult(True, "Runtime test timed out.")
44
+ except Exception as exc: # pragma: no cover
45
+ return HallucinationCheckResult(True, f"Runtime test error: {exc}")
46
+
47
+
48
+ def hallucination_check(code: str) -> HallucinationCheckResult:
49
+ """Run syntax and runtime checks and consolidate status."""
50
+ # Skip expensive/irrelevant execution for obviously non-Python snippets.
51
+ python_hints = ("def ", "class ", "import ", "from ", "print(", "if __name__")
52
+ if not any(hint in code for hint in python_hints):
53
+ return HallucinationCheckResult(False, "Skipped runtime check for non-Python-like output.")
54
+
55
+ syntax_result = _python_syntax_check(code)
56
+ if syntax_result.hallucination:
57
+ return syntax_result
58
+ return _python_runtime_smoke_test(code)
src/lora_prepare.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Starter utilities for future LoRA fine-tuning."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from peft import LoraConfig, get_peft_model
6
+
7
+
8
+ def attach_lora(model):
9
+ """Attach a default LoRA adapter to a loaded model."""
10
+ config = LoraConfig(
11
+ r=16,
12
+ lora_alpha=32,
13
+ target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
14
+ lora_dropout=0.05,
15
+ bias="none",
16
+ task_type="CAUSAL_LM",
17
+ )
18
+ return get_peft_model(model, config)
src/model_loader.py ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Model loading utilities."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from typing import Any
7
+
8
+ from transformers import AutoModelForCausalLM, AutoTokenizer
9
+
10
+ from src.config import settings
11
+
12
+
13
+ @dataclass
14
+ class ModelBundle:
15
+ """Holds tokenizer and model together."""
16
+
17
+ tokenizer: Any
18
+ model: Any
19
+ active_model_name: str
20
+ is_mock: bool = False
21
+ load_error: str = ""
22
+
23
+
24
+ def load_model_bundle() -> ModelBundle:
25
+ """Load Qwen2.5-Coder first, then fallback if needed."""
26
+ if settings.force_mock_mode:
27
+ return ModelBundle(
28
+ tokenizer=None,
29
+ model=None,
30
+ active_model_name="mock-rule-based",
31
+ is_mock=True,
32
+ load_error="FORCE_MOCK_MODE=true",
33
+ )
34
+
35
+ candidate_models = [
36
+ settings.model_name,
37
+ settings.fallback_model_name,
38
+ settings.final_fallback_model_name,
39
+ ]
40
+ last_error = None
41
+
42
+ for model_name in candidate_models:
43
+ try:
44
+ tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
45
+ model = AutoModelForCausalLM.from_pretrained(
46
+ model_name,
47
+ trust_remote_code=True,
48
+ device_map="auto",
49
+ )
50
+ return ModelBundle(
51
+ tokenizer=tokenizer,
52
+ model=model,
53
+ active_model_name=model_name,
54
+ )
55
+ except Exception as exc: # pragma: no cover
56
+ last_error = exc
57
+
58
+ return ModelBundle(
59
+ tokenizer=None,
60
+ model=None,
61
+ active_model_name="mock-rule-based",
62
+ is_mock=True,
63
+ load_error=str(last_error),
64
+ )
src/pipeline.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """End-to-end orchestration for generation and validation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import time
6
+
7
+ from src.config import settings
8
+ from src.generator import generate_response
9
+ from src.hallucination import hallucination_check
10
+ from src.model_loader import load_model_bundle
11
+ from src.prompts import SYSTEM_PROMPT, build_user_prompt
12
+ from src.rag import CodeRAG
13
+ from src.relevancy import RelevancyScorer
14
+
15
+
16
+ class CodingLLMPipeline:
17
+ """Coordinates model, RAG, explainability, and quality checks."""
18
+
19
+ def __init__(self):
20
+ self.model_bundle = None
21
+ self.relevancy = RelevancyScorer()
22
+ self.rag = CodeRAG() if settings.use_rag else None
23
+
24
+ def _ensure_model_loaded(self):
25
+ if self.model_bundle is None:
26
+ self.model_bundle = load_model_bundle()
27
+
28
+ def run(self, instruction: str, user_input: str) -> dict:
29
+ started = time.perf_counter()
30
+ self._ensure_model_loaded()
31
+
32
+ query_text = f"{instruction}\n{user_input}".strip()
33
+ retrieved_context = self.rag.retrieve(query_text) if self.rag else ""
34
+
35
+ prompt = f"{SYSTEM_PROMPT}\n\n{build_user_prompt(instruction, user_input, retrieved_context)}"
36
+ generation = generate_response(self.model_bundle, prompt)
37
+ hallucination_result = hallucination_check(generation.code)
38
+ relevancy_score = self.relevancy.score(query_text, generation.code)
39
+ explanation = generation.explanation
40
+ if hallucination_result.hallucination:
41
+ explanation = f"{generation.explanation}\n\nHallucination check reason: {hallucination_result.reason}"
42
+
43
+ latency_ms = int((time.perf_counter() - started) * 1000)
44
+ return {
45
+ "code": generation.code,
46
+ "explanation": explanation,
47
+ "confidence": round(generation.confidence, 4),
48
+ "important_tokens": generation.important_tokens,
49
+ "relevancy_score": round(relevancy_score, 4),
50
+ "hallucination": hallucination_result.hallucination,
51
+ "latency_ms": latency_ms,
52
+ }
53
+
54
+ @property
55
+ def active_model_name(self) -> str:
56
+ """Current model name, loading lazily if needed."""
57
+ self._ensure_model_loaded()
58
+ return self.model_bundle.active_model_name
src/prompts.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Prompt templates for coding assistant tasks."""
2
+
3
+ SYSTEM_PROMPT = """You are an advanced coding model.
4
+ Follow instructions exactly and produce high-quality code.
5
+ Always include:
6
+ 1) Final code
7
+ 2) A short explanation of what and why
8
+ """
9
+
10
+
11
+ def build_user_prompt(instruction: str, user_input: str, retrieved_context: str = "") -> str:
12
+ """Build a single prompt string with optional RAG context."""
13
+ context_block = f"\nRelevant snippets:\n{retrieved_context}\n" if retrieved_context else ""
14
+ return (
15
+ f"Instruction:\n{instruction}\n"
16
+ f"{context_block}"
17
+ f"\nInput:\n{user_input}\n"
18
+ "\nRespond with code first, then explanation."
19
+ )
src/rag.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Basic FAISS RAG retriever for code snippets."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+ from typing import List
8
+
9
+ import faiss
10
+ import numpy as np
11
+ from sklearn.feature_extraction.text import TfidfVectorizer
12
+
13
+
14
+ class CodeRAG:
15
+ """Loads snippets from data and serves top-k retrieval."""
16
+
17
+ def __init__(self, data_path: str = "data/sample_snippets.json"):
18
+ project_root = Path(__file__).resolve().parents[1]
19
+ self.data_path = project_root / data_path
20
+ self.snippets = self._load_data()
21
+ self.vectorizer = TfidfVectorizer(ngram_range=(1, 2), min_df=1)
22
+ self.snippet_vectors = self._build_vectors(self.snippets)
23
+ self.index = self._build_index(self.snippet_vectors)
24
+
25
+ def _load_data(self) -> List[str]:
26
+ if not self.data_path.exists():
27
+ return []
28
+ payload = json.loads(self.data_path.read_text(encoding="utf-8"))
29
+ return [item["snippet"] for item in payload]
30
+
31
+ def _build_vectors(self, snippets: List[str]) -> np.ndarray | None:
32
+ if not snippets:
33
+ return None
34
+ matrix = self.vectorizer.fit_transform(snippets)
35
+ vectors = matrix.toarray().astype(np.float32)
36
+ # Normalize for cosine-like similarity with IndexFlatIP.
37
+ norms = np.linalg.norm(vectors, axis=1, keepdims=True)
38
+ norms[norms == 0.0] = 1.0
39
+ return vectors / norms
40
+
41
+ def _build_index(self, vectors: np.ndarray | None):
42
+ if vectors is None:
43
+ return None
44
+ index = faiss.IndexFlatIP(vectors.shape[1])
45
+ index.add(vectors)
46
+ return index
47
+
48
+ def retrieve(self, query: str, top_k: int = 2) -> str:
49
+ if not self.snippets or self.index is None or self.snippet_vectors is None:
50
+ return ""
51
+ query_vec = self.vectorizer.transform([query]).toarray().astype(np.float32)
52
+ qnorm = np.linalg.norm(query_vec, axis=1, keepdims=True)
53
+ qnorm[qnorm == 0.0] = 1.0
54
+ query_vec = query_vec / qnorm
55
+ _, idx = self.index.search(query_vec, min(top_k, len(self.snippets)))
56
+ selected = [self.snippets[i] for i in idx[0] if i >= 0]
57
+ return "\n\n".join(selected)
src/relevancy.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Lightweight relevancy scoring without heavy embedding backends."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from sklearn.feature_extraction.text import TfidfVectorizer
6
+ from sklearn.metrics.pairwise import cosine_similarity
7
+
8
+
9
+ class RelevancyScorer:
10
+ """Computes semantic relevancy between request and generated code."""
11
+
12
+ def __init__(self):
13
+ self.vectorizer = TfidfVectorizer(ngram_range=(1, 2), min_df=1)
14
+
15
+ def score(self, query_text: str, generated_text: str) -> float:
16
+ matrix = self.vectorizer.fit_transform([query_text, generated_text])
17
+ return float(cosine_similarity(matrix[0], matrix[1])[0][0])
src/schemas.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """API schema definitions."""
2
+
3
+ from pydantic import BaseModel, Field
4
+
5
+
6
+ class GenerateRequest(BaseModel):
7
+ instruction: str = Field(..., min_length=1)
8
+ input: str = Field("", description="Source code or problem context.")
9
+
10
+
11
+ class GenerateResponse(BaseModel):
12
+ code: str
13
+ explanation: str
14
+ confidence: float
15
+ important_tokens: list[str]
16
+ relevancy_score: float
17
+ hallucination: bool
18
+ latency_ms: int
tasks.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Simple task runner for common project workflows."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import os
7
+ import subprocess
8
+ import sys
9
+ import time
10
+
11
+
12
+ def run_cmd(cmd: list[str]) -> int:
13
+ print(">", " ".join(cmd))
14
+ return subprocess.call(cmd)
15
+
16
+
17
+ def task_install() -> int:
18
+ return run_cmd([sys.executable, "-m", "pip", "install", "-r", "requirements.txt"])
19
+
20
+
21
+ def task_run() -> int:
22
+ return run_cmd([sys.executable, "-m", "uvicorn", "api.main:app", "--host", "0.0.0.0", "--port", "8000"])
23
+
24
+
25
+ def task_space() -> int:
26
+ return run_cmd([sys.executable, "app.py"])
27
+
28
+
29
+ def task_smoke() -> int:
30
+ return run_cmd([sys.executable, "smoke_test.py"])
31
+
32
+
33
+ def task_compile() -> int:
34
+ return run_cmd([sys.executable, "-m", "compileall", "."])
35
+
36
+
37
+ def task_docker_up() -> int:
38
+ return run_cmd(["docker", "compose", "up", "--build"])
39
+
40
+
41
+ def task_docker_down() -> int:
42
+ return run_cmd(["docker", "compose", "down"])
43
+
44
+
45
+ def task_hf_upload(repo_id: str, token: str, private: bool) -> int:
46
+ cmd = [sys.executable, "upload_to_hf.py", "--repo-id", repo_id, "--token", token]
47
+ if private:
48
+ cmd.append("--private")
49
+ return run_cmd(cmd)
50
+
51
+
52
+ def task_serve_smoke() -> int:
53
+ """Start API, run smoke test, then stop API."""
54
+ env = os.environ.copy()
55
+ server_cmd = [sys.executable, "-m", "uvicorn", "api.main:app", "--host", "127.0.0.1", "--port", "8000"]
56
+ print(">", " ".join(server_cmd))
57
+ server = subprocess.Popen(server_cmd, env=env)
58
+ try:
59
+ time.sleep(3)
60
+ return run_cmd([sys.executable, "smoke_test.py"])
61
+ finally:
62
+ server.terminate()
63
+ try:
64
+ server.wait(timeout=10)
65
+ except subprocess.TimeoutExpired:
66
+ server.kill()
67
+
68
+
69
+ def main():
70
+ parser = argparse.ArgumentParser(description="Project task runner")
71
+ sub = parser.add_subparsers(dest="task", required=True)
72
+
73
+ sub.add_parser("install")
74
+ sub.add_parser("run")
75
+ sub.add_parser("space")
76
+ sub.add_parser("smoke")
77
+ sub.add_parser("compile")
78
+ sub.add_parser("docker-up")
79
+ sub.add_parser("docker-down")
80
+ sub.add_parser("serve-smoke")
81
+ hf = sub.add_parser("hf-upload")
82
+ hf.add_argument("--repo-id", required=True)
83
+ hf.add_argument("--token", required=True)
84
+ hf.add_argument("--private", action="store_true")
85
+
86
+ args = parser.parse_args()
87
+
88
+ if args.task == "install":
89
+ code = task_install()
90
+ elif args.task == "run":
91
+ code = task_run()
92
+ elif args.task == "space":
93
+ code = task_space()
94
+ elif args.task == "smoke":
95
+ code = task_smoke()
96
+ elif args.task == "compile":
97
+ code = task_compile()
98
+ elif args.task == "docker-up":
99
+ code = task_docker_up()
100
+ elif args.task == "docker-down":
101
+ code = task_docker_down()
102
+ elif args.task == "serve-smoke":
103
+ code = task_serve_smoke()
104
+ else:
105
+ code = task_hf_upload(args.repo_id, args.token, args.private)
106
+
107
+ raise SystemExit(code)
108
+
109
+
110
+ if __name__ == "__main__":
111
+ main()
upload_to_hf.py ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Upload this project as a Hugging Face Space."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ from pathlib import Path
7
+
8
+ from huggingface_hub import HfApi, create_repo, upload_folder
9
+
10
+
11
+ def main():
12
+ parser = argparse.ArgumentParser(description="Upload coding-llm project to Hugging Face Space.")
13
+ parser.add_argument("--repo-id", required=True, help="Example: username/coding-llm-space")
14
+ parser.add_argument("--token", required=True, help="Hugging Face access token")
15
+ parser.add_argument("--private", action="store_true", help="Create private repo/space")
16
+ args = parser.parse_args()
17
+
18
+ root = Path(__file__).resolve().parent
19
+ api = HfApi(token=args.token)
20
+ create_repo(
21
+ repo_id=args.repo_id,
22
+ token=args.token,
23
+ repo_type="space",
24
+ private=args.private,
25
+ exist_ok=True,
26
+ space_sdk="gradio",
27
+ )
28
+ upload_folder(
29
+ folder_path=str(root),
30
+ repo_id=args.repo_id,
31
+ repo_type="space",
32
+ token=args.token,
33
+ ignore_patterns=[".git*", "__pycache__", "*.pyc"],
34
+ )
35
+ info = api.repo_info(repo_id=args.repo_id, repo_type="space")
36
+ print(f"Uploaded successfully: {info.id}")
37
+
38
+
39
+ if __name__ == "__main__":
40
+ main()