themehmi commited on
Commit
130bc23
Β·
verified Β·
1 Parent(s): 3e1ac70

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +151 -0
  2. requirements.txt +9 -0
app.py ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import torch
3
+ import os
4
+ from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
5
+ from langchain_community.document_loaders import DirectoryLoader
6
+ from langchain_text_splitters import RecursiveCharacterTextSplitter, Language
7
+ from langchain_huggingface import HuggingFaceEmbeddings, HuggingFacePipeline
8
+ from langchain_community.vectorstores import FAISS
9
+ from langchain_core.runnables import RunnablePassthrough
10
+ from langchain_core.output_parsers import StrOutputParser
11
+ from langchain_core.prompts import PromptTemplate
12
+
13
+ # 1. HARDWARE OPTIMIZED LLM LOADING
14
+
15
+ def load_llm():
16
+ model_id = "Qwen/Qwen2.5-Coder-0.5B-Instruct"
17
+ tokenizer = AutoTokenizer.from_pretrained(model_id)
18
+
19
+ model = AutoModelForCausalLM.from_pretrained(
20
+ model_id,
21
+ device_map="auto",
22
+ torch_dtype="auto",
23
+ low_cpu_mem_usage=True
24
+ )
25
+
26
+ pipe = pipeline(
27
+ "text-generation",
28
+ model=model,
29
+ tokenizer=tokenizer,
30
+ max_new_tokens=300,
31
+ temperature=0.1,
32
+ repetition_penalty=1.1,
33
+ return_full_text=False
34
+ )
35
+ return HuggingFacePipeline(pipeline=pipe)
36
+
37
+ # 2. CODE INGESTION & VECTOR DATABASE
38
+
39
+ def setup_vector_db():
40
+ if not os.path.exists('./repo'):
41
+ os.makedirs('./repo')
42
+
43
+ loader = DirectoryLoader('./repo', glob="**/*.py", show_progress=True)
44
+ docs = loader.load()
45
+
46
+ if not docs:
47
+ return None, 0
48
+
49
+ python_splitter = RecursiveCharacterTextSplitter.from_language(
50
+ language=Language.PYTHON,
51
+ chunk_size=500,
52
+ chunk_overlap=50
53
+ )
54
+ texts = python_splitter.split_documents(docs)
55
+
56
+ embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
57
+ db = FAISS.from_documents(texts, embeddings)
58
+
59
+ return db, len(docs)
60
+
61
+ # 3. GLOBAL INITIALIZATION
62
+ print("Initializing models...")
63
+ device_status = "🟒 GPU Active" if torch.cuda.is_available() else "🟑 CPU Mode"
64
+ llm = load_llm()
65
+ vector_db, file_count = setup_vector_db()
66
+
67
+ prompt_template = """Use the following codebase context to answer the question.
68
+ If you don't know the answer, just say that you don't know, don't try to make up code.
69
+
70
+ Context: {context}
71
+
72
+ Question: {input}
73
+ Helpful Developer Answer:"""
74
+
75
+ prompt = PromptTemplate.from_template(prompt_template)
76
+
77
+ def format_docs(docs):
78
+ return "\n\n".join(doc.page_content for doc in docs)
79
+
80
+ if vector_db:
81
+ retriever = vector_db.as_retriever(search_kwargs={"k": 3})
82
+ qa_chain = (
83
+ {"context": retriever, "input": RunnablePassthrough()}
84
+ | RunnablePassthrough.assign(
85
+ answer=(
86
+ RunnablePassthrough.assign(context=lambda x: format_docs(x["context"]))
87
+ | prompt
88
+ | llm
89
+ | StrOutputParser()
90
+ )
91
+ )
92
+ )
93
+ else:
94
+ qa_chain = None
95
+
96
+ # 4. CHAT LOGIC
97
+ def respond(message, chat_history):
98
+ if not vector_db:
99
+ bot_message = "πŸ‘‹ Welcome! Please upload some Python files to the `./repo` directory and restart the server to start chatting."
100
+ chat_history.append((message, bot_message))
101
+ return "", chat_history
102
+
103
+ # Fetch response from RAG
104
+ response = qa_chain.invoke(message)
105
+ answer = response["answer"]
106
+ sources = response["context"]
107
+
108
+ final_answer = answer
109
+
110
+ if sources:
111
+ final_answer += "\n\n<details><summary>πŸ” View Source Code Referenced</summary>\n\n"
112
+ for idx, doc in enumerate(sources):
113
+ source_file = doc.metadata.get("source", "Unknown File")
114
+ final_answer += f"**Snippet {idx + 1}** from `{source_file}`:\n"
115
+ final_answer += f"```python\n{doc.page_content}\n```\n\n"
116
+ final_answer += "</details>"
117
+
118
+ chat_history.append((message, final_answer))
119
+ return "", chat_history
120
+
121
+ # 5. GRADIO UI
122
+ custom_css = """
123
+ .status-box { padding: 10px; border-radius: 8px; background-color: #f0f0f0; margin-bottom: 10px; }
124
+ .dark .status-box { background-color: #1e293b; color: #cbd5e1; }
125
+ """
126
+
127
+ with gr.Blocks(title="Codebase Assistant", css=custom_css) as demo:
128
+ with gr.Row():
129
+ with gr.Column(scale=1):
130
+ gr.Markdown("# DevAssist AI\nYour personal Qwen-powered codebase expert.")
131
+ gr.Markdown("---")
132
+
133
+ with gr.Column(elem_classes=["status-box"]):
134
+ gr.Markdown("### System Status")
135
+ gr.Markdown(f"**Hardware:** {device_status}")
136
+ if vector_db:
137
+ gr.Markdown(f"**Repo Status:** {file_count} files indexed βœ…")
138
+ else:
139
+ gr.Markdown("**Repo Status:** Empty ❌\n\nDrop your `.py` files into the `/repo` folder to begin analyzing.")
140
+
141
+ with gr.Column(scale=3):
142
+ gr.Markdown("### πŸ’» Chat with your Codebase\nAsk architecture questions, find bugs, or request code explanations.")
143
+ chatbot = gr.Chatbot(height=500, show_label=False)
144
+ msg = gr.Textbox(placeholder="E.g., What does the main function do?", show_label=False)
145
+ clear = gr.Button("Clear Chat")
146
+
147
+ msg.submit(respond, inputs=[msg, chatbot], outputs=[msg, chatbot])
148
+ clear.click(lambda: None, None, chatbot, queue=False)
149
+
150
+ if __name__ == "__main__":
151
+ demo.launch(server_name="0.0.0.0")
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ gradio
2
+ torch
3
+ transformers
4
+ accelerate
5
+ langchain
6
+ langchain-community
7
+ langchain-huggingface
8
+ faiss-cpu
9
+ sentence-transformers