Masterogon commited on
Commit
0771fc6
·
verified ·
1 Parent(s): fc48bc4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +268 -129
app.py CHANGED
@@ -1,4 +1,4 @@
1
- import spaces
2
  import gradio as gr
3
  import pandas as pd
4
  import numpy as np
@@ -6,7 +6,7 @@ import os
6
  import re
7
 
8
  import matplotlib
9
- matplotlib.use('Agg')
10
  import matplotlib.pyplot as plt
11
  import seaborn as sns
12
  import networkx as nx
@@ -18,18 +18,19 @@ from sklearn.metrics.pairwise import cosine_similarity
18
  from sklearn.manifold import TSNE
19
 
20
  # ==========================================
21
- # INITIALIZATION
22
  # ==========================================
23
  model = SentenceTransformer('all-MiniLM-L6-v2')
24
 
25
  def split_into_sentences(text):
 
26
  sentences = re.split(r'(?<=[.!?])\s+', text.strip())
27
  return [s for s in sentences if len(s) > 10]
28
 
29
  # ==========================================
30
- # DATABASE
31
  # ==========================================
32
- DATASET_REPO_ID = "Masterogon/dream-database"
33
  DB_FILE = "dream_database.csv"
34
  HF_TOKEN = os.environ.get("HF_TOKEN")
35
 
@@ -38,202 +39,340 @@ api = HfApi()
38
  def init_db():
39
  if HF_TOKEN:
40
  try:
 
41
  file_path = hf_hub_download(repo_id=DATASET_REPO_ID, filename=DB_FILE, repo_type="dataset", token=HF_TOKEN)
42
  import shutil
43
  shutil.copy(file_path, DB_FILE)
44
- print("Database loaded.")
45
  return
46
  except Exception as e:
47
- print("Download failed:", e)
48
 
49
  if not os.path.exists(DB_FILE):
50
  df = pd.DataFrame(columns=["Timestamp", "Alias", "ASC_Type", "Emotion", "Intensity", "Narrative"])
51
  df.to_csv(DB_FILE, index=False)
 
52
 
53
  init_db()
54
 
55
  # ==========================================
56
- # SAVE ENTRY
57
  # ==========================================
58
  def process_entry(alias, asc_type, emotion, intensity, narrative):
59
  if not narrative.strip():
60
- return "Error: Please enter a narrative.", pd.read_csv(DB_FILE).tail(5)
61
 
62
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
63
- new_data = pd.DataFrame([[timestamp, alias or "Anonymous", asc_type, emotion, intensity, narrative]],
64
  columns=["Timestamp", "Alias", "ASC_Type", "Emotion", "Intensity", "Narrative"])
65
  new_data.to_csv(DB_FILE, mode='a', header=False, index=False)
66
 
67
- backup_status = "Synced to cloud." if HF_TOKEN else "Saved locally."
68
- return f"✅ Entry added successfully. {backup_status}", pd.read_csv(DB_FILE).tail(8)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
 
70
  # ==========================================
71
- # ANALYSES (без ссылок)
72
  # ==========================================
73
  @spaces.GPU
74
  def macro_analysis():
75
- df = pd.read_csv(DB_FILE).dropna(subset=['Narrative'])
76
- if len(df) < 3:
77
- return None, None, "Not enough data (minimum 3 reports)"
78
 
79
  texts = df['Narrative'].tolist()
 
 
 
 
80
  embeddings = model.encode(texts)
81
  sim_matrix = cosine_similarity(embeddings)
82
- labels = [f"R{i+1}" for i in range(len(texts))]
83
 
84
- fig_heat, ax = plt.subplots(figsize=(9, 7))
85
- sns.heatmap(sim_matrix, xticklabels=labels, yticklabels=labels, annot=True, cmap="YlOrRd", fmt=".2f", ax=ax)
86
- ax.set_title("Report Similarity Matrix")
 
 
87
  plt.tight_layout()
