rahul7star commited on
Commit
09fed57
·
verified ·
1 Parent(s): ef48f71

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +192 -171
app.py CHANGED
@@ -1,4 +1,5 @@
1
  import os
 
2
  import textwrap
3
  import traceback
4
  import gradio as gr
@@ -7,154 +8,221 @@ from openai import OpenAI
7
  # ---------------------------
8
  # Configuration
9
  # ---------------------------
10
- HF_ENV_VAR = "OPENAI_API_KEY" # actually holds your Hugging Face token
11
  HF_TOKEN = os.environ.get(HF_ENV_VAR)
12
  if not HF_TOKEN:
13
- print(
14
- f"ERROR: environment variable {HF_ENV_VAR} not found. "
15
- "Set it to your Hugging Face token before running."
16
  )
17
 
18
- MODEL_ID = "openai/gpt-oss-20b" # served through Hugging Face router
19
-
20
- # ---------------------------
21
- # Initialize Hugging Face router client
22
- # ---------------------------
23
  client = OpenAI(base_url="https://router.huggingface.co/v1", api_key=HF_TOKEN)
24
 
25
  # ---------------------------
26
- # Research loader (project root)
27
  # ---------------------------
28
- ROOT_DIR = "."
29
- ALLOWED_EXT = (".txt", ".md")
30
-
31
- def load_research_from_root(max_total_chars: int = 12000):
32
- files = []
33
- for name in sorted(os.listdir(ROOT_DIR)):
34
- if name.lower().endswith(ALLOWED_EXT) and name != "requirements.txt":
35
- if name == os.path.basename(__file__):
36
- continue
37
- files.append(name)
38
-
39
- if not files:
40
- return "No research files (.txt/.md) found in project root."
41
 
42
- combined_parts, total_len = [], 0
43
- for fname in files:
44
- try:
45
- with open(os.path.join(ROOT_DIR, fname), "r", encoding="utf-8", errors="ignore") as f:
 
 
 
46
  txt = f.read()
47
- except Exception as e:
48
- txt = f"[Error reading {fname}: {e}]"
49
-
50
- if len(txt) > 8000:
51
- sample = txt[:8000] + "\n\n[TRUNCATED]\n"
52
- else:
53
- sample = txt
54
-
55
- part = f"--- {fname} ---\n{sample.strip()}\n"
56
- combined_parts.append(part)
57
- total_len += len(part)
58
- if total_len >= max_total_chars:
59
- break
60
-
61
- combined = "\n\n".join(combined_parts)
62
- if len(combined) > max_total_chars:
63
- combined = combined[:max_total_chars] + "\n\n[TRUNCATED]"
64
- return combined
65
 
 
66
 
67
  # ---------------------------
68
- # System prompt templates
69
  # ---------------------------
70
- research_context = load_research_from_root(max_total_chars=12000)
 
 
71
 
72
- def get_system_prompt(mode="chat"):
73
- if mode == "chat":
74
- return textwrap.dedent(f"""
75
- OhamLab A Quantum Intelligence AI.
76
- Mode: Conversational.
77
- Guidelines:
78
- - Answer clearly in natural paragraphs (3–6 sentences).
79
- - Do NOT use tables, spreadsheets, or rigid formatting unless explicitly asked.
80
- - Always address the user’s question directly before expanding.
81
- - Be insightful, empathetic, and concise.
82
 
83
- --- BEGIN RESEARCH CONTEXT (TRIMMED) ---
84
- {research_context}
85
- --- END RESEARCH CONTEXT ---
86
- """).strip()
87
- else:
88
- return textwrap.dedent(f"""
89
- You are OhamLab, a Quantum Dialectical Agentic Crosssphere Intelligence AI.
90
- Mode: Research / Analytical.
91
- Guidelines:
92
- - Write structured, multi-sphere reasoning (science, philosophy, psychology, etc).
93
- - Use sections, subpoints, and dialectical chains.
94
- - Provide deep analysis, even if it looks like a research paper.
95
- - Always reference the research context if relevant.
96
-
97
- --- BEGIN RESEARCH CONTEXT (TRIMMED) ---
98
- {research_context}
99
- --- END RESEARCH CONTEXT ---
100
- """).strip()
101
 
102
  # ---------------------------
103
- # State
104
  # ---------------------------
105
- conversation_mode = "chat" # can toggle
106
  history_messages = [{"role": "system", "content": get_system_prompt("chat")}]
107
  chat_history_for_ui = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
 
109
  # ---------------------------
110
  # Model call helper
111
  # ---------------------------
112
- def call_model_get_response(model_id: str, messages: list, max_tokens: int = 700):
113
- resp = client.chat.completions.create(
114
- model=model_id,
115
- messages=messages,
116
- max_tokens=max_tokens,
117
- temperature=0.7,
118
- )
119
- choice = resp.choices[0]
120
  try:
121
- return choice.message.content.strip()
122
- except Exception:
123
- return str(choice)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
 
125
  # ---------------------------
126
  # Chat logic
127
  # ---------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  def chat_with_model(user_message, chat_history):
129
  global history_messages, chat_history_for_ui, conversation_mode
 
 
 
130
 
131
- if not user_message or str(user_message).strip() == "":
132
- return "", chat_history
133
 
134
- # Mode switching commands
135
- if "switch to research mode" in user_message.lower():
136
  conversation_mode = "research"
137
- history_messages = [{"role": "system", "content": get_system_prompt("research")}]
138
- return "", chat_history + [("🟢 Mode switched", "🔬 Research Mode activated.")]
139
- elif "switch to chat mode" in user_message.lower():
 
