rahul7star commited on
Commit
1ab5501
·
verified ·
1 Parent(s): 2f22182

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +167 -288
app.py CHANGED
@@ -1,319 +1,198 @@
1
- import os
2
- import re
3
- import textwrap
4
- import traceback
5
  import gradio as gr
6
  from openai import OpenAI
7
-
8
- # ---------------------------
9
- # Configuration
10
- # ---------------------------
11
- HF_ENV_VAR = "OPENAI_API_KEY"
12
- HF_TOKEN = os.environ.get(HF_ENV_VAR)
 
 
13
  if not HF_TOKEN:
14
- raise RuntimeError(
15
- f"Environment variable {HF_ENV_VAR} not found. Set {HF_ENV_VAR} before running."
16
- )
17
-
18
- MODEL_ID = "openai/gpt-oss-20b"
19
- client = OpenAI(base_url="https://router.huggingface.co/v1", api_key=HF_TOKEN)
20
 
21
- # ---------------------------
22
- # Load research files (size-capped)
23
- # ---------------------------
24
- MAX_RESEARCH_CHARS = 6000 # adjust to stay within token limits
25
 
26
- def load_research_context() -> str:
27
- """Concatenate .txt/.md files in repo root up to MAX_RESEARCH_CHARS."""
28
- repo_root = os.path.dirname(os.path.abspath(__file__))
29
- chunks, total = [], 0
30
- for fname in sorted(os.listdir(repo_root)):
31
- if fname.lower().endswith((".txt", ".md")):
32
- with open(os.path.join(repo_root, fname), "r", encoding="utf-8") as f:
33
- txt = f.read()
34
- if total + len(txt) > MAX_RESEARCH_CHARS:
35
- txt = txt[: MAX_RESEARCH_CHARS - total]
36
- chunks.append(f"\n--- {fname} ---\n{txt}")
37
- total += len(txt)
38
- if total >= MAX_RESEARCH_CHARS:
39
- break
40
- return "\n".join(chunks)
41
 
42
- RESEARCH_CONTEXT = load_research_context()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
- # ---------------------------
45
- # System prompt
46
- # ---------------------------
47
- def get_system_prompt(mode: str = "chat") -> str:
48
- base = textwrap.dedent(f"""
49
- You are OhamLab AI, a Dialectical Agentic CrossSphere Intelligence AI.
 
 
 
50
 
51
- IMPORTANT GUIDELINES:
52
- - Deliver a complete, self-contained answer in one message whenever possible.
53
- - If the user types "continue", resume exactly where you left off and DO NOT repeat previously provided text.
54
- - NEVER include runnable code blocks, scripts, or function/class definitions unless the user explicitly asks.
55
- - If code is requested, ask one clarifying question if needed, then provide code.
56
- - Express algorithms in plain English unless code is explicitly requested.
57
- - If uncertain, label speculation and state confidence.
 
 
 
 
 
58
 
59
- Mode: {mode}
60
- --- RESEARCH CONTEXT (TRIMMED) ---
61
- {RESEARCH_CONTEXT}
62
- --- END CONTEXT ---
63
- """).strip()
64
- return base
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
 
66
- # ---------------------------
67
- # Conversation state
68
- # ---------------------------
69
- conversation_mode = "chat"
70
- history_messages = [{"role": "system", "content": get_system_prompt("chat")}]
71
- chat_history_for_ui = []
72
- MAX_HISTORY_CHARS = 9000
73
 
74
- def trim_history_by_chars(msgs, max_chars=MAX_HISTORY_CHARS):
75
- if not msgs:
76
- return msgs
77
- system = msgs[0]
78
- tail, total = [], len(system["content"])
79
  for m in reversed(msgs[1:]):
80
- seg_len = len(m.get("content", ""))
81
- if total + seg_len > max_chars:
82
- break
83
  tail.append(m)
84
- total += seg_len
85
- return [system] + list(reversed(tail))
86
 
87
- # ---------------------------
88
- # Code-detection utilities
89
- # ---------------------------
90
- CODE_PATTERN = re.compile(
91
- r"(```|```[a-zA-Z]*|^\s*def\s+|^\s*class\s+|^\s*import\s+|#include\s+|<\?php|<html>|^\s*using\s+namespace)",
92
- re.MULTILINE,
93
- )
94
 
