Leonydis137 commited on
Commit
837c39f
Β·
verified Β·
1 Parent(s): ae3ae65

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +179 -76
app.py CHANGED
@@ -1,4 +1,4 @@
1
- # app.py (Compatible with OpenAI v0.x and v1.x)
2
  import gradio as gr
3
  import openai
4
  import threading
@@ -8,27 +8,22 @@ import faiss
8
  import os
9
  import pickle
10
  from datetime import datetime
 
11
 
12
  # === CONFIG ===
13
  EMBEDDING_MODEL = "text-embedding-3-small"
14
- CHAT_MODEL = "gpt-4o" # Updated to current model
15
  MEMORY_FILE = "memory.pkl"
16
  INDEX_FILE = "memory.index"
17
-
18
- # Initialize OpenAI API
19
  openai.api_key = os.environ.get("OPENAI_API_KEY")
20
 
21
  # === EMBEDDING UTILS ===
22
  def get_embedding(text, model=EMBEDDING_MODEL):
23
  text = text.replace("\n", " ")
24
-
25
- # Compatible API call for both v0.x and v1.x
26
  try:
27
- # Try v1.x API first
28
  response = openai.embeddings.create(input=[text], model=model)
29
  return response.data[0].embedding
30
  except AttributeError:
31
- # Fallback to v0.x API
32
  response = openai.Embedding.create(input=[text], model=model)
33
  return response['data'][0]['embedding']
34
 
@@ -44,59 +39,80 @@ try:
44
  with open(MEMORY_FILE, "rb") as f:
45
  memory_data = pickle.load(f)
46
  except:
47
- memory_index = faiss.IndexFlatL2(1536) # 1536 dimensions for text-embedding-3-small
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
- # === SYSTEM PROMPTS ===
50
- AGENT_A_PROMPT = "You are Agent A, initiating conversations with thoughtful questions."
51
- AGENT_B_PROMPT = "You are Agent B, responding to Agent A with insightful answers."
52
- OVERSEER_PROMPT = """You are the Overseer (Agent C). Monitor conversations between Agent A and B.
53
- Intervene when discussions become repetitive or need redirection. Ask thought-provoking questions
54
- to explore new dimensions of the topic."""
 
 
 
 
 
 
 
 
 
55
 
56
  # === GLOBAL STATE ===
57
  conversation = []
58
  turn_count = 0
59
  auto_mode = False
 
60
 
61
- # === CHAT COMPLETION (Compatible with both APIs) ===
62
  def chat_completion(system, messages, model=CHAT_MODEL):
63
  try:
64
- # Build message list with system prompt
65
  full_messages = [{"role": "system", "content": system}]
66
  full_messages.extend(messages)
67
 
68
- # Try v1.x API first
69
  try:
70
  response = openai.chat.completions.create(
71
  model=model,
72
  messages=full_messages,
73
- temperature=0.7,
74
- max_tokens=150
75
  )
76
  return response.choices[0].message.content.strip()
77
  except AttributeError:
78
- # Fallback to v0.x API
79
  response = openai.ChatCompletion.create(
80
  model=model,
81
  messages=full_messages,
82
- temperature=0.7,
83
- max_tokens=150
84
  )
85
  return response['choices'][0]['message']['content'].strip()
86
-
87
  except Exception as e:
88
  return f"[API Error: {str(e)}]"
89
 
90
  # === MEMORY MANAGEMENT ===
91
- def embed_and_store(text):
92
  try:
93
  vec = get_embedding(text)
94
  memory_index.add(np.array([vec], dtype='float32'))
95
  memory_data.append({
96
  "text": text,
97
- "timestamp": datetime.now().isoformat()
 
98
  })
99
- # Periodic save to avoid constant I/O
100
  if len(memory_data) % 5 == 0:
101
  with open(MEMORY_FILE, "wb") as f:
102
  pickle.dump(memory_data, f)