88
 
89
- fig_graph, ax_g = plt.subplots(figsize=(9, 7))
 
90
  G = nx.Graph()
91
- threshold = 0.45
92
- for i in range(len(texts)):
93
- G.add_node(f"R{i+1}")
94
- for i in range(len(texts)):
95
- for j in range(i + 1, len(texts)):
96
- if sim_matrix[i, j] > threshold:
97
- G.add_edge(f"R{i+1}", f"R{j+1}", weight=sim_matrix[i, j])
98
 
 
 
 
 
 
 
 
 
 
99
  pos = nx.spring_layout(G, seed=42)
100
- nx.draw_networkx_nodes(G, pos, node_color="lightblue", node_size=1400, ax=ax_g)
101
- nx.draw_networkx_labels(G, pos, font_size=10, ax=ax_g)
102
- weights = [G[u][v]['weight'] * 7 for u, v in G.edges()]
103
- nx.draw_networkx_edges(G, pos, width=weights, edge_color='darkred', alpha=0.7, ax=ax_g)
104
- ax_g.set_title("Network of Shared Semantic Structures")
105
- ax_g.axis('off')
 
 
 
 
106
 
107
- return fig_heat, fig_graph, f"Analyzed {len(df)} reports."
 
 
 
 
108
 
 
 
 
 
 
109
  @spaces.GPU
110
  def micro_analysis():
111
- df = pd.read_csv(DB_FILE).dropna(subset=['Narrative'])
112
  if len(df) < 2:
113
- return None, "Not enough data"
114
 
115
  all_sentences = []
116
- parent_report = []
117
 
118
  for idx, row in df.iterrows():
119
- rid = f"R{idx+1}"
120
  sents = split_into_sentences(row['Narrative'])
121
  all_sentences.extend(sents)
122
- parent_report.extend([rid] * len(sents))
123
-
 
124
  if len(all_sentences) < 5:
125
- return None, "Not enough sentences"
126
-
127
  sent_embeddings = model.encode(all_sentences)
128
- tsne = TSNE(n_components=2, random_state=42, perplexity=min(30, len(all_sentences)-1))
129
- vecs = tsne.fit_transform(sent_embeddings)
130
 
131
- fig, ax = plt.subplots(figsize=(10, 8))
132
- sns.scatterplot(x=vecs[:,0], y=vecs[:,1], hue=parent_report, palette="tab10", s=90, ax=ax)
133
- ax.set_title("Semantic Fragments Clustering (t-SNE)")
134
- plt.legend(bbox_to_anchor=(1.05, 1))
 
 
 
 
135
  plt.tight_layout()
136
 
137
  sim_matrix = cosine_similarity(sent_embeddings)
138
  mean_sims = sim_matrix.mean(axis=1)
139
- top_idx = mean_sims.argsort()[-8:][::-1]
140
-
141
- text = "### �� Most Central Reproducible Fragments\n\n"
142
- for i in top_idx:
143
- rid = parent_report[i]
144
- text += f"**{rid}** (Centrality: {mean_sims[i]:.3f})\n"
145
- text += f"\"{all_sentences[i]}\"\n\n"
146
- text += f"→ Go to Tab 5 and enter **{rid}** to read the full report.\n---\n"
147
 
148
- return fig, text
149
-
150
- @spaces.GPU
151
- def hybrid_pattern_discovery(mode, custom_motifs_text):
152
- df = pd.read_csv(DB_FILE).dropna(subset=['Narrative'])
153
- if len(df) < 3:
154
- return "Not enough data (minimum 3 reports needed)"
155
- return "✅ Analysis completed.\n\nFull original reports can be viewed in **Tab 5 → Full Reports Explorer**."
156
 
157
  # ==========================================
158
- # FULL REPORT VIEWER
159
  # ==========================================