95
- def is_code_like(text: str) -> bool:
96
- if not text:
97
- return False
98
- if CODE_PATTERN.search(text):
99
- return True
100
- lines = [ln for ln in text.splitlines() if ln.strip()]
101
- if len(lines) >= 4:
102
- codey = sum(
103
- 1 for ln in lines
104
- if ";" in ln or "{" in ln or "}" in ln or ln.strip().endswith(":")
105
- )
106
- return codey / len(lines) > 0.35
107
- return False
108
 
109
- def strip_code_blocks(text: str) -> str:
110
- text = re.sub(r"```.*?```", "", text, flags=re.DOTALL)
111
- text_lines = []
112
- for ln in text.splitlines():
113
- if re.match(r"^\s*(def |class |import |from |#include|using |<)", ln):
114
- continue
115
- if ln.strip().startswith("```") or ln.strip().endswith("```"):
116
- continue
117
- text_lines.append(ln)
118
- cleaned = "\n".join(text_lines).strip()
119
- return re.sub(r"\n{3,}", "\n\n", cleaned) or \
120
- "[Content removed: model produced code which has been stripped.]"
121
 
122
- # ---------------------------
123
- # Model call helper
124
- # ---------------------------
125
- def call_model_get_response(messages, max_tokens=2000, allow_code=False) -> str:
126
- msgs = trim_history_by_chars(messages)
127
  try:
128
  resp = client.chat.completions.create(
129
  model=MODEL_ID,
130
- messages=msgs,
131
- max_tokens=max_tokens,
132
- temperature=0.7,
133
  )
134
- content = resp.choices[0].message.content or ""
135
  except Exception as e:
136
- raise
137
-
138
- if not allow_code and is_code_like(content):
139
- rewrite_instruction = (
140
- "Rewrite the previous reply in clear prose — no code blocks, no imports, "
141
- "no function/class definitions. Keep all reasoning and numeric details."
142
- )
143
- rewrite_msgs = [
144
- msgs[0],
145
- {"role": "assistant", "content": content},
146
- {"role": "user", "content": rewrite_instruction},
147
- ]
148
- try:
149
- resp2 = client.chat.completions.create(
150
- model=MODEL_ID,
151
- messages=rewrite_msgs,
152
- max_tokens=max_tokens,
153
- temperature=0.7,
154
- )
155
- content2 = resp2.choices[0].message.content or ""
156
- except Exception:
157
- return strip_code_blocks(content) + \
158
- "\n\n⚠️ Note: rewrite failed; code removed."
159
- if is_code_like(content2):
160
- return strip_code_blocks(content2) + \
161
- "\n\n⚠️ Note: model persisted in producing code; sanitized."
162
- return content2
163
- return content
164
 
165
- # ---------------------------
166
- # Chat logic
167
- # ---------------------------
168
- def get_last_assistant_tail(max_chars=1200) -> str:
169
- for m in reversed(history_messages):
170
- if m["role"] == "assistant" and m.get("content"):
171
- return m["content"][-max_chars:]
172
- return ""
173
 
174
- CONTINUE_KEYWORDS = {"continue", "carry on", "go on", "proceed", "resume", "next"}
175
- def user_requested_code(user_text: str) -> bool:
176
- t = (user_text or "").lower()
177
- triggers = ["show code", "give me code", "provide code",
178
- "script", "python", "javascript", "implementation"]
179
- return any(k in t for k in triggers)
180
 