@@ -104,72 +120,130 @@ def embed_and_store(text):
104
  except Exception as e:
105
  print(f"Memory Error: {str(e)}")
106
 
107
- # === CONVERSATION MANAGEMENT ===
108
  def format_convo():
109
  return "\n".join([f"**{m['agent']}**: {m['text']}" for m in conversation])
110
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  def detect_repetition():
112
- """Check if recent messages are similar using embeddings"""
113
  if len(conversation) < 4:
114
  return False
115
 
116
- # Get embeddings of last 2 pairs
117
  recent = [m['text'] for m in conversation[-4:]]
118
  embeddings = [get_embedding(text) for text in recent]
119
-
120
- # Compare current with 2 messages back
121
  similarity = cosine_similarity(embeddings[-1], embeddings[-3])
122
- print(f"Similarity: {similarity:.4f}")
123
- return similarity > 0.85
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
 
125
  # === CORE CONVERSATION FLOW ===
126
- def step():
127
- global conversation, turn_count
128
 
129
- if not conversation: # Initial message
130
- msg = chat_completion(AGENT_A_PROMPT, [])
131
- conversation.append({"agent": "Agent A", "text": msg})
132
- embed_and_store(msg)
133
- turn_count = 0
134
- return format_convo(), ""
 
 
 
 
 
135
 
136
- # Agent B responds to last message
137
  last_msg = conversation[-1]['text']
138
  b_msg = chat_completion(
139
  AGENT_B_PROMPT,
140
- [{"role": "user", "content": last_msg}]
141
  )
142
- conversation.append({"agent": "Agent B", "text": b_msg})
143
- embed_and_store(b_msg)
144
 
145
- # Agent A responds to Agent B
146
  a_msg = chat_completion(
147
  AGENT_A_PROMPT,
148
- [{"role": "user", "content": b_msg}]
149
  )
150
- conversation.append({"agent": "Agent A", "text": a_msg})
151
- embed_and_store(a_msg)
152
 
153
- # Overseer intervention logic
154
- intervention = None
155
- if turn_count % 3 == 0 or detect_repetition():
156
  context = "\n".join([m['text'] for m in conversation[-4:]])
157
- prompt = f"Conversation Context:\n{context}\n\nIntervene to redirect or deepen the discussion:"
158
  intervention = chat_completion(OVERSEER_PROMPT, [{"role": "user", "content": prompt}])
159
- conversation.append({"agent": "Overseer", "text": intervention})
160
- embed_and_store(intervention)
 
 
 
 
 
 
 
161
 
162
  turn_count += 1
163
- return format_convo(), intervention or ""
164
 
165
  # === OVERSEER QUERY HANDLER ===
166
  def overseer_respond(query):
167
  try:
168
- # Add context from recent conversation
169
  context = "\n".join([m['text'] for m in conversation[-3:]]) if conversation else "No context"
170
- messages = [
171
- {"role": "user", "content": f"Recent conversation:\n{context}\n\nQuery: {query}"}
172
- ]
173
  return chat_completion(OVERSEER_PROMPT, messages)
174
  except Exception as e:
175
  return f"[Overseer Error: {str(e)}]"
@@ -179,7 +253,7 @@ def auto_loop():
179
  global auto_mode
180
  while auto_mode:
181
  step()
182
- time.sleep(5)
183
 
184
  def toggle_auto():
185
  global auto_mode
@@ -189,33 +263,62 @@ def toggle_auto():
189
  return "πŸ”΄ Auto: OFF" if not auto_mode else "🟒 Auto: ON"
190
 
191
  # === GRADIO UI ===
192
- with gr.Blocks() as demo:
193
- gr.Markdown("# πŸ€– Tri-Agent Conversational System")
194
- gr.Markdown("**Agents**: A (Initiator) β†’ B (Responder) β†’ C (Overseer)")
 
 
 
195
 
196
  with gr.Row():