160
- def show_full_report(report_id):
161
- try:
162
- df = pd.read_csv(DB_FILE)
163
- idx = int(report_id.strip().replace("R", "").replace("r", "")) - 1
164
- if 0 <= idx < len(df):
165
- row = df.iloc[idx]
166
- return f"""**Report ID:** {report_id.upper()}
167
- **Date:** {row['Timestamp']}
168
- **Participant:** {row['Alias']}
169
- **Type:** {row['ASC_Type']} | **Emotion:** {row['Emotion']} | **Intensity:** {row['Intensity']}
170
-
171
- ---
172
-
173
- {row['Narrative']}
174
- """
175
- return " Report not found. Check the ID in Tab 1 or Tab 5 table."
176
- except:
177
- return "❌ Invalid format. Please enter ID like: R1, R2, R5"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
 
179
- # ==========================================
180
- # INTERFACE
181
- # ==========================================
182
- with gr.Blocks(theme=gr.themes.Monochrome()) as app:
183
- gr.Markdown("# 🌌 DreamCode Detector of Reproducible Informational Signals")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
 
 
 
 
 
185
  with gr.Tabs():
 
186
  with gr.TabItem("1. Data Ingestion"):
187
  with gr.Row():
188
  with gr.Column():
189
- alias = gr.Textbox(label="Participant ID / Alias (optional)")
190
- asc_type = gr.Dropdown(choices=["Ordinary Dream", "Lucid Dream (LD)", "OBE", "NDE", "Other"], label="Altered State Type")
191
- emotion = gr.Radio(choices=["Positive", "Neutral", "Negative"], label="Dominant Emotion")
192
- intensity = gr.Slider(1, 3, value=2, step=1, label="Intensity (1-3)")
193
- narrative = gr.Textbox(label="Full Narrative / Experience Description", lines=8)
194
  submit_btn = gr.Button("Submit Experience", variant="primary")
195
-
196
  with gr.Column():
197
  status_output = gr.Textbox(label="Status")
198
- recent_data = gr.Dataframe(label="Recent Entries")
199
-
200
- submit_btn.click(process_entry, inputs=[alias, asc_type, emotion, intensity, narrative],
201
- outputs=[status_output, recent_data])
202
 
203
- with gr.TabItem("2. Document-Level Structures"):
204
- gr.Markdown("Similarity between full reports")
205
- btn_macro = gr.Button("Run Macro Analysis", variant="primary")
 
206
  with gr.Row():
207
- heat = gr.Plot(label="Similarity Heatmap")
208
- net = gr.Plot(label="Semantic Network")
209
- summary = gr.Textbox(label="Summary")
210
- btn_macro.click(macro_analysis, outputs=[heat, net, summary])
211
-
212
- with gr.TabItem("3. Scene & Fragment Clustering"):
213
- gr.Markdown("Sentence-level semantic analysis")
214
- btn_micro = gr.Button("Run Sentence Analysis", variant="primary")
 
215
  with gr.Row():
216
- tsne_plot = gr.Plot()
217
- central_md = gr.Markdown()
218
- btn_micro.click(micro_analysis, outputs=[tsne_plot, central_md])
 
219
 
 
220
  with gr.TabItem("4. AI Pattern Discovery"):
221
- gr.Markdown("### Main Analysis Engine")
222
- mode = gr.Radio(["Blind Extraction (Unsupervised)", "Targeted Search (Zero-Shot)"], value="Blind Extraction (Unsupervised)")
223
- motifs = gr.Textbox(label="Custom motifs (optional for Targeted mode)", lines=3, visible=False)
224
- btn_analyze = gr.Button("Run Analysis Engine", variant="primary")
225
- result_md = gr.Markdown()
226
- btn_analyze.click(hybrid_pattern_discovery, inputs=[mode, motifs], outputs=[result_md])
227
-
228
- with gr.TabItem("5. Full Reports Explorer"):
229
- gr.Markdown("### All Reports & Full Text Viewer")
230
- gr.Dataframe(get_all_reports(), label="All Submitted Reports")
231
 
