Spaces:
Running
Running
Jaiwincr7
commited on
Commit
·
dad3b3d
1
Parent(s):
5ecaa55
Final local changes before pulling latest remote updates
Browse files- Dockerfile +20 -13
- __pycache__/code.cpython-313.pyc +0 -0
- __pycache__/main.cpython-313.pyc +0 -0
- __pycache__/merged.cpython-313.pyc +0 -0
- app.py +83 -0
- finetunned.py +34 -0
- main.py +88 -0
- merged.py +69 -0
Dockerfile
CHANGED
|
@@ -1,20 +1,27 @@
|
|
| 1 |
-
|
|
|
|
| 2 |
|
|
|
|
| 3 |
WORKDIR /app
|
| 4 |
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
curl \
|
| 8 |
-
git \
|
| 9 |
-
&& rm -rf /var/lib/apt/lists/*
|
| 10 |
-
|
| 11 |
-
COPY requirements.txt ./
|
| 12 |
-
COPY src/ ./src/
|
| 13 |
|
| 14 |
-
|
|
|
|
| 15 |
|
| 16 |
-
|
|
|
|
|
|
|
|
|
|
| 17 |
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Use a Python base image
|
| 2 |
+
FROM python:3.10-slim
|
| 3 |
|
| 4 |
+
# Set the working directory
|
| 5 |
WORKDIR /app
|
| 6 |
|
| 7 |
+
# Expose the port Streamlit runs on
|
| 8 |
+
EXPOSE 8501
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
|
| 10 |
+
# Copy the requirements file and install dependencies
|
| 11 |
+
COPY requirements.txt .
|
| 12 |
|
| 13 |
+
# Install dependencies. We use specific indexes for PyTorch to ensure compatibility
|
| 14 |
+
# and a general upgrade to avoid issues.
|
| 15 |
+
RUN pip install --upgrade pip
|
| 16 |
+
RUN pip install -r requirements.txt
|
| 17 |
|
| 18 |
+
# Copy the rest of the application code
|
| 19 |
+
# Assuming the user saved the provided code blocks into these files
|
| 20 |
+
COPY main.py .
|
| 21 |
+
COPY merged.py .
|
| 22 |
+
COPY app.py .
|
| 23 |
|
| 24 |
+
# Command to run the Streamlit application
|
| 25 |
+
# We use --server.port 8501 to match the exposed port
|
| 26 |
+
# and --server.address 0.0.0.0 to make it accessible outside the container
|
| 27 |
+
CMD ["streamlit", "run", "app.py", "--server.port", "8501", "--server.address", "0.0.0.0"]
|
__pycache__/code.cpython-313.pyc
ADDED
|
Binary file (1.34 kB). View file
|
|
|
__pycache__/main.cpython-313.pyc
ADDED
|
Binary file (3.23 kB). View file
|
|
|
__pycache__/merged.cpython-313.pyc
ADDED
|
Binary file (1.97 kB). View file
|
|
|
app.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
from merged import load_model_and_tokenizer, generate_code_stream
|
| 3 |
+
from main import test_case # your existing PDF generator
|
| 4 |
+
|
| 5 |
+
st.write("🚀 App started")
|
| 6 |
+
# ---------------- Load model ONCE ----------------
|
| 7 |
+
@st.cache_resource
|
| 8 |
+
def load_model():
|
| 9 |
+
return load_model_and_tokenizer()
|
| 10 |
+
|
| 11 |
+
tokenizer, model = load_model()
|
| 12 |
+
|
| 13 |
+
# ---------------- Session state ----------------
|
| 14 |
+
for key in ["selected_language", "generated_code", "pdf_bytes", "user_input"]:
|
| 15 |
+
if key not in st.session_state:
|
| 16 |
+
st.session_state[key] = "" if key != "selected_language" else None
|
| 17 |
+
|
| 18 |
+
# ---------------- UI helpers ----------------
|
| 19 |
+
def select_language(lang):
|
| 20 |
+
st.session_state.selected_language = lang
|
| 21 |
+
st.session_state.generated_code = ""
|
| 22 |
+
st.session_state.pdf_bytes = ""
|
| 23 |
+
st.session_state.user_input = ""
|
| 24 |
+
|
| 25 |
+
def reset():
|
| 26 |
+
for k in st.session_state:
|
| 27 |
+
st.session_state[k] = "" if k != "selected_language" else None
|
| 28 |
+
|
| 29 |
+
# ---------------- UI ----------------
|
| 30 |
+
st.title("Generate any code and get test case for it")
|
| 31 |
+
|
| 32 |
+
if st.session_state.selected_language is None:
|
| 33 |
+
cols = st.columns(4)
|
| 34 |
+
cols[0].button("Java", on_click=select_language, args=("Java",))
|
| 35 |
+
cols[1].button("React.js", on_click=select_language, args=("React.js",))
|
| 36 |
+
cols[2].button("Python", on_click=select_language, args=("Python",))
|
| 37 |
+
cols[3].button("C++", on_click=select_language, args=("C++",))
|
| 38 |
+
|
| 39 |
+
else:
|
| 40 |
+
lang = st.session_state.selected_language
|
| 41 |
+
st.subheader(f"Selected Language: {lang}")
|
| 42 |
+
st.button("Reset", on_click=reset)
|
| 43 |
+
|
| 44 |
+
st.session_state.user_input = st.text_input(
|
| 45 |
+
"Describe the task",
|
| 46 |
+
value=st.session_state.user_input
|
| 47 |
+
)
|
| 48 |
+
|
| 49 |
+
if st.button("Generate Code"):
|
| 50 |
+
if st.session_state.user_input.strip():
|
| 51 |
+
st.session_state.generated_code = ""
|
| 52 |
+
|
| 53 |
+
st.subheader("Generated Code")
|
| 54 |
+
code_placeholder = st.empty()
|
| 55 |
+
|
| 56 |
+
with st.spinner("Generating code..."):
|
| 57 |
+
for token in generate_code_stream(
|
| 58 |
+
lang,
|
| 59 |
+
st.session_state.user_input,
|
| 60 |
+
tokenizer,
|
| 61 |
+
model
|
| 62 |
+
):
|
| 63 |
+
st.session_state.generated_code += token
|
| 64 |
+
code_placeholder.code(
|
| 65 |
+
st.session_state.generated_code
|
| 66 |
+
)
|
| 67 |
+
|
| 68 |
+
# Generate test case PDF AFTER code generation
|
| 69 |
+
st.session_state.pdf_bytes = test_case(
|
| 70 |
+
st.session_state.generated_code
|
| 71 |
+
)
|
| 72 |
+
|
| 73 |
+
else:
|
| 74 |
+
st.warning("Please enter a task")
|
| 75 |
+
|
| 76 |
+
if st.session_state.pdf_bytes:
|
| 77 |
+
st.download_button(
|
| 78 |
+
"Download Test Cases PDF",
|
| 79 |
+
st.session_state.pdf_bytes,
|
| 80 |
+
file_name="test_cases.pdf",
|
| 81 |
+
mime="application/pdf"
|
| 82 |
+
)
|
| 83 |
+
|
finetunned.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from transformers import AutoModelForCausalLM, AutoTokenizer
|
| 2 |
+
from peft import PeftModel
|
| 3 |
+
import torch
|
| 4 |
+
|
| 5 |
+
model_id = "Qwen/Qwen2.5-Coder-0.5B-Instruct"
|
| 6 |
+
lora_path = "./Qwen2.5-Coder-0.5B-lora"
|
| 7 |
+
|
| 8 |
+
tokenizer = AutoTokenizer.from_pretrained(
|
| 9 |
+
model_id,
|
| 10 |
+
trust_remote_code=True
|
| 11 |
+
)
|
| 12 |
+
tokenizer.pad_token = tokenizer.eos_token
|
| 13 |
+
|
| 14 |
+
# 🔴 CPU ONLY — NO CUDA, NO device_map
|
| 15 |
+
base_model = AutoModelForCausalLM.from_pretrained(
|
| 16 |
+
model_id,
|
| 17 |
+
trust_remote_code=True,
|
| 18 |
+
torch_dtype=torch.float32,
|
| 19 |
+
low_cpu_mem_usage=True,
|
| 20 |
+
)
|
| 21 |
+
|
| 22 |
+
model = PeftModel.from_pretrained(
|
| 23 |
+
base_model,
|
| 24 |
+
lora_path,
|
| 25 |
+
)
|
| 26 |
+
|
| 27 |
+
print("🔄 Merging LoRA (this will take time on CPU)...")
|
| 28 |
+
|
| 29 |
+
merged_model = model.merge_and_unload()
|
| 30 |
+
|
| 31 |
+
merged_model.save_pretrained("./Qwen2.5-Coder-0.5B-lora-merged")
|
| 32 |
+
tokenizer.save_pretrained("./Qwen2.5-Coder-0.5B-lora-merged")
|
| 33 |
+
|
| 34 |
+
print("✅ Merge complete")
|
main.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from langchain_core.runnables import RunnablePassthrough, RunnableMap
|
| 2 |
+
from langchain_core.output_parsers import StrOutputParser
|
| 3 |
+
from langchain_core.prompts import PromptTemplate, ChatPromptTemplate
|
| 4 |
+
from langchain_huggingface import HuggingFacePipeline
|
| 5 |
+
from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
|
| 6 |
+
import torch
|
| 7 |
+
from fpdf import FPDF
|
| 8 |
+
import re
|
| 9 |
+
|
| 10 |
+
model_id = "deepseek-ai/deepseek-coder-1.3b-instruct"
|
| 11 |
+
|
| 12 |
+
# Load model and tokenizer once
|
| 13 |
+
tokenizer = AutoTokenizer.from_pretrained(model_id)
|
| 14 |
+
model = AutoModelForCausalLM.from_pretrained(
|
| 15 |
+
model_id,
|
| 16 |
+
dtype=torch.float16,
|
| 17 |
+
device_map="auto",
|
| 18 |
+
offload_folder="./offload"
|
| 19 |
+
)
|
| 20 |
+
stop_tokens = ["<|end_of_text|>", "<|end_of_user|>"]
|
| 21 |
+
|
| 22 |
+
# Wrap Transformers pipeline
|
| 23 |
+
pipe = pipeline("text-generation", model=model, tokenizer=tokenizer)
|
| 24 |
+
llm = HuggingFacePipeline(
|
| 25 |
+
pipeline=pipe,
|
| 26 |
+
model_kwargs={
|
| 27 |
+
"max_new_tokens": 4096,
|
| 28 |
+
"do_sample": True,
|
| 29 |
+
"temperature": 0.2,
|
| 30 |
+
"repetition_penalty": 1.05,
|
| 31 |
+
"eos_token_id": tokenizer.eos_token_id,
|
| 32 |
+
}
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
def test_case(code):
|
| 36 |
+
test_prompt = ChatPromptTemplate.from_messages(
|
| 37 |
+
[
|
| 38 |
+
(
|
| 39 |
+
"system",
|
| 40 |
+
"""You are an expert QA engineer.
|
| 41 |
+
STRICTLY follow these rules for your output:
|
| 42 |
+
- Generate EXACTLY 10 numbered test cases (1–5 functional, 6–10 edge cases).
|
| 43 |
+
- Output ONLY the numbered list.
|
| 44 |
+
- DO NOT include explanations, headers, filler text, or markdown.
|
| 45 |
+
- Each test MUST be a single, concise sentence.
|
| 46 |
+
- Begin your response immediately with '1. '""", # Slightly relaxed constraints
|
| 47 |
+
),
|
| 48 |
+
(
|
| 49 |
+
"user",
|
| 50 |
+
"Generate test cases for the following code:\n{code}",
|
| 51 |
+
),
|
| 52 |
+
]
|
| 53 |
+
)
|
| 54 |
+
|
| 55 |
+
test_chain = test_prompt | llm | StrOutputParser()
|
| 56 |
+
test_cases = test_chain.invoke({"code": code})
|
| 57 |
+
|
| 58 |
+
print("\nGenerated Test Cases (Raw):\n", test_cases)
|
| 59 |
+
|
| 60 |
+
# Aggressive cleaning
|
| 61 |
+
test_cases = re.sub(r"```.*?```", "", test_cases, flags=re.DOTALL)
|
| 62 |
+
test_cases = re.sub(r"```", "", test_cases)
|
| 63 |
+
test_cases = test_cases.strip()
|
| 64 |
+
|
| 65 |
+
# --- ADD THIS CHECK ---
|
| 66 |
+
if not test_cases:
|
| 67 |
+
test_cases = "Error: Test case generation failed or returned empty content."
|
| 68 |
+
|
| 69 |
+
print("\nGenerated Test Cases (Cleaned):\n", test_cases)
|
| 70 |
+
# -----------------------
|
| 71 |
+
|
| 72 |
+
# Encoding step remains the same for FPDF compatibility
|
| 73 |
+
safe_text = test_cases.encode("latin-1", "ignore").decode("latin-1")
|
| 74 |
+
|
| 75 |
+
# If the safe_text is still empty, FPDF will produce an empty PDF
|
| 76 |
+
# It's better to verify the content being passed to FPDF
|
| 77 |
+
|
| 78 |
+
pdf=FPDF()
|
| 79 |
+
pdf.add_page()
|
| 80 |
+
pdf.set_font("Arial", size=12)
|
| 81 |
+
# You can set a title to ensure the PDF isn't blank
|
| 82 |
+
pdf.multi_cell(0, 10, txt="--- Generated Test Cases ---", align='C')
|
| 83 |
+
pdf.multi_cell(0, 10, txt=safe_text)
|
| 84 |
+
|
| 85 |
+
# Use output as a bytes object without saving to disk first
|
| 86 |
+
file = pdf.output(dest='S').encode('latin-1')
|
| 87 |
+
|
| 88 |
+
return file
|
merged.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from transformers import (
|
| 3 |
+
AutoModelForCausalLM,
|
| 4 |
+
AutoTokenizer,
|
| 5 |
+
TextIteratorStreamer
|
| 6 |
+
)
|
| 7 |
+
from threading import Thread
|
| 8 |
+
|
| 9 |
+
MODEL_PATH = "jaiwinrc7/Qwen2.5-Coder-0.5B-finetunned-merged"
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def load_model_and_tokenizer():
|
| 13 |
+
tokenizer = AutoTokenizer.from_pretrained(
|
| 14 |
+
MODEL_PATH,
|
| 15 |
+
trust_remote_code=True
|
| 16 |
+
)
|
| 17 |
+
|
| 18 |
+
model = AutoModelForCausalLM.from_pretrained(
|
| 19 |
+
MODEL_PATH,
|
| 20 |
+
device_map="cpu",
|
| 21 |
+
trust_remote_code=True,
|
| 22 |
+
torch_dtype=torch.float32, # IMPORTANT: faster on CPU
|
| 23 |
+
)
|
| 24 |
+
|
| 25 |
+
model.eval()
|
| 26 |
+
return tokenizer, model
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def build_prompt(lang, task):
|
| 30 |
+
# Keep prompt SIMPLE for speed
|
| 31 |
+
return f"""You are a coding assistant.
|
| 32 |
+
Write {lang} code for the following task:
|
| 33 |
+
|
| 34 |
+
{task}
|
| 35 |
+
|
| 36 |
+
Code:
|
| 37 |
+
"""
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def generate_code_stream(lang, user_input, tokenizer, model):
|
| 41 |
+
prompt = build_prompt(lang, user_input)
|
| 42 |
+
inputs = tokenizer(prompt, return_tensors="pt")
|
| 43 |
+
|
| 44 |
+
streamer = TextIteratorStreamer(
|
| 45 |
+
tokenizer,
|
| 46 |
+
skip_prompt=True,
|
| 47 |
+
skip_special_tokens=True
|
| 48 |
+
)
|
| 49 |
+
|
| 50 |
+
generation_kwargs = dict(
|
| 51 |
+
**inputs,
|
| 52 |
+
max_new_tokens=250,
|
| 53 |
+
do_sample=False,
|
| 54 |
+
temperature=0.0,
|
| 55 |
+
use_cache=True,
|
| 56 |
+
streamer=streamer,
|
| 57 |
+
pad_token_id=tokenizer.eos_token_id,
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
# Run generation in background thread
|
| 61 |
+
thread = Thread(
|
| 62 |
+
target=model.generate,
|
| 63 |
+
kwargs=generation_kwargs
|
| 64 |
+
)
|
| 65 |
+
thread.start()
|
| 66 |
+
|
| 67 |
+
# Yield tokens as they arrive
|
| 68 |
+
for token in streamer:
|
| 69 |
+
yield token
|