197
- convo_display = gr.Markdown(value="**Conversation will appear here**")
198
 
199
  with gr.Row():
200
- step_btn = gr.Button("▢️ Next Conversation Step")
201
  auto_btn = gr.Button("πŸ”΄ Auto: OFF", variant="secondary")
202
- clear_btn = gr.Button("πŸ”„ Reset Conversation")
 
203
 
204
- with gr.Accordion("🧠 Overseer Query Panel", open=False):
205
- gr.Markdown("Ask the Overseer (Agent C) for insights:")
206
- qbox = gr.Textbox(label="Your Question", placeholder="What should we discuss next?")
207
- overseer_out = gr.Textbox(label="Overseer's Response", interactive=False)
 
 
 
 
 
 
 
 
208
 
209
  # Event handlers
210
  def clear_convo():
211
- global conversation, turn_count
212
  conversation = []
213
  turn_count = 0
214
- return "**Conversation reset**", "πŸ”΄ Auto: OFF"
 
 
 
 
 
 
215
 
216
- step_btn.click(step, outputs=[convo_display, overseer_out])
 
 
 
 
217
  qbox.submit(overseer_respond, inputs=qbox, outputs=overseer_out)
218
  auto_btn.click(toggle_auto, outputs=auto_btn)
219
- clear_btn.click(clear_convo, outputs=[convo_display, auto_btn])
 
 
 
 
 
 
 
220
 
221
  demo.launch()
 
1
+ # app.py - Advanced Discussion Simulator with Quad-Agent System
2
  import gradio as gr
3
  import openai
4
  import threading
 
8
  import os
9
  import pickle
10
  from datetime import datetime
11
+ import re
12
 
13
  # === CONFIG ===
14
  EMBEDDING_MODEL = "text-embedding-3-small"
15
+ CHAT_MODEL = "gpt-4o"
16
  MEMORY_FILE = "memory.pkl"
17
  INDEX_FILE = "memory.index"
 
 
18
  openai.api_key = os.environ.get("OPENAI_API_KEY")
19
 
20
  # === EMBEDDING UTILS ===
21
  def get_embedding(text, model=EMBEDDING_MODEL):
22
  text = text.replace("\n", " ")
 
 
23
  try:
 
24
  response = openai.embeddings.create(input=[text], model=model)
25
  return response.data[0].embedding
26
  except AttributeError:
 
27
  response = openai.Embedding.create(input=[text], model=model)
28
  return response['data'][0]['embedding']
29
 
 
39
  with open(MEMORY_FILE, "rb") as f:
40
  memory_data = pickle.load(f)
41
  except:
42
+ memory_index = faiss.IndexFlatL2(1536)
43
+
44
+ # === AGENT SYSTEM PROMPTS ===
45
+ AGENT_A_PROMPT = """You are the Discussion Initiator. Your role:
46
+ 1. Introduce complex topics requiring multidisciplinary perspectives
47
+ 2. Frame debates with nuanced questions exploring tensions between values
48
+ 3. Challenge assumptions while maintaining intellectual humility
49
+ 4. Connect concepts across domains (science, ethics, policy, technology)
50
+ 5. Elevate discussions beyond surface-level analysis"""
51
+
52
+ AGENT_B_PROMPT = """You are the Critical Responder. Your role:
53
+ 1. Provide counterpoints with evidence-based reasoning
54
+ 2. Identify logical fallacies and cognitive biases in arguments
55
+ 3. Analyze implications at different scales (individual, societal, global)
56
+ 4. Consider second and third-order consequences
57
+ 5. Balance idealism with practical constraints"""
58
 