232
- with gr.Row():
233
- id_input = gr.Textbox(label="Enter Report ID (example: R1, R3, R7)", placeholder="R1")
234
- view_button = gr.Button("Show Full Report", variant="primary", size="large")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
 
236
- full_text = gr.Markdown(label="Full Report Content")
237
- view_button.click(show_full_report, inputs=id_input, outputs=full_text)
238
 
239
- app.launch()
 
1
+ import spaces
2
  import gradio as gr
3
  import pandas as pd
4
  import numpy as np
 
6
  import re
7
 
8
  import matplotlib
9
+ matplotlib.use('Agg')
10
  import matplotlib.pyplot as plt
11
  import seaborn as sns
12
  import networkx as nx
 
18
  from sklearn.manifold import TSNE
19
 
20
  # ==========================================
21
+ # 1. ИНИЦИАЛИЗАЦИЯ ИИ
22
  # ==========================================
23
  model = SentenceTransformer('all-MiniLM-L6-v2')
24
 
25
  def split_into_sentences(text):
26
+ # Разбиваем по точкам, восклицательным и вопросительным знакам
27
  sentences = re.split(r'(?<=[.!?])\s+', text.strip())
28
  return [s for s in sentences if len(s) > 10]
29
 
30
  # ==========================================
31
+ # 2. КОНФИГУРАЦИЯ БАЗЫ ДАННЫХ
32
  # ==========================================
33
+ DATASET_REPO_ID = "Masterogon/dream-database"
34
  DB_FILE = "dream_database.csv"
35
  HF_TOKEN = os.environ.get("HF_TOKEN")
36
 
 
39
  def init_db():
40
  if HF_TOKEN:
41
  try:
42
+ print("Downloading database from Hugging Face Hub...")
43
  file_path = hf_hub_download(repo_id=DATASET_REPO_ID, filename=DB_FILE, repo_type="dataset", token=HF_TOKEN)
44
  import shutil
45
  shutil.copy(file_path, DB_FILE)
46
+ print("Database successfully loaded.")
47
  return
48
  except Exception as e:
49
+ print("Could not download DB. It might be empty or missing. Error:", e)
50
 
51
  if not os.path.exists(DB_FILE):
52
  df = pd.DataFrame(columns=["Timestamp", "Alias", "ASC_Type", "Emotion", "Intensity", "Narrative"])
53
  df.to_csv(DB_FILE, index=False)
54
+ print("Created a fresh local database.")
55
 
56
  init_db()
57
 
58
  # ==========================================
59
+ # 3. ФУНКЦИЯ ЗАПИСИ (Data Ingestion)
60
  # ==========================================
61
  def process_entry(alias, asc_type, emotion, intensity, narrative):
62
  if not narrative.strip():
63
+ return "Error: Please describe your experience.", pd.read_csv(DB_FILE).tail(5)
64
 
65
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
66
+ new_data = pd.DataFrame([[timestamp, alias, asc_type, emotion, intensity, narrative]],
67
  columns=["Timestamp", "Alias", "ASC_Type", "Emotion", "Intensity", "Narrative"])
68
  new_data.to_csv(DB_FILE, mode='a', header=False, index=False)
69
 