181
- def chat_with_model(user_message, chat_history):
182
- global history_messages, chat_history_for_ui, conversation_mode
183
- user_message = (user_message or "").strip()
184
- if not user_message:
185
- return chat_history, ""
186
-
187
- lower = user_message.lower().strip()
188
-
189
- # Mode switching
190
- if "switch to research mode" in lower:
191
- conversation_mode = "research"
192
- history_messages = [{"role": "system",
193
- "content": get_system_prompt("research")}]
194
- return chat_history + [("🟢 Mode switched", "🔬 Research Mode activated.")], ""
195
- if "switch to chat mode" in lower:
196
- conversation_mode = "chat"
197
- history_messages = [{"role": "system",
198
- "content": get_system_prompt("chat")}]
199
- return chat_history + [("🟢 Mode switched", "💬 Chat Mode activated.")], ""
200
-
201
- allow_code = user_requested_code(user_message)
202
- if lower in CONTINUE_KEYWORDS:
203
- last_tail = get_last_assistant_tail()
204
- resume_hint = (
205
- "User requested continuation. Resume exactly where you left off "
206
- "and DO NOT repeat earlier sections.\n\nLast assistant message tail:\n"
207
- + last_tail
208
- )
209
- history_messages.append({"role": "user", "content": resume_hint})
210
- else:
211
- history_messages.append({"role": "user", "content": user_message})
212
-
213
- history_messages[:] = trim_history_by_chars(history_messages)
214
-
215
- try:
216
- bot_text = call_model_get_response(
217
- history_messages, max_tokens=2000, allow_code=allow_code
218
- )
219
- except Exception as e:
220
- tb = traceback.format_exc()
221
- bot_text = f"⚠️ **Error**: {e}\n\nTraceback:\n{tb.splitlines()[-6:]}"
222
-
223
- history_messages.append({"role": "assistant", "content": bot_text})
224
- chat_history_for_ui.append((user_message, bot_text))
225
- return chat_history_for_ui, ""
226
-
227
- def reset_chat():
228
- global history_messages, chat_history_for_ui
229
- history_messages = [{"role": "system", "content": get_system_prompt(conversation_mode)}]
230
- chat_history_for_ui = []
231
  return []
232
 
233
- # ---------------------------
234
- # Gradio UI with working Send button
235
- # ---------------------------
236
-
237
- # Gradio UI
238
- # ---------------------------
239
  def build_ui():
240
- with gr.Blocks(
241
- theme=gr.themes.Soft(),
242
- css="""
243
- #chatbot {
244
- background-color: #f9f9fb;
245
- border-radius: 12px;
246
- padding: 10px;
247
- overflow-y: auto;
248
- }
249
- .user-bubble {
250
- background: #4a90e2;
251
- color: white;
252
- border-radius: 14px;
253
- padding: 8px 12px;
254
- margin: 6px;
255
- max-width: 75%;
256
- align-self: flex-end;
257
- font-size: 14px;
258
- }
259
- .bot-bubble {
260
- background: #e6e6e6;
261
- color: #333;
262
- border-radius: 14px;
263
- padding: 8px 12px;
264
- margin: 6px;
265
- max-width: 75%;
266
- align-self: flex-start;
267
- font-size: 14px;
268
- }
269
- #controls {
270
- display: flex;
271
- gap: 8px;
272
- align-items: center;
273
- margin-top: 6px;
274
- }
275
- #topbar {
276
- display: flex;
277
- justify-content: flex-end;
278
- gap: 8px;
279
- margin-bottom: 6px;
280
- }
281
- """
282
- ) as demo:
283
- # Top bar with close + clear
284
- with gr.Row(elem_id="topbar"):
285
- close_btn = gr.Button("❌", size="sm")
286
- clear_btn = gr.Button("🧹 Clear", size="sm")
287
-
288
- chatbot = gr.Chatbot(
289
- label="",
290
- height=350, # reduced height so input is visible
291
- elem_id="chatbot",
292
- type="tuples",
293
- bubble_full_width=False,
294
- avatar_images=("👤", "🤖"),
295
- )
296
-
297
- with gr.Row(elem_id="controls"):
298
- msg = gr.Textbox(
299
- placeholder="Type your message here...",
300
- lines=2,
301
- scale=8,
302
- )
303
- submit_btn = gr.Button("🚀 Send", variant="primary", scale=2)
304
-
305
- # Wire buttons
306
- submit_btn.click(chat_with_model, inputs=[msg, chatbot], outputs=[msg, chatbot])
307
- msg.submit(chat_with_model, inputs=[msg, chatbot], outputs=[msg, chatbot])
308
- clear_btn.click(reset_chat, inputs=None, outputs=chatbot)
309
-
310
- demo.launch(server_name="0.0.0.0", server_port=7860, share=False)
311
  return demo
312
 
313
-
314
- # ---------------------------
315
- # Entrypoint
316
- # ---------------------------
317
  if __name__ == "__main__":
318
- print("Starting Aerelyth with size-capped research context…")
319
- build_ui()
 
1
+ import os, re, time, json, textwrap, traceback, numpy as np
2
+ from typing import List
 
 
3
  import gradio as gr
4
  from openai import OpenAI
5
+ from huggingface_hub import list_repo_files, hf_hub_download
6
+
7
+ # ==========================================================
8
+ # CONFIG
9
+ # ==========================================================
10
+ REPO_ID = "rahul7star/OhamLab-LLM"
11
+ API_KEY_ENV = "OPENAI_API_KEY"
12
+ HF_TOKEN = os.getenv(API_KEY_ENV)
13
  if not HF_TOKEN:
14
+ raise RuntimeError(f"Missing {API_KEY_ENV}")
 
 
 
 
 
15
 
16
+ MODEL_ID = "openai/gpt-4o-mini" # efficient chat model
17
+ EMBED_MODEL = "text-embedding-3-small"
18
+ CACHE_FILE = "/tmp/ohamlab_emb_cache.json"
 
19
 
20
+ client = OpenAI(api_key=HF_TOKEN)
21
+ LOG_FILE = "ohamlab_chat.log"
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
+ def log(msg: str):
24
+ ts = time.strftime("%Y-%m-%d %H:%M:%S")
25
+ line = f"[{ts}] {msg}"
26
+ print(line)
27
+ try:
28
+ with open(LOG_FILE, "a", encoding="utf-8") as f:
29
+ f.write(line + "\n")
30
+ except Exception:
31
+ pass
32
+
33
+ # ==========================================================
34
+ # KNOWLEDGE SCANNER + EMBEDDINGS
35
+ # ==========================================================
36
+ def load_all_md_from_repo(repo_id: str) -> List[str]:
37
+ """Scan all .md files in the repo and return concatenated chunks."""
38
+ try:
39
+ files = list_repo_files(repo_id=repo_id, repo_type="model", token=HF_TOKEN)
40
+ md_files = [f for f in files if f.lower().endswith(".md")]
41
+ if not md_files:
42
+ log("⚠️ No markdown files found.")
43
+ return []
44
+
45
+ chunks = []
46
+ for fname in md_files:
47
+ try:
48
+ local_path = hf_hub_download(repo_id, filename=fname, token=HF_TOKEN)
49
+ with open(local_path, "r", encoding="utf-8") as f:
50
+ text = f.read()
51
+ text = re.sub(r"<[^>]+>", "", text) # strip HTML
52
+ # Split into ~500 char chunks
53
+ buf = ""
54
+ for line in text.splitlines():
55
+ buf += line.strip() + " "
56
+ if len(buf) > 500:
57
+ chunks.append(buf.strip())
58
+ buf = ""
59
+ if buf:
60
+ chunks.append(buf.strip())
61
+ log(f"Loaded {fname} ({len(text)} chars → {len(chunks)} chunks)")
62
+ except Exception as e:
63
+ log(f"⚠️ Failed to load {fname}: {e}")
64
+ return chunks
65
+ except Exception as e:
66
+ log(f"⚠️ Repo scan failed: {e}")
67
+ return []
68
 
69
+ def get_embeddings(texts: List[str]) -> np.ndarray:
70
+ if not texts:
71
+ return np.zeros((1, 1536))
72
+ try:
73
+ res = client.embeddings.create(model=EMBED_MODEL, input=texts)
74
+ return np.array([r.embedding for r in res.data])
75
+ except Exception as e:
76
+ log(f"Embedding error: {e}")
77
+ return np.zeros((len(texts), 1536))
78
 
79
+ def load_knowledge_cache() -> tuple[list[str], np.ndarray]:
80
+ """Load embeddings from cache or regenerate from repo."""
81
+ if os.path.exists(CACHE_FILE):
82
+ try:
83
+ with open(CACHE_FILE, "r", encoding="utf-8") as f:
84
+ data = json.load(f)
85
+ chunks = data["chunks"]
86
+ embs = np.array(data["embs"])
87
+ log(f"Loaded cached embeddings: {len(chunks)} chunks.")
88
+ return chunks, embs
89
+ except Exception:
90
+ pass
91
 