59
+ OVERSEER_PROMPT = """You are the Depth Guardian. Your role:
60
+ 1. Ensure discussions maintain intellectual rigor
61
+ 2. Intervene when conversations become superficial or repetitive
62
+ 3. Highlight unexamined assumptions and blind spots
63
+ 4. Introduce relevant frameworks (systems thinking, ethical paradigms)
64
+ 5. Prompt consideration of marginalized perspectives
65
+ 6. Synthesize key tensions and paradoxes"""
66
+
67
+ OUTSIDER_PROMPT = """You are the Cross-Disciplinary Provocateur. Your role:
68
+ 1. Introduce radical perspectives from unrelated fields
69
+ 2. Challenge conventional wisdom with contrarian viewpoints
70
+ 3. Surface historical precedents and analogies
71
+ 4. Propose unconventional solutions to complex problems
72
+ 5. Highlight overlooked connections and systemic relationships
73
+ 6. Question the framing of the discussion itself"""
74
 
75
  # === GLOBAL STATE ===
76
  conversation = []
77
  turn_count = 0
78
  auto_mode = False
79
+ current_topic = ""
80
 
81
+ # === CHAT COMPLETION ===
82
  def chat_completion(system, messages, model=CHAT_MODEL):
83
  try:
 
84
  full_messages = [{"role": "system", "content": system}]
85
  full_messages.extend(messages)
86
 
 
87
  try:
88
  response = openai.chat.completions.create(
89
  model=model,
90
  messages=full_messages,
91
+ temperature=0.75,
92
+ max_tokens=300
93
  )
94
  return response.choices[0].message.content.strip()
95
  except AttributeError:
 
96
  response = openai.ChatCompletion.create(
97
  model=model,
98
  messages=full_messages,
99
+ temperature=0.75,
100
+ max_tokens=300
101
  )
102
  return response['choices'][0]['message']['content'].strip()
 
103
  except Exception as e:
104
  return f"[API Error: {str(e)}]"
105
 
106
  # === MEMORY MANAGEMENT ===
107
+ def embed_and_store(text, agent=None):
108
  try:
109
  vec = get_embedding(text)
110
  memory_index.add(np.array([vec], dtype='float32'))
111
  memory_data.append({
112
  "text": text,
113
+ "timestamp": datetime.now().isoformat(),
114
+ "agent": agent or "system"
115
  })
 
116
  if len(memory_data) % 5 == 0:
117
  with open(MEMORY_FILE, "wb") as f:
118
  pickle.dump(memory_data, f)
 
120
  except Exception as e:
121
  print(f"Memory Error: {str(e)}")
122
 
123
+ # === CONVERSATION UTILITIES ===
124
  def format_convo():
125
  return "\n".join([f"**{m['agent']}**: {m['text']}" for m in conversation])
126
 
127
+ def detect_superficiality():
128
+ """Detect shallow arguments using linguistic analysis"""
129
+ if len(conversation) < 3:
130
+ return False
131
+
132
+ last_texts = [m['text'] for m in conversation[-3:]]
133
+
134
+ # Linguistic markers of superficiality
135
+ superficial_indicators = [
136
+ r"\b(obviously|clearly|everyone knows)\b",
137
+ r"\b(simply|just|merely)\b",
138
+ r"\b(always|never)\b",
139
+ r"\b(I (think|believe|feel))\b",
140
+ r"\b(without question|undeniably)\b"
141
+ ]
142
+
143
+ # Argument depth markers
144
+ depth_markers = [
145
+ r"\b(however|conversely|paradoxically)\b",
146
+ r"\b(evidence suggests|studies indicate)\b",
147
+ r"\b(complex interplay|multifaceted nature)\b",
148
+ r"\b(trade-off|tension between)\b",
149
+ r"\b(historical precedent|comparative analysis)\b"
150
+ ]
151
+
152
+ superficial_count = 0
153
+ depth_count = 0
154
+
155
+ for text in last_texts:
156
+ for pattern in superficial_indicators:
157
+ if re.search(pattern, text, re.IGNORECASE):
158
+ superficial_count += 1
159
+ for pattern in depth_markers:
160
+ if re.search(pattern, text, re.IGNORECASE):
161
+ depth_count += 1
162
+
163
+ return superficial_count > depth_count * 2
164
+
165
  def detect_repetition():