70
+ if HF_TOKEN:
71
+ try:
72
+ api.upload_file(
73
+ path_or_fileobj=DB_FILE,
74
+ path_in_repo=DB_FILE,
75
+ repo_id=DATASET_REPO_ID,
76
+ repo_type="dataset",
77
+ token=HF_TOKEN,
78
+ commit_message=f"Added new report by {alias}"
79
+ )
80
+ backup_status = "Successfully synced to cloud."
81
+ except Exception as e:
82
+ backup_status = f"Warning: Cloud sync failed ({e})"
83
+ else:
84
+ backup_status = "Warning: No HF_TOKEN. Data is local."
85
+
86
+ return f"Success! Report added. {backup_status}", pd.read_csv(DB_FILE).tail(10)
87
+
88
+ # ==========================================
89
+ # 4. ФУНКЦИЯ ДЛЯ ПРОСМОТРА ПОЛНОЙ БАЗЫ (Tab 5)
90
+ # ==========================================
91
+ def view_database():
92
+ if not os.path.exists(DB_FILE):
93
+ return pd.DataFrame()
94
+ df = pd.read_csv(DB_FILE).dropna(subset=['Narrative']).reset_index(drop=True)
95
+ if len(df) == 0:
96
+ return pd.DataFrame()
97
+
98
+ # Создаем анонимные ID для удобного чтения
99
+ df.insert(0, 'Report_ID', [f"Report #{i+1}" for i in range(len(df))])
100
+ # Возвращаем нужные столбцы, скрывая реальные имена (Alias)
101
+ return df[['Report_ID', 'Timestamp', 'ASC_Type', 'Emotion', 'Intensity', 'Narrative']]
102
 
103
  # ==========================================
104
+ # 5. ФУНКЦИИ МАКРО-АНАЛИЗА (Tab 2: Heatmap & Graph)
105
  # ==========================================
106
  @spaces.GPU
107
  def macro_analysis():
108
+ df = pd.read_csv(DB_FILE).dropna(subset=['Narrative']).reset_index(drop=True)
109
+ if len(df) < 2:
110
+ return None, None
111
 
112
  texts = df['Narrative'].tolist()
113
+ # Заменяем имена на анонимные номера отчетов
114
+ report_ids = [f"Report #{i+1}" for i in range(len(df))]
115
+
116
+ # Векторизация целых документов для общей картины
117
  embeddings = model.encode(texts)
118
  sim_matrix = cosine_similarity(embeddings)
 
119
 
120
+ # Построение Heatmap
121
+ fig_heat, ax_heat = plt.subplots(figsize=(8, 6))
122
+ sns.heatmap(sim_matrix, xticklabels=report_ids, yticklabels=report_ids,
123
+ annot=True, cmap="YlOrRd", fmt=".2f", ax=ax_heat)
124
+ ax_heat.set_title("Document Cosine Similarity Matrix")
125
  plt.tight_layout()
126
 
127
+ # Построение Network Graph
128
+ fig_graph, ax_graph = plt.subplots(figsize=(8, 6))
129
  G = nx.Graph()
 
 
 
 
 
 
 
130
 
131
+ for i, r_id in enumerate(report_ids):
132
+ G.add_node(r_id)
133
+
134
+ threshold = 0.40 # Порог сходства для создания связи
135
+ for i in range(len(report_ids)):
136
+ for j in range(i + 1, len(report_ids)):
137
+ if sim_matrix[i, j] > threshold:
138
+ G.add_edge(report_ids[i], report_ids[j], weight=sim_matrix[i, j])
139
+
140
  pos = nx.spring_layout(G, seed=42)
141
+
142
+ # Рисуем только вершины
143
+ nx.draw_networkx_nodes(
144
+ G, pos, node_color="skyblue", node_size=2000, ax=ax_graph
145
+ )
146
+
147
+ # Подписи
148
+ nx.draw_networkx_labels(
149
+ G, pos, font_size=10, font_weight="bold", ax=ax_graph
150
+ )
151
 
152
+ # Рисуем толщину линий в зависимости от силы сходства
153
+ edges = G.edges()
154
+ weights = [G[u][v]['weight'] * 5 for u,v in edges]
155
+ nx.draw_networkx_edges(G, pos, edgelist=edges, width=weights, edge_color='red', alpha=0.5, ax=ax_graph)
156
+ ax_graph.set_title(f"Semantic Network Graph (Threshold > {threshold})")
157
 
158
+ return fig_heat, fig_graph
159
+
160
+ # ==========================================
161
+ # 6. ФУНКЦИИ МИКРО-АНАЛИЗА (Tab 3: Sentence t-SNE)
162
+ # ==========================================
163
  @spaces.GPU
