prepforge 0.2.0__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {prepforge-0.2.0 → prepforge-0.2.2}/PKG-INFO +2 -2
- prepforge-0.2.2/major_project/core/train.py +156 -0
- {prepforge-0.2.0 → prepforge-0.2.2}/prepforge.egg-info/PKG-INFO +2 -2
- {prepforge-0.2.0 → prepforge-0.2.2}/pyproject.toml +2 -2
- prepforge-0.2.0/major_project/core/train.py +0 -212
- {prepforge-0.2.0 → prepforge-0.2.2}/LICENSE +0 -0
- {prepforge-0.2.0 → prepforge-0.2.2}/README.md +0 -0
- {prepforge-0.2.0 → prepforge-0.2.2}/major_project/__init__.py +0 -0
- {prepforge-0.2.0 → prepforge-0.2.2}/major_project/cli.py +0 -0
- {prepforge-0.2.0 → prepforge-0.2.2}/major_project/config.py +0 -0
- {prepforge-0.2.0 → prepforge-0.2.2}/major_project/core/__init__.py +0 -0
- {prepforge-0.2.0 → prepforge-0.2.2}/major_project/core/config_store.py +0 -0
- {prepforge-0.2.0 → prepforge-0.2.2}/major_project/core/model.py +0 -0
- {prepforge-0.2.0 → prepforge-0.2.2}/major_project/core/utils.py +0 -0
- {prepforge-0.2.0 → prepforge-0.2.2}/major_project/gui_app.py +0 -0
- {prepforge-0.2.0 → prepforge-0.2.2}/major_project/streamlit_app.py +0 -0
- {prepforge-0.2.0 → prepforge-0.2.2}/major_project/tui_app.py +0 -0
- {prepforge-0.2.0 → prepforge-0.2.2}/prepforge.egg-info/SOURCES.txt +0 -0
- {prepforge-0.2.0 → prepforge-0.2.2}/prepforge.egg-info/dependency_links.txt +0 -0
- {prepforge-0.2.0 → prepforge-0.2.2}/prepforge.egg-info/entry_points.txt +0 -0
- {prepforge-0.2.0 → prepforge-0.2.2}/prepforge.egg-info/requires.txt +0 -0
- {prepforge-0.2.0 → prepforge-0.2.2}/prepforge.egg-info/top_level.txt +0 -0
- {prepforge-0.2.0 → prepforge-0.2.2}/setup.cfg +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: prepforge
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: AI Exam Preparation System with TUI, GUI, and Streamlit
|
|
5
|
-
Author: Himanshu Arora
|
|
5
|
+
Author-email: Himanshu Arora <ha0himanshuarora@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Classifier: Programming Language :: Python :: 3
|
|
8
8
|
Classifier: Operating System :: OS Independent
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
from unsloth import FastLanguageModel
|
|
2
|
+
from datasets import load_dataset, Dataset
|
|
3
|
+
from transformers import TrainingArguments
|
|
4
|
+
from trl import SFTTrainer
|
|
5
|
+
|
|
6
|
+
from major_project.config import get_model
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def train_model(
|
|
10
|
+
dataset_path,
|
|
11
|
+
output_dir,
|
|
12
|
+
epochs=2,
|
|
13
|
+
lr=2e-4,
|
|
14
|
+
batch_size=2,
|
|
15
|
+
max_seq_length=512,
|
|
16
|
+
limit=None,
|
|
17
|
+
subset=None,
|
|
18
|
+
):
|
|
19
|
+
# -----------------------------
|
|
20
|
+
# LOAD MODEL FROM CONFIG
|
|
21
|
+
# -----------------------------
|
|
22
|
+
model_name = get_model()
|
|
23
|
+
print(f"[TRAIN] Using model: {model_name}")
|
|
24
|
+
|
|
25
|
+
model, tokenizer = FastLanguageModel.from_pretrained(
|
|
26
|
+
model_name=model_name,
|
|
27
|
+
max_seq_length=max_seq_length,
|
|
28
|
+
load_in_4bit=True,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# -----------------------------
|
|
32
|
+
# APPLY LoRA
|
|
33
|
+
# -----------------------------
|
|
34
|
+
model = FastLanguageModel.get_peft_model(
|
|
35
|
+
model,
|
|
36
|
+
r=16,
|
|
37
|
+
target_modules=[
|
|
38
|
+
"q_proj",
|
|
39
|
+
"k_proj",
|
|
40
|
+
"v_proj",
|
|
41
|
+
"o_proj",
|
|
42
|
+
"gate_proj",
|
|
43
|
+
"up_proj",
|
|
44
|
+
"down_proj",
|
|
45
|
+
],
|
|
46
|
+
lora_alpha=16,
|
|
47
|
+
lora_dropout=0,
|
|
48
|
+
bias="none",
|
|
49
|
+
use_gradient_checkpointing="unsloth",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# -----------------------------
|
|
53
|
+
# LOAD DATASET
|
|
54
|
+
# -----------------------------
|
|
55
|
+
dataset = load_dataset("json", data_files=dataset_path, split="train")
|
|
56
|
+
assert isinstance(dataset, Dataset)
|
|
57
|
+
|
|
58
|
+
print(f"[TRAIN] Original dataset size: {len(dataset)}")
|
|
59
|
+
|
|
60
|
+
# -----------------------------
|
|
61
|
+
# APPLY LIMIT / SUBSET
|
|
62
|
+
# -----------------------------
|
|
63
|
+
if limit is not None:
|
|
64
|
+
dataset = dataset.select(range(min(limit, len(dataset))))
|
|
65
|
+
print(f"[TRAIN] Using first {len(dataset)} samples")
|
|
66
|
+
|
|
67
|
+
elif subset is not None:
|
|
68
|
+
size = int(len(dataset) * subset)
|
|
69
|
+
dataset = dataset.select(range(size))
|
|
70
|
+
print(f"[TRAIN] Using {subset * 100:.1f}% → {len(dataset)} samples")
|
|
71
|
+
|
|
72
|
+
# -----------------------------
|
|
73
|
+
# FORMAT DATA
|
|
74
|
+
# -----------------------------
|
|
75
|
+
def format_prompt(example):
|
|
76
|
+
instruction = example.get("instruction", "")
|
|
77
|
+
input_text = example.get("input", "")
|
|
78
|
+
output = example.get("output", "")
|
|
79
|
+
|
|
80
|
+
prompt = f"{instruction}\n{input_text}".strip()
|
|
81
|
+
|
|
82
|
+
user_part = f"""<|begin_of_text|>
|
|
83
|
+
<|start_header_id|>user<|end_header_id|>
|
|
84
|
+
|
|
85
|
+
{prompt}
|
|
86
|
+
|
|
87
|
+
<|start_header_id|>assistant<|end_header_id|>
|
|
88
|
+
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
full_text = user_part + output
|
|
92
|
+
|
|
93
|
+
user_tokens = tokenizer(user_part, add_special_tokens=False)
|
|
94
|
+
full_tokens = tokenizer(
|
|
95
|
+
full_text,
|
|
96
|
+
truncation=True,
|
|
97
|
+
max_length=max_seq_length,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
labels = full_tokens["input_ids"].copy()
|
|
101
|
+
user_len = len(user_tokens["input_ids"])
|
|
102
|
+
|
|
103
|
+
# Mask user part
|
|
104
|
+
labels[:user_len] = [-100] * user_len
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
"input_ids": full_tokens["input_ids"],
|
|
108
|
+
"attention_mask": full_tokens["attention_mask"],
|
|
109
|
+
"labels": labels,
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
dataset = dataset.map(format_prompt, batched=False)
|
|
113
|
+
|
|
114
|
+
# Keep only required columns
|
|
115
|
+
dataset = dataset.remove_columns(
|
|
116
|
+
[
|
|
117
|
+
col
|
|
118
|
+
for col in dataset.column_names
|
|
119
|
+
if col not in ["input_ids", "attention_mask", "labels"]
|
|
120
|
+
]
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# -----------------------------
|
|
124
|
+
# TRAINER
|
|
125
|
+
# -----------------------------
|
|
126
|
+
training_args = TrainingArguments(
|
|
127
|
+
per_device_train_batch_size=batch_size,
|
|
128
|
+
num_train_epochs=epochs,
|
|
129
|
+
learning_rate=lr,
|
|
130
|
+
output_dir=output_dir,
|
|
131
|
+
logging_steps=10,
|
|
132
|
+
save_steps=500,
|
|
133
|
+
optim="adamw_8bit",
|
|
134
|
+
report_to="none", # avoids wandb issues
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
trainer = SFTTrainer(
|
|
138
|
+
model=model,
|
|
139
|
+
tokenizer=tokenizer,
|
|
140
|
+
train_dataset=dataset,
|
|
141
|
+
args=training_args,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# -----------------------------
|
|
145
|
+
# TRAIN
|
|
146
|
+
# -----------------------------
|
|
147
|
+
print("[TRAIN] Starting training...")
|
|
148
|
+
trainer.train()
|
|
149
|
+
|
|
150
|
+
# -----------------------------
|
|
151
|
+
# SAVE
|
|
152
|
+
# -----------------------------
|
|
153
|
+
model.save_pretrained(output_dir)
|
|
154
|
+
tokenizer.save_pretrained(output_dir)
|
|
155
|
+
|
|
156
|
+
print("[TRAIN] Training complete.")
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: prepforge
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: AI Exam Preparation System with TUI, GUI, and Streamlit
|
|
5
|
-
Author: Himanshu Arora
|
|
5
|
+
Author-email: Himanshu Arora <ha0himanshuarora@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Classifier: Programming Language :: Python :: 3
|
|
8
8
|
Classifier: Operating System :: OS Independent
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "prepforge"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.2"
|
|
4
4
|
description = "AI Exam Preparation System with TUI, GUI, and Streamlit"
|
|
5
5
|
authors = [
|
|
6
|
-
{ name = "Himanshu Arora" }
|
|
6
|
+
{ name = "Himanshu Arora", email = "ha0himanshuarora@gmail.com" }
|
|
7
7
|
]
|
|
8
8
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
9
9
|
requires-python = ">=3.10"
|
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
|
|
3
|
-
# -----------------------------
|
|
4
|
-
# 🔥 FORCE OFFLINE MODE
|
|
5
|
-
# -----------------------------
|
|
6
|
-
os.environ["TRANSFORMERS_OFFLINE"] = "1"
|
|
7
|
-
os.environ["HF_HUB_OFFLINE"] = "1"
|
|
8
|
-
|
|
9
|
-
import streamlit as st
|
|
10
|
-
|
|
11
|
-
# 🔥 CORE IMPORTS
|
|
12
|
-
from major_project.core.model import load_model
|
|
13
|
-
from major_project.core.utils import (
|
|
14
|
-
build_prompt,
|
|
15
|
-
generate_stream,
|
|
16
|
-
chat_history,
|
|
17
|
-
last_mcq_block,
|
|
18
|
-
)
|
|
19
|
-
from major_project.config import DEFAULT_APP_NAME, DEFAULT_LORA, DEFAULT_MODEL
|
|
20
|
-
|
|
21
|
-
# -----------------------------
|
|
22
|
-
# CONFIG
|
|
23
|
-
# -----------------------------
|
|
24
|
-
APP_NAME = DEFAULT_APP_NAME
|
|
25
|
-
st.set_page_config(page_title=APP_NAME, layout="wide")
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
# -----------------------------
|
|
29
|
-
# MODEL CACHE
|
|
30
|
-
# -----------------------------
|
|
31
|
-
@st.cache_resource
|
|
32
|
-
def cached_model(base_model, lora_path):
|
|
33
|
-
return load_model(base_model, lora_path)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
# -----------------------------
|
|
37
|
-
# SIDEBAR
|
|
38
|
-
# -----------------------------
|
|
39
|
-
st.sidebar.title("⚙️ Model Settings")
|
|
40
|
-
|
|
41
|
-
# ❌ OLD (kept but unused safely)
|
|
42
|
-
model_name = DEFAULT_MODEL
|
|
43
|
-
|
|
44
|
-
model_mode = st.sidebar.selectbox("Model Type", ["Base Model", "LoRA Adapter"])
|
|
45
|
-
|
|
46
|
-
lora_path = DEFAULT_LORA
|
|
47
|
-
if model_mode == "LoRA Adapter":
|
|
48
|
-
lora_path = st.sidebar.text_input("LoRA Path")
|
|
49
|
-
|
|
50
|
-
load_btn = st.sidebar.button("🚀 Load Model")
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
# -----------------------------
|
|
54
|
-
# SESSION STATE
|
|
55
|
-
# -----------------------------
|
|
56
|
-
if "model" not in st.session_state:
|
|
57
|
-
st.session_state.model = None
|
|
58
|
-
st.session_state.tokenizer = None
|
|
59
|
-
st.session_state.is_gguf = False
|
|
60
|
-
|
|
61
|
-
if "history" not in st.session_state:
|
|
62
|
-
st.session_state.history = []
|
|
63
|
-
|
|
64
|
-
if "last_mcq" not in st.session_state:
|
|
65
|
-
st.session_state.last_mcq = ""
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
# -----------------------------
|
|
69
|
-
# LOAD MODEL
|
|
70
|
-
# -----------------------------
|
|
71
|
-
if load_btn:
|
|
72
|
-
if model_mode == "LoRA Adapter" and not lora_path:
|
|
73
|
-
st.sidebar.error("Provide LoRA path")
|
|
74
|
-
else:
|
|
75
|
-
with st.spinner("Loading model..."):
|
|
76
|
-
# 🔥 FIX: Always use config-selected model
|
|
77
|
-
model, tokenizer = cached_model(
|
|
78
|
-
None, # ← THIS FIXES YOUR BUG
|
|
79
|
-
lora_path if model_mode == "LoRA Adapter" else None,
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
st.session_state.model = model
|
|
83
|
-
st.session_state.tokenizer = tokenizer
|
|
84
|
-
st.session_state.is_gguf = (
|
|
85
|
-
isinstance(model, dict) and model.get("type") == "gguf"
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
backend = "GGUF (llama.cpp)" if st.session_state.is_gguf else "HuggingFace"
|
|
89
|
-
st.sidebar.success(f"Model loaded [{backend}]")
|
|
90
|
-
|
|
91
|
-
# 🔥 NEW: Show actual model path/name
|
|
92
|
-
if st.session_state.is_gguf:
|
|
93
|
-
st.sidebar.info(f"Using: {model.get('path')}")
|
|
94
|
-
else:
|
|
95
|
-
st.sidebar.info("Using HuggingFace model (from config)")
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
model = st.session_state.model
|
|
99
|
-
tokenizer = st.session_state.tokenizer
|
|
100
|
-
is_gguf = st.session_state.is_gguf
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
# -----------------------------
|
|
104
|
-
# SAFETY (FIXED)
|
|
105
|
-
# -----------------------------
|
|
106
|
-
if model is None:
|
|
107
|
-
st.title(f"🎓 {APP_NAME}")
|
|
108
|
-
st.warning("Load model from sidebar")
|
|
109
|
-
st.stop()
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
# -----------------------------
|
|
113
|
-
# UI
|
|
114
|
-
# -----------------------------
|
|
115
|
-
st.title(f"🎓 {APP_NAME}")
|
|
116
|
-
|
|
117
|
-
mode = st.sidebar.selectbox(
|
|
118
|
-
"Mode", ["Chat", "Notes", "Study Plan", "MCQ Generator", "Practice"]
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
length = st.sidebar.slider("Response Length", 100, 2000, 600)
|
|
122
|
-
count = st.sidebar.number_input("Number of Questions", 1, 50, 5)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
# -----------------------------
|
|
126
|
-
# RESET
|
|
127
|
-
# -----------------------------
|
|
128
|
-
if st.sidebar.button("🧹 Reset Chat"):
|
|
129
|
-
st.session_state.history = []
|
|
130
|
-
chat_history.clear()
|
|
131
|
-
st.session_state.last_mcq = ""
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
# -----------------------------
|
|
135
|
-
# DISPLAY HISTORY
|
|
136
|
-
# -----------------------------
|
|
137
|
-
for msg in st.session_state.history:
|
|
138
|
-
with st.chat_message(msg["role"]):
|
|
139
|
-
st.write(msg["content"])
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
# -----------------------------
|
|
143
|
-
# INPUT
|
|
144
|
-
# -----------------------------
|
|
145
|
-
user_input = st.chat_input(f"Ask {APP_NAME}...")
|
|
146
|
-
|
|
147
|
-
if user_input:
|
|
148
|
-
with st.chat_message("user"):
|
|
149
|
-
st.write(user_input)
|
|
150
|
-
|
|
151
|
-
prompt = build_prompt(mode, user_input, count)
|
|
152
|
-
|
|
153
|
-
with st.chat_message("assistant"):
|
|
154
|
-
placeholder = st.empty()
|
|
155
|
-
response = ""
|
|
156
|
-
|
|
157
|
-
# =========================================================
|
|
158
|
-
# 🔥 GGUF STREAMING
|
|
159
|
-
# =========================================================
|
|
160
|
-
if is_gguf:
|
|
161
|
-
try:
|
|
162
|
-
llm = model["llm"]
|
|
163
|
-
|
|
164
|
-
stream = llm(
|
|
165
|
-
prompt,
|
|
166
|
-
max_tokens=length,
|
|
167
|
-
stream=True,
|
|
168
|
-
temperature=0.3,
|
|
169
|
-
)
|
|
170
|
-
|
|
171
|
-
for chunk in stream:
|
|
172
|
-
token = chunk["choices"][0]["text"]
|
|
173
|
-
response += token
|
|
174
|
-
placeholder.markdown(response)
|
|
175
|
-
|
|
176
|
-
except Exception as e:
|
|
177
|
-
placeholder.error(f"GGUF Error: {e}")
|
|
178
|
-
st.stop()
|
|
179
|
-
|
|
180
|
-
# =========================================================
|
|
181
|
-
# 🔥 HF STREAMING (UNCHANGED)
|
|
182
|
-
# =========================================================
|
|
183
|
-
else:
|
|
184
|
-
streamer = generate_stream(model, tokenizer, prompt, length)
|
|
185
|
-
|
|
186
|
-
for token in streamer:
|
|
187
|
-
response += token
|
|
188
|
-
placeholder.markdown(response)
|
|
189
|
-
|
|
190
|
-
st.session_state.history.append({"role": "user", "content": user_input})
|
|
191
|
-
st.session_state.history.append({"role": "assistant", "content": response})
|
|
192
|
-
|
|
193
|
-
# 🔥 MCQ STORAGE
|
|
194
|
-
if "SECTION 1" in response and "SECTION 2" in response:
|
|
195
|
-
st.session_state.last_mcq = response
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
# -----------------------------
|
|
199
|
-
# SAVE CHAT
|
|
200
|
-
# -----------------------------
|
|
201
|
-
if st.session_state.history:
|
|
202
|
-
chat_text = f"=== {APP_NAME} Notes ===\n\n"
|
|
203
|
-
|
|
204
|
-
for msg in st.session_state.history:
|
|
205
|
-
role = "User" if msg["role"] == "user" else APP_NAME
|
|
206
|
-
chat_text += f"{role}:\n{msg['content']}\n\n"
|
|
207
|
-
|
|
208
|
-
st.sidebar.download_button(
|
|
209
|
-
"💾 Download Chat",
|
|
210
|
-
chat_text,
|
|
211
|
-
file_name=f"{APP_NAME.lower()}_chat.txt",
|
|
212
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|