166
+ """Check if recent messages are conceptually similar"""
167
  if len(conversation) < 4:
168
  return False
169
 
 
170
  recent = [m['text'] for m in conversation[-4:]]
171
  embeddings = [get_embedding(text) for text in recent]
 
 
172
  similarity = cosine_similarity(embeddings[-1], embeddings[-3])
173
+ return similarity > 0.82
174
+
175
+ # === AGENT FUNCTIONS ===
176
+ def generate_topic():
177
+ """Generate a complex discussion topic"""
178
+ topic = chat_completion(
179
+ "Generate a complex discussion topic requiring multidisciplinary analysis",
180
+ [{"role": "user", "content": "Create a topic addressing tensions between technological progress and human values"}]
181
+ )
182
+ return topic.split(":")[-1].strip() if ":" in topic else topic
183
+
184
+ def outsider_comment():
185
+ """Generate outsider perspective"""
186
+ context = "\n".join([f"{m['agent']}: {m['text']}" for m in conversation[-4:]])
187
+ prompt = f"Conversation Context:\n{context}\n\nProvide your cross-disciplinary perspective:"
188
+ return chat_completion(OUTSIDER_PROMPT, [{"role": "user", "content": prompt}])
189
 
190
  # === CORE CONVERSATION FLOW ===
191
+ def step(topic_input=""):
192
+ global conversation, turn_count, current_topic
193
 
194
+ # Initialize new discussion
195
+ if not conversation:
196
+ current_topic = topic_input or generate_topic()
197
+ msg = chat_completion(
198
+ AGENT_A_PROMPT,
199
+ [{"role": "user", "content": f"Initiate a deep discussion on: {current_topic}"}]
200
+ )
201
+ conversation.append({"agent": "πŸ’‘ Initiator", "text": msg})
202
+ embed_and_store(msg, "Initiator")
203
+ turn_count = 1
204
+ return format_convo(), "", "", current_topic
205
 
206
+ # Critical Responder engages
207
  last_msg = conversation[-1]['text']
208
  b_msg = chat_completion(
209
  AGENT_B_PROMPT,
210
+ [{"role": "user", "content": f"Topic: {current_topic}\n\nLast statement: {last_msg}"}]
211
  )
212
+ conversation.append({"agent": "πŸ” Responder", "text": b_msg})
213
+ embed_and_store(b_msg, "Responder")
214
 
215
+ # Initiator counters
216
  a_msg = chat_completion(
217
  AGENT_A_PROMPT,
218
+ [{"role": "user", "content": f"Topic: {current_topic}\n\nCritical response: {b_msg}"}]
219
  )
220
+ conversation.append({"agent": "πŸ’‘ Initiator", "text": a_msg})
221
+ embed_and_store(a_msg, "Initiator")
222
 
223
+ # Overseer intervention
224
+ intervention = ""
225
+ if turn_count % 3 == 0 or detect_repetition() or detect_superficiality():
226
  context = "\n".join([m['text'] for m in conversation[-4:]])
227
+ prompt = f"Topic: {current_topic}\n\nDiscussion Context:\n{context}\n\nDeepen the analysis:"
228
  intervention = chat_completion(OVERSEER_PROMPT, [{"role": "user", "content": prompt}])
229
+ conversation.append({"agent": "βš–οΈ Depth Guardian", "text": intervention})
230
+ embed_and_store(intervention, "Overseer")
231
+
232
+ # Outsider commentary
233
+ outsider_msg = ""
234
+ if turn_count % 4 == 0 or "paradox" in last_msg.lower():
235
+ outsider_msg = outsider_comment()
236
+ conversation.append({"agent": "🌐 Provocateur", "text": outsider_msg})
237
+ embed_and_store(outsider_msg, "Outsider")
238
 