164
  def micro_analysis():
165
+ df = pd.read_csv(DB_FILE).dropna(subset=['Narrative']).reset_index(drop=True)
166
  if len(df) < 2:
167
+ return None, "Not enough data."
168
 
169
  all_sentences = []
170
+ parent_report_ids = []
171
 
172
  for idx, row in df.iterrows():
 
173
  sents = split_into_sentences(row['Narrative'])
174
  all_sentences.extend(sents)
175
+ # Привязываем предложение к анонимному номеру отчета
176
+ parent_report_ids.extend([f"Report #{idx+1}"] * len(sents))
177
+
178
  if len(all_sentences) < 5:
179
+ return None, "Not enough sentences extracted."
180
+
181
  sent_embeddings = model.encode(all_sentences)
 
 
182
 
183
+ perplexity = min(30, len(all_sentences) - 1)
184
+ tsne = TSNE(n_components=2, random_state=42, perplexity=perplexity)
185
+ vecs_2d = tsne.fit_transform(sent_embeddings)
186
+
187
+ fig_tsne, ax_tsne = plt.subplots(figsize=(10, 8))
188
+ sns.scatterplot(x=vecs_2d[:,0], y=vecs_2d[:,1], hue=parent_report_ids, palette="tab10", s=100, ax=ax_tsne)
189
+ ax_tsne.set_title("Sentence-Level Semantic Projections (t-SNE)")
190
+ plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
191
  plt.tight_layout()
192
 
193
  sim_matrix = cosine_similarity(sent_embeddings)
194
  mean_sims = sim_matrix.mean(axis=1)
195
+ top_indices = mean_sims.argsort()[-5:][::-1]
 
 
 
 
 
 
 
196
 
197
+ central_text = "### Top 5 Most Central Semantic Fragments\n\n"
198
+ for idx in top_indices:
199
+ central_text += f"> **{parent_report_ids[idx]}**: \"{all_sentences[idx]}\" *(Centrality Score: {mean_sims[idx]:.2f})*\n\n"
200
+
201
+ return fig_tsne, central_text
 
 
 
202
 
203
  # ==========================================
204
+ # 7. ГИБРИДНЫЙ ДВИЖОК ПОИСКА (Tab 4)
205
  # ==========================================
