robertolofaro commited on
Commit
5f5a49f
·
verified ·
1 Parent(s): 531a781

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +129 -22
app.py CHANGED
@@ -2,10 +2,39 @@ import gradio as gr
2
  from llama_cpp import Llama
3
  from huggingface_hub import hf_hub_download
4
  import os
 
 
5
 
6
- # Download model
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  model_path = hf_hub_download(
8
- repo_id="robertolofaro/articles-model",
9
  filename="articles-Q4_K_M.gguf",
10
  repo_type="model",
11
  token=os.environ.get("HF_TOKEN")
@@ -13,50 +42,128 @@ model_path = hf_hub_download(
13
 
14
  llm = Llama(
15
  model_path=model_path,
16
- n_ctx=65000,
17
  n_threads=2,
18
  n_batch=512,
19
  n_ubatch=512,
20
- n_gpu_layers=0,
21
- verbose=False
22
  )
23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  SYSTEM_PROMPT = """You are the reference expert for the articles contained in this database, all extracted from the website robertolofaro.com, and all focused on change.
25
  #Your Mission:
26
- When a user asks a question, your goal is to provide a structured response based ONLY on the articles provided in your training. Do not provide general advice from outside these sources. Do not reference specific articles or their titles, only the argument that they represent
27
  # Response Format:
28
  1. Executive Summary: A 2-3 sentence overview answering the core query.
29
- 2. Guidelines & Hints: A narrative of specific "answers/guidelines/hints" found in the source material, but without repetition and as storytelling. Verify and summarize your answer before sharing it with the user, so that it is not verbose and as short as possible- no more than 250 words should be within your answer.
30
  """
31
 
32
- def generate_response(message, history):
 
 
33
  full_prompt = f"<|im_start|>system\n{SYSTEM_PROMPT}<|im_end|>\n"
34
-
35
- for msg in history:
36
- role = msg["role"]
37
- content = msg["content"]
38
- full_prompt += f"<|im_start|>{role}\n{content}<|im_end|>\n"
39
-
40
- full_prompt += f"<|im_start|>user\n{message}<|im_end|>\n<|im_start|>assistant\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
  partial_text = ""
43
  for chunk in llm(
44
  full_prompt,
45
- max_tokens=2048,
 
 
 
46
  stop=["<|im_end|>", "<|im_start|>"],
47
  stream=True,
48
- temperature=0.7,
49
  ):
50
  token = chunk['choices'][0]['text']
51
  partial_text += token
52
  yield partial_text
53
 
54
 
55
- demo = gr.ChatInterface(
56
- fn=generate_response,
57
- title="Articles Q&A (CPU)",
58
- description="Experimental Q&A based on 350+ articles on Change Management published on robertolofaro.com. It could take few minutes to start delivering an answer (CPU-based).",
59
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
 
61
  if __name__ == "__main__":
62
  demo.queue(default_concurrency_limit=1).launch()
 
2
  from llama_cpp import Llama
3
  from huggingface_hub import hf_hub_download
4
  import os
5
+ import pickle
6
+ from langchain_huggingface import HuggingFaceEmbeddings
7
 
8
+ # ====================== CONFIG ======================
9
+ repo_id = "robertolofaro/books-model"
10
+
11
+ BACKENDS = {
12
+ "Fast Mode (No RAG)": None,
13
+ "Chroma - RAG": "Chroma",
14
+ "FAISS - RAG (HNSW)": "FAISS",
15
+ "Qdrant - RAG": "Qdrant"
16
+ }
17
+
18
+ CHROMA_PATH = "chroma_db"
19
+ FAISS_PATH = "faiss_index_hnsw"
20
+ QDRANT_PATH = "qdrant_db"
21
+ QDRANT_COLLECTION = "articles"
22
+
23
+ # ====================== LOAD METADATA FOR BOOK LIST ======================
24
+ def load_articles_list():
25
+ try:
26
+ with open("metadata.pkl", "rb") as f:
27
+ df = pickle.load(f)
28
+ articles = sorted(df['article_category'].unique().tolist())
29
+ return ["All categories"] + articles
30
+ except:
31
+ return ["All categories"]
32
+
33
+ ARTICLE_LIST = load_article_list()
34
+
35
+ # ====================== LOAD LLM ======================
36
  model_path = hf_hub_download(
37
+ repo_id=repo_id,
38
  filename="articles-Q4_K_M.gguf",
39
  repo_type="model",
40
  token=os.environ.get("HF_TOKEN")
 
42
 
43
  llm = Llama(
44
  model_path=model_path,
45
+ n_ctx=4096,
46
  n_threads=2,
47
  n_batch=512,
48
  n_ubatch=512,
49
+ verbose=False,
 
50
  )
51
 
52
+ # ====================== RAG CACHE ======================
53
+ vectorstores = {}
54
+
55
+ def get_vectorstore(backend_name: str):
56
+ if backend_name in vectorstores:
57
+ return vectorstores[backend_name]
58
+ # ... (same loading logic as before - Chroma, FAISS, Qdrant) ...
59
+ # I'll keep it short here for brevity, but same as previous version
60
+ try:
61
+ embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-small-en-v1.5", encode_kwargs={'normalize_embeddings': True})
62
+
63
+ if backend_name == "Chroma":
64
+ from langchain_community.vectorstores import Chroma
65
+ vs = Chroma(persist_directory=CHROMA_PATH, embedding_function=embeddings)
66
+ elif backend_name == "FAISS":
67
+ from langchain_community.vectorstores import FAISS
68
+ vs = FAISS.load_local(FAISS_PATH, embeddings, allow_dangerous_deserialization=True)
69
+ elif backend_name == "Qdrant":
70
+ from langchain_community.vectorstores import Qdrant
71
+ vs = Qdrant(path=QDRANT_PATH, collection_name=QDRANT_COLLECTION, embeddings=embeddings)
72
+ else:
73
+ return None
74
+
75
+ vectorstores[backend_name] = vs
76
+ return vs
77
+ except:
78
+ return None
79
+
80
+
81
+ # ====================== SYSTEM PROMPT ======================
82
  SYSTEM_PROMPT = """You are the reference expert for the articles contained in this database, all extracted from the website robertolofaro.com, and all focused on change.
83
  #Your Mission:
84
+ When a user asks a question, your goal is to provide a structured response based ONLY on the articles provided in your training. Do not provide general advice from outside these sources.
85
  # Response Format:
86
  1. Executive Summary: A 2-3 sentence overview answering the core query.
87
+ 2. Guidelines & Hints: A markdown list of specific "answers/guidelines/hints" found in the source material.
88
  """
89
 
90
+
91
+ # ====================== GENERATION FUNCTION ======================
92
+ def generate_response(message, history, rag_mode, book_filter, max_tokens, temperature, top_p, repeat_penalty):
93
  full_prompt = f"<|im_start|>system\n{SYSTEM_PROMPT}<|im_end|>\n"
94
+
95
+ for msg in history[-4:]:
96
+ full_prompt += f"<|im_start|>{msg['role']}\n{msg['content']}<|im_end|>\n"
97
+
98
+ backend = BACKENDS.get(rag_mode)
99
+ context = ""
100
+
101
+ if backend:
102
+ vs = get_vectorstore(backend)
103
+ if vs:
104
+ try:
105
+ filter_dict = {"article_category": article_filter} if article_filter != "All categories" else None
106
+ docs = vs.similarity_search(message, k=5, filter=filter_dict)
107
+ context = "\n\n".join([
108
+ f"[Category: {doc.metadata.get('article_category', 'N/A')}] {doc.page_content[:700]}"
109
+ for doc in docs
110
+ ])
111
+ except:
112
+ pass
113
+
114
+ if context:
115
+ full_prompt += f"<|im_start|>user\nContext:\n{context}\n\nQuestion: {message}<|im_end|>\n"
116
+ else:
117
+ full_prompt += f"<|im_start|>user\n{message}<|im_end|>\n"
118
+
119
+ full_prompt += "<|im_start|>assistant\n"
120
 
121
  partial_text = ""
122
  for chunk in llm(
123
  full_prompt,
124
+ max_tokens=int(max_tokens),
125
+ temperature=float(temperature),
126
+ top_p=float(top_p),
127
+ repeat_penalty=float(repeat_penalty),
128
  stop=["<|im_end|>", "<|im_start|>"],
129
  stream=True,
 
130
  ):
131
  token = chunk['choices'][0]['text']
132
  partial_text += token
133
  yield partial_text
134
 
135
 
136
+ # ====================== GRADIO INTERFACE ======================
137
+ with gr.Blocks(title="Article Q&A model") as demo:
138
+ gr.Markdown("# sourcing 350+ articles on change")
139
+ gr.Markdown("Qwen3.5-4B DoRA fine-tuned on 350+ articles")
140
+
141
+ with gr.Row():
142
+ rag_mode = gr.Radio(
143
+ choices=list(BACKENDS.keys()),
144
+ value="Fast Mode (No RAG)",
145
+ label="Mode"
146
+ )
147
+ book_filter = gr.Dropdown(
148
+ choices=BOOK_LIST,
149
+ value="All categories",
150
+ label="Focus on category"
151
+ )
152
+
153
+ with gr.Accordion("Advanced Generation Parameters", open=False):
154
+ max_tokens = gr.Slider(256, 2048, value=900, step=64, label="Max Tokens")
155
+ temperature = gr.Slider(0.0, 1.0, value=0.65, step=0.05, label="Temperature")
156
+ top_p = gr.Slider(0.0, 1.0, value=0.9, step=0.05, label="Top-p")
157
+ repeat_penalty = gr.Slider(1.0, 2.0, value=1.1, step=0.05, label="Repeat Penalty")
158
+
159
+ gr.ChatInterface(
160
+ fn=generate_response,
161
+ additional_inputs=[rag_mode, book_filter, max_tokens, temperature, top_p, repeat_penalty],
162
+ examples=[
163
+ ["What is the potential for Italy?"],
164
+ ["What is the potential for Turin?"]
165
+ ],
166
+ )
167
 
168
  if __name__ == "__main__":
169
  demo.queue(default_concurrency_limit=1).launch()