239
  turn_count += 1
240
+ return format_convo(), intervention, outsider_msg, current_topic
241
 
242
  # === OVERSEER QUERY HANDLER ===
243
  def overseer_respond(query):
244
  try:
 
245
  context = "\n".join([m['text'] for m in conversation[-3:]]) if conversation else "No context"
246
+ messages = [{"role": "user", "content": f"Discussion Topic: {current_topic}\n\nRecent context:\n{context}\n\nQuery: {query}"}]
 
 
247
  return chat_completion(OVERSEER_PROMPT, messages)
248
  except Exception as e:
249
  return f"[Overseer Error: {str(e)}]"
 
253
  global auto_mode
254
  while auto_mode:
255
  step()
256
+ time.sleep(6)
257
 
258
  def toggle_auto():
259
  global auto_mode
 
263
  return "πŸ”΄ Auto: OFF" if not auto_mode else "🟒 Auto: ON"
264
 
265
  # === GRADIO UI ===
266
+ with gr.Blocks(title="Advanced Discussion Simulator") as demo:
267
+ gr.Markdown("# 🧠 Advanced Discussion Simulator")
268
+ gr.Markdown("### Quad-Agent System for Complex Discourse")
269
+
270
+ with gr.Row():
271
+ topic_display = gr.Textbox(label="Current Topic", interactive=False)
272
 
273
  with gr.Row():
274
+ convo_display = gr.Markdown(value="**Discussion will appear here**")
275
 
276
  with gr.Row():
277
+ step_btn = gr.Button("▢️ Next Turn", variant="primary")
278
  auto_btn = gr.Button("πŸ”΄ Auto: OFF", variant="secondary")
279
+ clear_btn = gr.Button("πŸ”„ New Discussion", variant="stop")
280
+ topic_btn = gr.Button("🎲 Random Topic", variant="secondary")
281
 
282
+ with gr.Row():
283
+ with gr.Column(scale=1):
284
+ gr.Markdown("### βš–οΈ Depth Guardian")
285
+ intervention_display = gr.Textbox(label="Intervention", interactive=False)
286
+ with gr.Column(scale=1):
287
+ gr.Markdown("### 🌐 Cross-Disciplinary View")
288
+ outsider_display = gr.Textbox(label="Provocation", interactive=False)
289
+
290
+ with gr.Accordion("πŸ’¬ Guide the Discussion", open=False):
291
+ topic_input = gr.Textbox(label="Set Custom Topic", placeholder="e.g., Ethics of generative AI in creative industries...")
292
+ qbox = gr.Textbox(label="Ask the Depth Guardian", placeholder="What perspectives are missing in this discussion?")
293
+ overseer_out = gr.Textbox(label="Response", interactive=False)
294
 
295
  # Event handlers
296
  def clear_convo():
297
+ global conversation, turn_count, current_topic
298
  conversation = []
299
  turn_count = 0
300
+ current_topic = ""
301
+ return "**New discussion started**", "", "", "", ""
302
+
303
+ def new_topic():
304
+ clear_convo()
305
+ topic = generate_topic()
306
+ return "", "", "", topic, topic
307
 
308
+ step_btn.click(
309
+ step,
310
+ inputs=[topic_input],
311
+ outputs=[convo_display, intervention_display, outsider_display, topic_display]
312
+ )
313
  qbox.submit(overseer_respond, inputs=qbox, outputs=overseer_out)
314
  auto_btn.click(toggle_auto, outputs=auto_btn)
315
+ clear_btn.click(
316
+ clear_convo,
317
+ outputs=[convo_display, intervention_display, outsider_display, topic_display, overseer_out]
318
+ )
319
+ topic_btn.click(
320
+ new_topic,
321
+ outputs=[convo_display, intervention_display, outsider_display, topic_display, overseer_out]
322
+ )
323
 
324
  demo.launch()