206
+ @spaces.GPU
207
+ def hybrid_pattern_discovery(mode, custom_motifs_text):
208
+ df = pd.read_csv(DB_FILE).dropna(subset=['Narrative']).reset_index(drop=True)
209
+ if len(df) < 2:
210
+ return "Not enough data. Need at least 2 reports."
211
+
212
+ all_sentences = []
213
+ parent_report_ids = []
214
+
215
+ for idx, row in df.iterrows():
216
+ sents = split_into_sentences(row['Narrative'])
217
+ all_sentences.extend(sents)
218
+ parent_report_ids.extend([f"Report #{idx+1}"] * len(sents))
219
+
220
+ if len(all_sentences) < 5:
221
+ return "Not enough detailed sentences."
222
+
223
+ # РЕЖИМ 1: СЛЕПОЙ ПОИСК
224
+ if mode == "Blind Extraction (Unsupervised)":
225
+ sent_embeddings = model.encode(all_sentences)
226
+ num_clusters = max(2, min(8, len(all_sentences) // 10))
227
+
228
+ from sklearn.cluster import KMeans
229
+ kmeans = KMeans(n_clusters=num_clusters, random_state=42)
230
+ labels = kmeans.fit_predict(sent_embeddings)
231
+
232
+ results = "### 👁️ Blind AI Extraction: Emergent Signals from Noise\n"
233
+ results += f"*Analyzed {len(all_sentences)} sentences. Extracted {num_clusters} hidden thematic clusters without human prompts.*\n\n"
234
+
235
+ for i in range(num_clusters):
236
+ cluster_indices = np.where(labels == i)[0]
237
+ if len(cluster_indices) < 2: continue
238
+
239
+ cluster_embeddings = sent_embeddings[cluster_indices]
240
+ centroid = kmeans.cluster_centers_[i]
241
+
242
+ from sklearn.metrics.pairwise import cosine_distances
243
+ distances = cosine_distances([centroid], cluster_embeddings)[0]
244
+ global_central_idx = cluster_indices[np.argmin(distances)]
245
+ central_sentence = all_sentences[global_central_idx]
246
+
247
+ authors_in_cluster = set([parent_report_ids[idx] for idx in cluster_indices])
248
+
249
+ if len(authors_in_cluster) > 1:
250
+ results += f"#### 🟢 Discovered Archetype: *«{central_sentence[:100]}...»*\n"
251
+ results += f"**Cross-validation:** Found in {len(authors_in_cluster)} independent reports.\n"
252
+ count = 0
253
+ for idx in cluster_indices:
254
+ if idx != global_central_idx and count < 3:
255
+ results += f"- *{parent_report_ids[idx]}*: \"{all_sentences[idx]}\"\n"
256
+ count += 1
257
+ results += "---\n"
258
+ return results
259
 
260
+ # РЕЖИМ 2: ЦЕЛЕВОЙ ПОИСК
261
+ else:
262
+ seed_motifs = [m.strip() for m in re.split(r'[,|\n]', custom_motifs_text) if m.strip()]
263
+ if not seed_motifs:
264
+ return "Please enter at least one motif to search for."
265
+
266
+ motif_embeddings = model.encode(seed_motifs)
267
+ results = "### 🎯 Targeted AI Search: Hypothesis Testing\n\n"
268
+
269
+ for idx_motif, motif in enumerate(seed_motifs):
270
+ motif_emb = motif_embeddings[idx_motif]
271
+ reports_with_motif = 0
272
+ best_matches = []
273
+
274
+ for idx_row, row in df.iterrows():
275
+ sents = split_into_sentences(row['Narrative'])
276
+ if not sents: continue
277
+
278
+ sent_embs = model.encode(sents)
279
+ sims = cosine_similarity([motif_emb], sent_embs)[0]
280
+
281
+ max_sim = np.max(sims)
282
+ if max_sim > 0.40:
283
+ reports_with_motif += 1
284
+ best_idx = np.argmax(sims)
285
+ best_matches.append(f"*Report #{idx_row+1}*: \"{sents[best_idx]}\"")
286
+
287
+ results += f"#### ✔ Target: '{motif}'\n"
288
+ results += f"**Found in {reports_with_motif} out of {len(df)} reports.**\n"
289
+ if best_matches:
290
+ for match in best_matches[:4]:
291
+ results += f"- {match}\n"
292
+ results += "---\n"
293
+
294
+ return results
295
 
296
+ # ----------------- ИНТЕРФЕЙС GRADIO -----------------
297
+ with gr.Blocks() as app:
298
+ gr.Markdown("# 🌌 DreamCode: Research Platform")
299
+
300
  with gr.Tabs():
301
+ # TAB 1: Data Ingestion
302
  with gr.TabItem("1. Data Ingestion"):
303
  with gr.Row():
304
  with gr.Column():
305
+ alias = gr.Textbox(label="Participant ID (Kept private during analysis)")
306
+ asc_type = gr.Dropdown(choices=["Ordinary Dream", "Lucid Dream (LD)", "OBE", "NDE", "Other"], label="State")
307
+ emotion = gr.Radio(choices=["Positive", "Neutral", "Negative"], label="Emotional Tone")
308
+ intensity = gr.Slider(minimum=1, maximum=3, step=1, label="Intensity")
309
+ narrative = gr.Textbox(label="Narrative", lines=7)
310
  submit_btn = gr.Button("Submit Experience", variant="primary")
311
+
312
  with gr.Column():
313
  status_output = gr.Textbox(label="Status")
314
+ data_preview = gr.Dataframe(headers=["Timestamp", "Alias", "ASC_Type", "Emotion", "Intensity", "Narrative"])
315
+
316
+ submit_btn.click(fn=process_entry, inputs=[alias, asc_type, emotion, intensity, narrative], outputs=[status_output, data_preview])
 
317
 
318
+ # TAB 2: Document Level Analysis
319
+ with gr.TabItem("2. Document Similarity"):
320
+ gr.Markdown("Analyze macro-connections between entire reports using Cosine Similarity (Anonymized).")
321
+ analyze_macro_btn = gr.Button("Generate Matrix & Graph", variant="primary")
322
  with gr.Row():
323
+ heat_plot = gr.Plot(label="Cosine Similarity Heatmap")
324
+ network_plot = gr.Plot(label="Semantic Network Graph")
325
+
326
+ analyze_macro_btn.click(fn=macro_analysis, inputs=[], outputs=[heat_plot, network_plot])
327
+
328
+ # TAB 3: Sentence Level Analysis
329
+ with gr.TabItem("3. Scene & Sentence t-SNE"):
330
+ gr.Markdown("AI splits narratives into individual sentences to cluster specific scenes.")
331
+ analyze_micro_btn = gr.Button("Process Sentences", variant="primary")
332
  with gr.Row():
333
+ tsne_plot = gr.Plot(label="Sentence t-SNE")
334
+ central_text = gr.Markdown(label="Central Fragments")
335
+
336
+ analyze_micro_btn.click(fn=micro_analysis, inputs=[], outputs=[tsne_plot, central_text])
337
 
338
+ # TAB 4: AI Pattern Discovery
339
  with gr.TabItem("4. AI Pattern Discovery"):
340
+ gr.Markdown("### Semantic Search Engine\nChoose between extracting unknown signals automatically or testing specific hypotheses against the database.")
 
 
 
 
 
 
 
 
 
341
 
342
+ search_mode = gr.Radio(
343
+ choices=["Blind Extraction (Unsupervised)", "Targeted Search (Zero-Shot)"],
344
+ value="Blind Extraction (Unsupervised)",
345
+ label="Select Analysis Mode"
346
+ )
347
+
348
+ motif_input = gr.Textbox(
349
+ label="Enter custom phrases, motifs, or fragments from your own dream (separated by commas or new lines)",
350
+ lines=3,
351
+ value="A red celestial body, Huge planet in the sky, Feeling of global catastrophe",
352
+ visible=False
353
+ )
354
+
355
+ def toggle_input(mode):
356
+ if mode == "Targeted Search (Zero-Shot)":
357
+ return gr.update(visible=True)
358
+ else:
359
+ return gr.update(visible=False)
360
+
361
+ search_mode.change(fn=toggle_input, inputs=[search_mode], outputs=[motif_input])
362
+
363
+ analyze_btn = gr.Button("Run Analysis Engine", variant="primary")
364
+ output_md = gr.Markdown()
365
+
366
+ analyze_btn.click(fn=hybrid_pattern_discovery, inputs=[search_mode, motif_input], outputs=[output_md])
367
+
368
+ # TAB 5: Database Explorer (NEW)
369
+ with gr.TabItem("5. Database Explorer"):
370
+ gr.Markdown("### 📖 Full Reports Viewer\nCross-reference the **Report ID** from the analysis tabs to read the full context of the experience here.")
371
+ refresh_btn = gr.Button("Refresh Database")
372
+ # wrap=True позволяет тексту переноситься на новые строки, чтобы читать абзацы целиком
373
+ db_display = gr.Dataframe(wrap=True)
374
 
375
+ refresh_btn.click(fn=view_database, inputs=[], outputs=[db_display])
376
+ app.load(fn=view_database, inputs=[], outputs=[db_display])
377
 
378
+ app.launch(theme=gr.themes.Monochrome())