140
  conversation_mode = "chat"
141
- history_messages = [{"role": "system", "content": get_system_prompt("chat")}]
142
- return "", chat_history + [("🟢 Mode switched", "💬 Chat Mode activated.")]
 
 
 
 
 
 
 
 
 
 
 
 
 
143
 
144
- # Append user message
145
- history_messages.append({"role": "user", "content": user_message})
146
 
147
  try:
148
- bot_text = call_model_get_response(MODEL_ID, history_messages, max_tokens=700)
 
 
149
  except Exception as e:
150
  tb = traceback.format_exc()
151
- bot_text = f"⚠️ **Error**: {str(e)}\n\nTraceback:\n{tb.splitlines()[-6:]}"
152
 
153
- # Append response
154
  history_messages.append({"role": "assistant", "content": bot_text})
155
  chat_history_for_ui.append((user_message, bot_text))
156
-
157
- return "", chat_history_for_ui
158
 
159
  def reset_chat():
160
  global history_messages, chat_history_for_ui
@@ -163,85 +231,38 @@ def reset_chat():
163
  return []
164
 
165
  # ---------------------------
166
- # Gradio UI
167
  # ---------------------------
168
  def build_ui():
169
- with gr.Blocks(
170
- theme=gr.themes.Soft(),
171
- css="""
172
- #chatbot {
173
- background-color: #f9f9fb;
174
- border-radius: 12px;
175
- padding: 10px;
176
- overflow-y: auto;
177
- }
178
- .user-bubble {
179
- background: #4a90e2;
180
- color: white;
181
- border-radius: 14px;
182
- padding: 8px 12px;
183
- margin: 6px;
184
- max-width: 75%;
185
- align-self: flex-end;
186
- font-size: 14px;
187
- }
188
- .bot-bubble {
189
- background: #e6e6e6;
190
- color: #333;
191
- border-radius: 14px;
192
- padding: 8px 12px;
193
- margin: 6px;
194
- max-width: 75%;
195
- align-self: flex-start;
196
- font-size: 14px;
197
- }
198
- #controls {
199
- display: flex;
200
- gap: 8px;
201
- align-items: center;
202
- margin-top: 6px;
203
- }
204
- #topbar {
205
- display: flex;
206
- justify-content: flex-end;
207
- gap: 8px;
208
- margin-bottom: 6px;
209
- }
210
- """
211
- ) as demo:
212
- # Top bar with close + clear
213
- with gr.Row(elem_id="topbar"):
214
- close_btn = gr.Button("❌", size="sm")
215
- clear_btn = gr.Button("🧹 Clear", size="sm")
216
-
217
- chatbot = gr.Chatbot(
218
- label="",
219
- height=350, # reduced height so input is visible
220
- elem_id="chatbot",
221
- type="tuples",
222
- bubble_full_width=False,
223
- avatar_images=("👤", "🤖"),
224
- )
225
 
226
- with gr.Row(elem_id="controls"):
227
  msg = gr.Textbox(
228
- placeholder="Type your message here...",
229
- lines=2,
230
  scale=8,
 
231
  )
232
- submit_btn = gr.Button("🚀 Send", variant="primary", scale=2)
 
 
 
233
 
234
- # Wire buttons
235
- submit_btn.click(chat_with_model, inputs=[msg, chatbot], outputs=[msg, chatbot])
236
- msg.submit(chat_with_model, inputs=[msg, chatbot], outputs=[msg, chatbot])
237
- clear_btn.click(reset_chat, inputs=None, outputs=chatbot)
238
 
239
  demo.launch(server_name="0.0.0.0", server_port=7860, share=False)
240
- return demo
241
 
242
  # ---------------------------
243
  # Entrypoint
244
  # ---------------------------
245
  if __name__ == "__main__":
246
- print(f"Starting Aerelyth with Hugging Face model: {MODEL_ID}")
247
  build_ui()
 
1
  import os
2
+ import re
3
  import textwrap
4
  import traceback
5
  import gradio as gr
 
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 Aerelyth, 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
 
231
  return []
232
 
233
  # ---------------------------
234
+ # Gradio UI with working Send button
235
  # ---------------------------
236
  def build_ui():
237
+ custom_css = """
238
+ #chatbot { background-color:#11121a; color:#e6eef8;
239
+ border-radius:10px; padding:10px; }
240
+ """
241
+ with gr.Blocks(theme=gr.themes.Soft(), css=custom_css) as demo:
242
+ gr.Markdown("## 🤖 Aerelyth — Dialectical Agentic CrossSphere AI")
243
+ chatbot = gr.Chatbot(height=520, elem_id="chatbot", type="tuples")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
 
245
+ with gr.Row():
246
  msg = gr.Textbox(
247
+ placeholder="Type message (press Enter or click Send)…",
248
+ lines=3,
249
  scale=8,
250
+ show_label=False
251
  )
252
+ send = gr.Button("Send", variant="primary", scale=1)
253
+
254
+ with gr.Row():
255
+ clear = gr.Button("Clear")
256
 
257
+ send.click(chat_with_model, inputs=[msg, chatbot], outputs=[chatbot, msg])
258
+ msg.submit(chat_with_model, inputs=[msg, chatbot], outputs=[chatbot, msg])
259
+ clear.click(reset_chat, outputs=chatbot)
 
260
 
261
  demo.launch(server_name="0.0.0.0", server_port=7860, share=False)
 
262
 
263
  # ---------------------------
264
  # Entrypoint
265
  # ---------------------------
266
  if __name__ == "__main__":
267
+ print("Starting Aerelyth with size-capped research context…")
268
  build_ui()