92
+ log("Scanning Markdown files in repo for knowledge base...")
93
+ chunks = load_all_md_from_repo(REPO_ID)
94
+ embs = get_embeddings(chunks)
95
+ try:
96
+ json.dump({"chunks": chunks, "embs": embs.tolist()}, open(CACHE_FILE, "w"))
97
+ except Exception:
98
+ pass
99
+ log(f"Knowledge base ready: {len(chunks)} chunks.")
100
+ return chunks, embs
101
+
102
+ KNOWLEDGE_CHUNKS, KNOWLEDGE_EMBS = load_knowledge_cache()
103
+
104
+ # ==========================================================
105
+ # RETRIEVAL
106
+ # ==========================================================
107
+ def get_relevant_context(query: str, top_k=3) -> str:
108
+ if not KNOWLEDGE_CHUNKS or not query.strip():
109
+ return ""
110
+ q_emb = get_embeddings([query])[0]
111
+ sims = np.dot(KNOWLEDGE_EMBS, q_emb) / (
112
+ np.linalg.norm(KNOWLEDGE_EMBS, axis=1) * np.linalg.norm(q_emb)
113
+ )
114
+ top_idx = np.argsort(sims)[-top_k:][::-1]
115
+ return "\n\n".join(KNOWLEDGE_CHUNKS[i] for i in top_idx)
116
+
117
+ # ==========================================================
118
+ # CHAT ENGINE
119
+ # ==========================================================
120
+ SYSTEM_PROMPT = (
121
+ "You are OhamLab AI — a concise, factual chat assistant.\n"
122
+ "When relevant, use the OhamLab knowledge base provided in context.\n"
123
+ "Never show code unless explicitly requested. Keep tone professional and calm."
124
+ )
125
 
126
+ history = [{"role": "system", "content": SYSTEM_PROMPT}]
127
+ chat_ui_history = []
128
+ MAX_HISTORY_CHARS = 3000
 
 
 
 
129
 
130
+ def trim_msgs(msgs, max_chars=MAX_HISTORY_CHARS):
131
+ sys = msgs[0]
132
+ tail, total = [], len(sys["content"])
 
 
133
  for m in reversed(msgs[1:]):
134
+ seg = len(m["content"])
135
+ if total + seg > max_chars: break
 
136
  tail.append(m)
137
+ total += seg
138
+ return [sys] + list(reversed(tail))
139
 
140
+ def chat(user_msg, chat_hist):
141
+ global history, chat_ui_history
142
+ user_msg = (user_msg or "").strip()
143
+ if not user_msg:
144
+ return chat_ui_history, ""
 
 
145
 
146
+ context = get_relevant_context(user_msg)
147
+ if context:
148
+ user_msg += f"\n\n[Context]\n{context[:1500]}"
 
 
 
 
 
 
 
 
 
 
149
 
150
+ history.append({"role": "user", "content": user_msg})
151
+ trimmed = trim_msgs(history)
 
 
 
 
 
 
 
 
 
 
152
 
 
 
 
 
 
153
  try:
154
  resp = client.chat.completions.create(
155
  model=MODEL_ID,
156
+ messages=trimmed,
157
+ max_tokens=600,
158
+ temperature=0.6,
159
  )
160
+ reply = resp.choices[0].message.content.strip()
161
  except Exception as e:
162
+ log(f"Chat error: {e}")
163
+ reply = "I'm experiencing a temporary issue. Please try again shortly."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
 
165
+ history.append({"role": "assistant", "content": reply})
166
+ chat_ui_history.append((user_msg, reply))
167
+ history[:] = trim_msgs(history)
 
 
 
 
 
168
 
169
+ log(f"USER: {user_msg[:60]} | BOT: {reply[:60]}")
170
+ return chat_ui_history, ""
 
 
 
 
171
 
172
+ def reset():
173
+ global history, chat_ui_history
174
+ history = [{"role": "system", "content": SYSTEM_PROMPT}]
175
+ chat_ui_history = []
176
+ log("Chat reset.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  return []
178
 
179
+ # ==========================================================
180
+ # UI
181
+ # ==========================================================
 
 
 
182
  def build_ui():
183
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
184
+ gr.Markdown("### 💬 OhamLab AI — Knowledge Chat (Markdown-Aware)")
185
+ chatbot = gr.Chatbot(height=350)
186
+ msg = gr.Textbox(placeholder="Ask anything about OhamLab...", lines=1)
187
+ send = gr.Button("Send", variant="primary")
188
+ clear = gr.Button("Clear")
189
+
190
+ send.click(chat, inputs=[msg, chatbot], outputs=[chatbot, msg])
191
+ msg.submit(chat, inputs=[msg, chatbot], outputs=[chatbot, msg])
192
+ clear.click(reset, None, chatbot)
193
+ demo.launch(server_name="0.0.0.0", server_port=7860)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  return demo
195
 
 
 
 
 
196
  if __name__ == "__main__":
197
+ log("Starting OhamLab AI Chat Agent (Markdown Knowledge)...")
198
+ build_ui()