suh4s commited on
Commit
3c2e3e7
·
1 Parent(s): 2e8046f

Clean up repository, update docs & Dockerfile, ready for publish

Browse files
.env.example ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ # InsightFlow AI Environment Variables
2
+ # OpenAI API key
3
+ # Get from: https://platform.openai.com/api-keys
4
+ OPENAI_API_KEY=your_openai_api_key_here
5
+
6
+ # Tavily API key
7
+ # Get from: https://tavily.com/
8
+ TAVILY_API_KEY=your_tavily_api_key_here
.gitignore ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Environment variables
2
+ .env
3
+
4
+ # Python cache
5
+ __pycache__/
6
+ *.py[cod]
7
+ *$py.class
8
+ .pytest_cache/
9
+ .coverage
10
+ htmlcov/
11
+
12
+ # Virtual environment
13
+ venv/
14
+ .venv/
15
+ env/
16
+ ENV/
17
+
18
+ # Distribution / packaging
19
+ *.egg-info/
20
+ dist/
21
+ build/
22
+
23
+ # Chainlit local files
24
+ .chainlit/
25
+ .chainlit_client/
26
+
27
+ # Data directories
28
+ exports/*
29
+ !exports/.gitkeep
30
+ # data/* # This directory was removed
31
+ # !data/.gitkeep # This directory was removed
32
+
33
+ # Data source example files (generated by download_data.py)
34
+ data_sources/**/*.txt
35
+
36
+ # IDE files
37
+ .idea/
38
+ .vscode/
39
+ *.swp
40
+ *.swo
41
+
42
+ # Logs
43
+ *.log
44
+ logs/
45
+
46
+ # OS files
47
+ .DS_Store
48
+ Thumbs.db
Dockerfile ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ # Set environment variables
4
+ ENV PYTHONDONTWRITEBYTECODE=1 \
5
+ PYTHONUNBUFFERED=1 \
6
+ PYTHONFAULTHANDLER=1 \
7
+ HOME=/home/user \
8
+ # PATH will be set by uv if using its venv or if installed to system Python
9
+ UV_HOME="$HOME/.uv" \
10
+ PATH="$HOME/.uv/bin:$PATH" \
11
+ PYTHONPATH="$HOME/app:$PYTHONPATH"
12
+
13
+ # Add non-root user
14
+ RUN useradd -m -u 1000 user
15
+
16
+ # Install system dependencies & uv
17
+ RUN apt-get update && apt-get install -y --no-install-recommends \
18
+ build-essential \
19
+ curl \
20
+ && rm -rf /var/lib/apt/lists/* \
21
+ && curl -LsSf https://astral.sh/uv/install.sh | sh
22
+
23
+ # Set working directory
24
+ WORKDIR $HOME/app
25
+
26
+ # Copy project definition
27
+ COPY --chown=user pyproject.toml pyproject.toml
28
+
29
+ # Install dependencies using uv (into the system Python for simplicity in Docker)
30
+ # Using --system to install into the global site-packages
31
+ # Using --no-cache to reduce layer size slightly
32
+ RUN uv pip install --system --no-cache .
33
+
34
+ # Copy all application files required for runtime
35
+ COPY --chown=user app.py app.py
36
+ COPY --chown=user insight_state.py insight_state.py
37
+ COPY --chown=user chainlit.md chainlit.md
38
+ COPY --chown=user README.md README.md
39
+ COPY --chown=user download_data.py download_data.py
40
+ COPY --chown=user LICENSE LICENSE
41
+ COPY --chown=user utils ./utils/
42
+ COPY --chown=user persona_configs ./persona_configs/
43
+ COPY --chown=user public ./public/
44
+ # If you have a .env.example, you can copy it too:
45
+ # COPY --chown=user .env.example .env.example
46
+
47
+ # Create necessary directories (data_sources is key for download_data.py)
48
+ RUN mkdir -p data_sources exports
49
+
50
+ # Set permissions for the app directory
51
+ RUN chown -R user:user $HOME/app
52
+
53
+ # Switch to non-root user
54
+ USER user
55
+
56
+ # Run data download script to initialize data sources
57
+ RUN python download_data.py
58
+
59
+ # Expose the port the app runs on
60
+ EXPOSE 7860
61
+
62
+ # Run the app
63
+ CMD ["chainlit", "run", "app.py", "--host", "0.0.0.0", "--port", "7860"]
README.md ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: InsightFlow AI
3
+ emoji: 🧠
4
+ colorFrom: blue
5
+ colorTo: indigo
6
+ sdk: docker
7
+ pinned: true
8
+ # app_port: 7860
9
+ short_description: "AI research assistant: RAG, visuals, multi-perspective"
10
+ ---
11
+
12
+ # InsightFlow AI: Multi-Perspective Research Assistant
13
+
14
+ Dive deep into complex topics with InsightFlow AI! This advanced research assistant leverages multiple perspectives to provide nuanced understanding. Features Retrieval Augmented Generation (RAG) for richer insights and a slick UI powered by Chainlit.
15
+
16
+ ![InsightFlow AI](https://huggingface.co/datasets/suhas/InsightFlow-AI-demo/resolve/main/insightflow_banner.png)
17
+
18
+ ## Key Features
19
+
20
+ 🧠 **Multiple Perspective Analysis**:
21
+ Explore topics through various lenses:
22
+ - **Analytical**: Logical, pattern-seeking examination.
23
+ - **Scientific**: Evidence-based, empirical reasoning.
24
+ - **Philosophical**: Deeper meaning and holistic implications.
25
+ - **Factual**: Straightforward, verified information.
26
+ - **Metaphorical**: Creative analogies for clarity.
27
+ - **Futuristic**: Forward-looking potential developments.
28
+
29
+ 🕵️ **Personality Perspectives**:
30
+ Gain insights from unique AI personas like:
31
+ - **Sherlock Holmes**: Deductive, detailed observation.
32
+ - **Richard Feynman**: Clear, first-principles explanations.
33
+ - **Hannah Fry**: Math-meets-society storytelling.
34
+
35
+ 🎨 **Visualization Capabilities**:
36
+ - **Concept Maps**: Auto-generated Mermaid diagrams.
37
+ - **Visual Notes**: DALL-E sketches of key insights.
38
+ - **Visual-Only Mode**: Focus on visuals for quick takes (in Settings).
39
+
40
+ 🔍 **Retrieval Augmented Generation (RAG)**:
41
+ - Supported personas search dedicated knowledge bases for informed answers.
42
+ - Toggle RAG via the Settings panel.
43
+
44
+ 📤 **Export Options**:
45
+ - **Markdown & PDF Export**: Save your analyses (full functionality under development).
46
+
47
+ ## How to Use
48
+
49
+ 1. **Configure (⚙️ Settings)**: Select Persona Teams (e.g., Balanced Overview) or individual personas. Adjust RAG, display modes, etc.
50
+ 2. **Ask**: Type your research question.
51
+ 3. **Review**: Explore the synthesized view, individual perspectives, and visuals.
52
+ 4. **Help**: Type `/help` for a detailed guide.
53
+ 5. **Export**: Use `/export_md` or `/export_pdf` (coming soon!).
54
+
55
+ ## Quick Commands
56
+
57
+ Most options are in the **Settings (⚙️) panel**. Type `/help` for a full guide.
58
+
59
+ ```markdown
60
+ # Core
61
+ /help - Detailed help on features & settings.
62
+
63
+ # Toggles (also in Settings)
64
+ /direct on|off - Bypass multi-persona for direct LLM response.
65
+ /perspectives on|off - Show/hide individual perspective views.
66
+ /visualization on|off - Show/hide Mermaid & DALL-E visuals.
67
+ /quick_mode on|off - Use a smaller, faster set of personas.
68
+ /rag on|off - Toggle RAG for supported personas.
69
+ ```
70
+
71
+ ## Example Topics
72
+
73
+ - Historical events, scientific concepts, societal issues.
74
+ - Future trends, complex problems needing diverse viewpoints.
75
+
76
+ ## Tech Stack
77
+
78
+ - Python, LangGraph, OpenAI APIs, Chainlit.
79
+
80
+ ## Try It Out!
81
+
82
+ - "The societal impact of artificial intelligence"
83
+ - "Strategies for climate change adaptation"
84
+ - "The nature of consciousness"
85
+
86
+ ## Feedback
87
+
88
+ Got questions or feedback? Open an issue on the [GitHub repository](https://github.com/suhas/InsightFlow-AI) or comment on this Space.
__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ # This file makes the directory a Python package
app.py ADDED
@@ -0,0 +1,1080 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # InsightFlow AI - Main Application
2
+ import chainlit as cl
3
+ from insight_state import InsightFlowState # Import InsightFlowState
4
+ from dotenv import load_dotenv
5
+ from langchain_openai import ChatOpenAI
6
+ from openai import AsyncOpenAI # Import AsyncOpenAI for DALL-E
7
+ from langgraph.graph import StateGraph, END
8
+ from langchain_core.messages import HumanMessage, SystemMessage # Added for prompts
9
+ from utils.persona import PersonaFactory # Import PersonaFactory
10
+ from utils.visualization_utils import generate_dalle_image, generate_mermaid_code # <--- UPDATED IMPORT
11
+ import asyncio # For asyncio.gather in execute_persona_tasks
12
+ from langchain_core.callbacks.base import AsyncCallbackHandler # <--- ADDED for progress
13
+ from typing import Any, Dict, List, Optional, Union # <--- ADDED for callbacks
14
+ from langchain_core.outputs import LLMResult # <--- ADDED for callbacks
15
+ import datetime # For export filenames
16
+ from pathlib import Path # For export directory
17
+ from chainlit.input_widget import Switch, Select # <--- REMOVE Option import
18
+ # from chainlit.input_widget import Collapse # <--- COMMENT OUT FOR NOW
19
+ # from chainlit.element import Divider # <--- REMOVE Collapse from this import for now
20
+
21
+ # --- RAG UTILS IMPORT ---
22
+ from utils.rag_utils import get_embedding_model_instance, get_relevant_context_for_query
23
+ # --- END RAG UTILS IMPORT ---
24
+
25
+ # --- GLOBAL CONFIGURATION STATE ---
26
+ _configurations_initialized = False
27
+ # _embedding_model_initialized = False # REMOVE OLD GLOBAL FLAG
28
+ llm_planner = None
29
+ llm_synthesizer = None
30
+ llm_direct = None
31
+ llm_analytical = None
32
+ llm_scientific = None
33
+ llm_philosophical = None
34
+ llm_factual = None
35
+ llm_metaphorical = None
36
+ llm_futuristic = None
37
+ llm_mermaid_generator = None # <--- ADDED
38
+ openai_async_client = None # For DALL-E
39
+ PERSONA_LLM_MAP = {}
40
+
41
+ # Embedding Model Identifiers
42
+ OPENAI_EMBED_MODEL_ID = "text-embedding-3-small"
43
+ # IMPORTANT: Replace with your actual fine-tuned model ID from Hugging Face Hub after training
44
+ FINETUNED_BALANCED_TEAM_EMBED_ID = "suh4s/insightflow-balanced-team-embed-v1-7099e82c-e4c8-48ed-88a8-36bd9255036b" # Placeholder
45
+
46
+ QUICK_MODE_PERSONAS = ["analytical", "factual"] # Default personas for Quick Mode
47
+ # --- RAG Configuration (moved to global scope) ---
48
+ RAG_ENABLED_PERSONA_IDS = ["analytical", "philosophical", "metaphorical"]
49
+ # --- End RAG Configuration ---
50
+
51
+ PERSONA_TEAMS = {
52
+ "creative_synthesis": {
53
+ "name": "🎨 Creative Synthesis Team",
54
+ "description": "Generates novel ideas and artistic interpretations.",
55
+ "members": ["metaphorical", "futuristic", "philosophical"]
56
+ },
57
+ "data_driven_analysis": {
58
+ "name": "📊 Data-Driven Analysis Squad",
59
+ "description": "Focuses on factual accuracy and logical deduction.",
60
+ "members": ["analytical", "factual", "scientific"]
61
+ },
62
+ "balanced_overview": {
63
+ "name": "⚖️ Balanced Overview Group",
64
+ "description": "Provides a well-rounded perspective.",
65
+ "members": ["analytical", "philosophical", "metaphorical"] # UPDATED: factual -> metaphorical
66
+ }
67
+ }
68
+
69
+ def initialize_configurations():
70
+ """Loads environment variables and initializes LLM configurations."""
71
+ global _configurations_initialized
72
+ global llm_planner, llm_synthesizer, llm_direct, llm_analytical, llm_scientific
73
+ global llm_philosophical, llm_factual, llm_metaphorical, llm_futuristic
74
+ global llm_mermaid_generator # <--- ADDED
75
+ global openai_async_client # Add new client to globals
76
+ global PERSONA_LLM_MAP
77
+
78
+ if _configurations_initialized:
79
+ return
80
+
81
+ print("Initializing configurations: Loading .env and setting up LLMs...")
82
+ load_dotenv()
83
+
84
+ # LLM CONFIGURATIONS
85
+ llm_planner = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.1)
86
+ llm_synthesizer = ChatOpenAI(model="gpt-4o-mini", temperature=0.4)
87
+ llm_direct = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.3)
88
+ llm_analytical = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.2)
89
+ llm_scientific = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.3)
90
+ llm_philosophical = ChatOpenAI(model="gpt-4o-mini", temperature=0.5)
91
+ llm_factual = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.1)
92
+ llm_metaphorical = ChatOpenAI(model="gpt-4o-mini", temperature=0.6)
93
+ llm_futuristic = ChatOpenAI(model="gpt-4o-mini", temperature=0.6)
94
+ llm_mermaid_generator = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.1) # <--- ADDED INITIALIZATION
95
+
96
+ # Initialize OpenAI client for DALL-E etc.
97
+ openai_async_client = AsyncOpenAI()
98
+
99
+ # Mapping persona IDs to their specific LLM instances
100
+ PERSONA_LLM_MAP.update({
101
+ "analytical": llm_analytical,
102
+ "scientific": llm_scientific,
103
+ "philosophical": llm_philosophical,
104
+ "factual": llm_factual,
105
+ "metaphorical": llm_metaphorical,
106
+ "futuristic": llm_futuristic,
107
+ })
108
+
109
+ _configurations_initialized = True
110
+ print("Configurations initialized.")
111
+
112
+ # Load environment variables first
113
+ # load_dotenv() # Moved to initialize_configurations
114
+
115
+ # --- LLM CONFIGURATIONS ---
116
+ # Configurations based on tests/test_llm_config.py
117
+ # llm_planner = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.1) # Moved
118
+ # llm_synthesizer = ChatOpenAI(model="gpt-4o-mini", temperature=0.4) # Moved
119
+ # llm_direct = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.3) # Moved
120
+ # llm_analytical = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.2) # Moved
121
+ # llm_scientific = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.3) # Moved
122
+ # llm_philosophical = ChatOpenAI(model="gpt-4o-mini", temperature=0.5) # Moved
123
+ # llm_factual = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.1) # Moved
124
+ # llm_metaphorical = ChatOpenAI(model="gpt-4o-mini", temperature=0.6) # Moved
125
+ # llm_futuristic = ChatOpenAI(model="gpt-4o-mini", temperature=0.6) # Moved
126
+
127
+ # Mapping persona IDs to their specific LLM instances
128
+ # PERSONA_LLM_MAP = { # Moved and will be populated in initialize_configurations
129
+ # "analytical": llm_analytical,
130
+ # "scientific": llm_scientific,
131
+ # "philosophical": llm_philosophical,
132
+ # "factual": llm_factual,
133
+ # "metaphorical": llm_metaphorical,
134
+ # "futuristic": llm_futuristic,
135
+ # Add other personas here if they have dedicated LLMs or share one from above
136
+ # }
137
+
138
+ # --- SYSTEM PROMPTS (from original app.py) ---
139
+ DIRECT_SYSPROMPT = """You are a highly intelligent AI assistant that provides clear, direct, and helpful answers.
140
+ Your responses should be accurate, concise, and well-reasoned."""
141
+
142
+ SYNTHESIZER_SYSTEM_PROMPT_TEMPLATE = """You are a master synthesizer AI. Your task is to integrate the following diverse perspectives into a single, coherent, and insightful response. Ensure that the final synthesis is well-structured, easy to understand, and accurately reflects the nuances of each provided viewpoint. Do not simply list the perspectives; weave them together.
143
+
144
+ Perspectives:
145
+ {formatted_perspectives}
146
+
147
+ Synthesized Response:"""
148
+
149
+ # --- LANGGRAPH NODE FUNCTIONS (DUMMIES FOR NOW) ---
150
+ async def run_planner_agent(state: InsightFlowState) -> InsightFlowState:
151
+ print(f"Node: run_planner_agent, Query: {state.get('query')}")
152
+ progress_msg = cl.user_session.get("progress_msg")
153
+ completed_steps_log = cl.user_session.get("completed_steps_log")
154
+
155
+ activity_description = "Planning research approach..."
156
+ activity_emoji = "📅"
157
+ current_activity_display = f"{activity_emoji} {activity_description} (10%)"
158
+
159
+ if progress_msg:
160
+ progress_msg.content = f"**Current Activity:**\n{current_activity_display}"
161
+ if completed_steps_log:
162
+ progress_msg.content = f"**Completed Steps:**\n{(chr(10)).join(completed_steps_log)}\n\n{progress_msg.content}"
163
+ await progress_msg.update()
164
+
165
+ completed_steps_log.append(f"{activity_emoji} {activity_description}") # Log without percentage
166
+ cl.user_session.set("completed_steps_log", completed_steps_log)
167
+
168
+ state["current_step_name"] = "execute_persona_tasks"
169
+ return state
170
+
171
+ async def execute_persona_tasks(state: InsightFlowState) -> InsightFlowState:
172
+ print(f"Node: execute_persona_tasks")
173
+ progress_msg = cl.user_session.get("progress_msg")
174
+ completed_steps_log = cl.user_session.get("completed_steps_log", [])
175
+
176
+ activity_description = "Generating perspectives from selected team..."
177
+ activity_emoji = "🧠"
178
+ current_activity_display = f"{activity_emoji} {activity_description} (20%)"
179
+
180
+ if progress_msg:
181
+ progress_msg.content = f"**Completed Steps:**\n{(chr(10)).join(completed_steps_log)}\n\n**Current Activity:**\n{current_activity_display}"
182
+ await progress_msg.update()
183
+
184
+ persona_factory: PersonaFactory = cl.user_session.get("persona_factory")
185
+ selected_persona_ids = state.get("selected_personas", [])
186
+ query = state.get("query")
187
+
188
+ if not selected_persona_ids:
189
+ state["persona_responses"] = {"error": "No personas selected for perspective generation."}
190
+ state["current_step_name"] = "synthesize_responses" # Or an error handling state
191
+ if progress_msg: completed_steps_log.append(f"{activity_emoji} {activity_description} - No personas selected.")
192
+ cl.user_session.set("completed_steps_log", completed_steps_log)
193
+ return state
194
+
195
+ await cl.Message(content=f"Invoking {len(selected_persona_ids)} personas...").send()
196
+
197
+ tasks = []
198
+ # --- RAG Enhancement ---
199
+ embedding_model = cl.user_session.get("embedding_model_instance") # <--- GET MODEL FROM SESSION
200
+ global_rag_enabled = cl.user_session.get("enable_rag", True)
201
+ # --- End RAG Enhancement ---
202
+
203
+ valid_persona_ids_for_results = [] # Keep track of personas for which tasks were created
204
+ for persona_id in selected_persona_ids:
205
+ persona_llm = PERSONA_LLM_MAP.get(persona_id.lower())
206
+ if not persona_llm:
207
+ print(f"Warning: LLM not found for persona {persona_id}. Skipping.")
208
+ continue
209
+
210
+ persona = persona_factory.create_persona(persona_id, persona_llm)
211
+ if persona:
212
+ final_query_for_llm = query # Default to original query
213
+
214
+ # --- RAG Integration for Balanced Team Personas ---
215
+ if global_rag_enabled and persona.persona_id.lower() in RAG_ENABLED_PERSONA_IDS: # Check global toggle first; Changed persona.id to persona.persona_id
216
+ if embedding_model:
217
+ rag_progress_message = f"\n 🔍 Persona '{persona.name}' (RAG enabled): Searching knowledge base for context related to: '{query[:50]}...'"
218
+ if progress_msg: await progress_msg.stream_token(rag_progress_message)
219
+ print(rag_progress_message.strip())
220
+
221
+ retrieved_context = await get_relevant_context_for_query(query, persona.persona_id, embedding_model)
222
+
223
+ if retrieved_context:
224
+ context_log_msg = f" ✅ Context retrieved for '{persona.name}'. Augmenting prompt."
225
+ # print(f"Retrieved context for {persona.id}:\n{retrieved_context}") # Full context - verbose
226
+ final_query_for_llm = f"""As the {persona.name}, consider the following retrieved context to answer the user's query.
227
+ Integrate this context with your inherent expertise and perspective.
228
+
229
+ Retrieved Context:
230
+ ---
231
+ {retrieved_context}
232
+ ---
233
+
234
+ User Query: {query}
235
+
236
+ Answer as {persona.name}:
237
+ """
238
+ if progress_msg: await progress_msg.stream_token(f"\n 💡 Context found for {persona.name}. Crafting response...")
239
+ print(context_log_msg)
240
+ else:
241
+ no_context_log_msg = f" ℹ️ No specific context found for '{persona.name}' for this query. Relying on general knowledge."
242
+ if progress_msg: await progress_msg.stream_token(f"\n 🧐 No specific context found for {persona.name}. Proceeding with general knowledge...")
243
+ print(no_context_log_msg)
244
+ # Prompt when no context is found - persona relies on its base system prompt and expertise
245
+ # The persona.generate_perspective method will use its inherent system_prompt.
246
+ # We can just pass the original query, or a slightly modified one indicating no specific context was found.
247
+ final_query_for_llm = f"""As the {persona.name}, answer the user's query using your inherent expertise and perspective.
248
+ No specific context from the knowledge base was retrieved for this particular query.
249
+
250
+ User Query: {query}
251
+
252
+ Answer as {persona.name}:
253
+ """
254
+ else:
255
+ no_embed_msg = f"\n ⚠️ Embedding model (current selection) not available for RAG for persona '{persona.name}'. Using general knowledge."
256
+ if progress_msg: await progress_msg.stream_token(no_embed_msg)
257
+ print(no_embed_msg.strip())
258
+ # --- End RAG Integration ---
259
+
260
+ # Stream persona-specific LLM call message
261
+ llm_call_msg = f"\n 🗣️ Consulting AI for {persona.name} perspective..."
262
+ if progress_msg: await progress_msg.stream_token(llm_call_msg)
263
+ print(llm_call_msg.strip())
264
+
265
+ tasks.append(persona.generate_perspective(final_query_for_llm)) # Use final_query_for_llm
266
+ valid_persona_ids_for_results.append(persona_id)
267
+ else:
268
+ print(f"Warning: Could not create persona object for {persona_id}. Skipping.")
269
+
270
+ state["persona_responses"] = {} # Initialize/clear previous responses
271
+ if tasks:
272
+ try:
273
+ # Timeout logic can be added here if needed for asyncio.gather
274
+ if progress_msg: await progress_msg.stream_token(f"\n ↪ Invoking {len(valid_persona_ids_for_results)} personas...")
275
+ persona_results = await asyncio.gather(*tasks)
276
+ # Store responses keyed by the valid persona_ids used for tasks
277
+ for i, persona_id in enumerate(valid_persona_ids_for_results):
278
+ state["persona_responses"][persona_id] = persona_results[i]
279
+ except Exception as e:
280
+ print(f"Error during persona perspective generation: {e}")
281
+ state["error_message"] = f"Error generating perspectives: {str(e)[:100]}"
282
+ # Optionally, populate partial results if some tasks succeeded before error
283
+ # For now, just reports error. Individual task errors could be handled in PersonaReasoning too.
284
+
285
+ if progress_msg:
286
+ completed_activity_description = "Perspectives generated."
287
+ # Emoji for this completed step was 🧠 from current_activity_display
288
+ completed_steps_log.append(f"{activity_emoji} {completed_activity_description}")
289
+ cl.user_session.set("completed_steps_log", completed_steps_log)
290
+
291
+ # Display updated completed list; the next node will set the new "Current Activity"
292
+ # For this brief moment, show only completed steps before next node updates with its current activity
293
+ progress_msg.content = f"**Completed Steps:**\n{(chr(10)).join(completed_steps_log)}\n\n**Current Activity:**\n✅ Perspectives generated. (60%)" # Show this as current before next node takes over
294
+ await progress_msg.update()
295
+
296
+ state["current_step_name"] = "synthesize_responses"
297
+ return state
298
+
299
+ async def synthesize_responses(state: InsightFlowState) -> InsightFlowState:
300
+ print(f"Node: synthesize_responses")
301
+ progress_msg = cl.user_session.get("progress_msg")
302
+ completed_steps_log = cl.user_session.get("completed_steps_log")
303
+
304
+ activity_description = "Synthesizing insights..."
305
+ activity_emoji = "✍️"
306
+ current_activity_display = f"{activity_emoji} {activity_description} (65%)"
307
+
308
+ if progress_msg:
309
+ progress_msg.content = f"**Current Activity:**\n{current_activity_display}"
310
+ if completed_steps_log:
311
+ progress_msg.content = f"**Completed Steps:**\n{(chr(10)).join(completed_steps_log)}\n\n{progress_msg.content}"
312
+ await progress_msg.update()
313
+
314
+ persona_responses = state.get("persona_responses", {})
315
+
316
+ if not persona_responses:
317
+ print("No persona responses to synthesize.")
318
+ state["synthesized_response"] = "No perspectives were available to synthesize."
319
+ state["current_step_name"] = "generate_visualization"
320
+ return state
321
+
322
+ formatted_perspectives_list = []
323
+ for persona_id, response_text in persona_responses.items():
324
+ formatted_perspectives_list.append(f"- Perspective from {persona_id}: {response_text}")
325
+
326
+ formatted_perspectives_string = "\n".join(formatted_perspectives_list)
327
+
328
+ final_prompt_content = SYNTHESIZER_SYSTEM_PROMPT_TEMPLATE.format(
329
+ formatted_perspectives=formatted_perspectives_string
330
+ )
331
+
332
+ messages = [
333
+ SystemMessage(content=final_prompt_content)
334
+ ]
335
+
336
+ try:
337
+ # Ensure llm_synthesizer is available (initialized by initialize_configurations)
338
+ if llm_synthesizer is None:
339
+ print("Error: llm_synthesizer is not initialized.")
340
+ state["error_message"] = "Synthesizer LLM not available."
341
+ state["synthesized_response"] = "Synthesis failed due to internal error."
342
+ state["current_step_name"] = "error_presenting" # Or a suitable error state
343
+ return state
344
+
345
+ ai_response = await llm_synthesizer.ainvoke(messages)
346
+ synthesized_text = ai_response.content
347
+ state["synthesized_response"] = synthesized_text
348
+ print(f"Synthesized response: {synthesized_text[:200]}...") # Log snippet
349
+ except Exception as e:
350
+ print(f"Error during synthesis: {e}")
351
+ state["error_message"] = f"Synthesis error: {str(e)[:100]}"
352
+ state["synthesized_response"] = "Synthesis failed."
353
+ # Optionally, decide if we proceed to visualization or an error state
354
+ # For now, let's assume we still try to visualize if there's a partial/failed synthesis
355
+
356
+ if progress_msg:
357
+ completed_activity_description = "Insights synthesized."
358
+ completed_steps_log.append(f"{activity_emoji} {completed_activity_description}")
359
+ cl.user_session.set("completed_steps_log", completed_steps_log)
360
+ progress_msg.content = f"**Completed Steps:**\n{(chr(10)).join(completed_steps_log)}\n\n**Current Activity:**\n✅ Insights synthesized. (80%)"
361
+ await progress_msg.update()
362
+
363
+ state["current_step_name"] = "generate_visualization"
364
+ return state
365
+
366
+ async def generate_visualization(state: InsightFlowState) -> InsightFlowState:
367
+ print(f"Node: generate_visualization")
368
+ progress_msg = cl.user_session.get("progress_msg")
369
+ completed_steps_log = cl.user_session.get("completed_steps_log")
370
+
371
+ activity_description = "Creating visualizations..."
372
+ activity_emoji = "🎨"
373
+ current_activity_display = f"{activity_emoji} {activity_description} (85%)"
374
+
375
+ if progress_msg:
376
+ progress_msg.content = f"**Current Activity:**\n{current_activity_display}"
377
+ if completed_steps_log:
378
+ progress_msg.content = f"**Completed Steps:**\n{(chr(10)).join(completed_steps_log)}\n\n{progress_msg.content}"
379
+ await progress_msg.update()
380
+
381
+ synthesized_response = state.get("synthesized_response")
382
+ image_url = None
383
+ mermaid_code_output = None # Changed variable name for clarity
384
+
385
+ # DALL-E Image Generation (existing logic)
386
+ if synthesized_response and openai_async_client:
387
+ # Log the full DALL-E prompt for debugging if issues persist
388
+ # print(f"Full DALL-E prompt for visualization: {dalle_prompt}")
389
+ dalle_prompt = f"A hand-drawn style visual note or sketch representing the key concepts of: {synthesized_response}"
390
+ if len(dalle_prompt) > 4000:
391
+ dalle_prompt = dalle_prompt[:3997] + "..."
392
+
393
+ print(f"Attempting DALL-E image generation for: {dalle_prompt[:100]}...")
394
+ image_url = await generate_dalle_image(prompt=dalle_prompt, client=openai_async_client)
395
+ if image_url:
396
+ state["visualization_image_url"] = image_url
397
+ print(f"DALL-E Image URL: {image_url}")
398
+ else:
399
+ print("DALL-E image generation failed or returned no URL.")
400
+ state["visualization_image_url"] = None
401
+ elif not synthesized_response:
402
+ print("No synthesized response available to generate DALL-E image.")
403
+ state["visualization_image_url"] = None
404
+ elif not openai_async_client:
405
+ print("OpenAI async client not initialized, skipping DALL-E generation.")
406
+ state["visualization_image_url"] = None
407
+
408
+ # Mermaid Code Generation
409
+ if synthesized_response and llm_mermaid_generator: # Check if both are available
410
+ print(f"Attempting Mermaid code generation for: {synthesized_response[:100]}...")
411
+ mermaid_code_output = await generate_mermaid_code(synthesized_response, llm_mermaid_generator)
412
+ if mermaid_code_output:
413
+ state["visualization_code"] = mermaid_code_output
414
+ print(f"Mermaid code generated: {mermaid_code_output[:100]}...")
415
+ else:
416
+ print("Mermaid code generation failed or returned no code.")
417
+ state["visualization_code"] = None # Ensure it's None if failed
418
+ else:
419
+ print("Skipping Mermaid code generation due to missing response or LLM.")
420
+ state["visualization_code"] = None
421
+
422
+ if progress_msg:
423
+ completed_activity_description = "Visualizations created."
424
+ completed_steps_log.append(f"{activity_emoji} {completed_activity_description}")
425
+ cl.user_session.set("completed_steps_log", completed_steps_log)
426
+ progress_msg.content = f"**Completed Steps:**\n{(chr(10)).join(completed_steps_log)}\n\n**Current Activity:**\n✅ Visualizations created. (95%)"
427
+ await progress_msg.update()
428
+
429
+ state["current_step_name"] = "present_results"
430
+ return state
431
+
432
+ async def present_results(state: InsightFlowState) -> InsightFlowState:
433
+ print(f"Node: present_results")
434
+ progress_msg = cl.user_session.get("progress_msg")
435
+ completed_steps_log = cl.user_session.get("completed_steps_log")
436
+
437
+ activity_description = "Preparing final presentation..."
438
+ activity_emoji = "🎁"
439
+ current_activity_display = f"{activity_emoji} {activity_description} (98%)"
440
+
441
+ if progress_msg:
442
+ progress_msg.content = f"**Current Activity:**\n{current_activity_display}"
443
+ if completed_steps_log:
444
+ progress_msg.content = f"**Completed Steps:**\n{(chr(10)).join(completed_steps_log)}\n\n{progress_msg.content}"
445
+ await progress_msg.update()
446
+
447
+ show_visualization = cl.user_session.get("show_visualization", True)
448
+ show_perspectives = cl.user_session.get("show_perspectives", True)
449
+ synthesized_response = state.get("synthesized_response")
450
+
451
+ # 1. Send Synthesized Response (always)
452
+ if synthesized_response:
453
+ await cl.Message(content=synthesized_response, author="Synthesized Insight").send()
454
+ else:
455
+ await cl.Message(content="No synthesized response was generated.", author="System").send()
456
+
457
+ # 2. Send Visualizations (if enabled and available)
458
+ if show_visualization:
459
+ image_url = state.get("visualization_image_url")
460
+ if image_url:
461
+ image_element = cl.Image(
462
+ url=image_url,
463
+ name="dalle_visualization",
464
+ display="inline",
465
+ size="large"
466
+ )
467
+ # Send image with a title in the content or as a separate message
468
+ await cl.Message(content="Visual Summary:", elements=[image_element], author="System").send()
469
+
470
+ mermaid_code = state.get("visualization_code")
471
+ if mermaid_code:
472
+ mermaid_element = cl.Text(
473
+ content=mermaid_code,
474
+ mime_type="text/mermaid",
475
+ name="generated_diagram",
476
+ display="inline"
477
+ )
478
+ await cl.Message(content="Concept Map:", elements=[mermaid_element], author="System").send()
479
+
480
+ # 3. Send Persona Perspectives within cl.Collapse (if enabled and available)
481
+ if show_perspectives:
482
+ persona_responses = state.get("persona_responses")
483
+ if persona_responses:
484
+ perspective_elements = []
485
+ for persona_id, response_text in persona_responses.items():
486
+ if response_text:
487
+ # Temporarily revert to sending simple messages for perspectives
488
+ await cl.Message(content=f"**Perspective from {persona_id.capitalize()}:**\n{response_text}", author=persona_id.capitalize()).send()
489
+ # perspective_elements.append(
490
+ # Collapse( # <--- COMMENT OUT USAGE
491
+ # label=f"👁️ Perspective from {persona_id.capitalize()}",
492
+ # content=response_text,
493
+ # initial_collapsed=True # Start collapsed
494
+ # )
495
+ # )
496
+ # if perspective_elements:
497
+ # await cl.Message(
498
+ # content="Dive Deeper into Individual Perspectives:",
499
+ # elements=perspective_elements,
500
+ # author="System"
501
+ # ).send()
502
+
503
+ state["current_step_name"] = "results_presented"
504
+
505
+ if progress_msg:
506
+ # Log the "Preparing presentation" step as completed first
507
+ completed_steps_log.append(f"{activity_emoji} {activity_description}")
508
+ cl.user_session.set("completed_steps_log", completed_steps_log)
509
+
510
+ # Show the 100% completion message as current activity initially
511
+ final_emoji = "✨"
512
+ final_message_description = "All insights presented!"
513
+ current_activity_display = f"{final_emoji} {final_message_description} (100%)"
514
+
515
+ progress_msg.content = f"**Completed Steps:**\n{(chr(10)).join(completed_steps_log)}\n\n**Current Activity:**\n{current_activity_display}"
516
+ await progress_msg.update()
517
+
518
+ # Now, move the 100% step to completed and finalize the progress message
519
+ await cl.sleep(0.5) # Optional short delay for UI to update before final change
520
+ completed_steps_log.append(f"{final_emoji} {final_message_description}")
521
+ cl.user_session.set("completed_steps_log", completed_steps_log)
522
+
523
+ # Generate witty remark
524
+ query_for_remark = state.get("query", "this topic")
525
+ personas_for_remark = state.get("selected_personas", [])
526
+ snippet_for_remark = state.get("synthesized_response")
527
+ witty_remark = await generate_witty_completion_remark(query_for_remark, personas_for_remark, snippet_for_remark)
528
+
529
+ progress_msg.content = f"**Completed Steps:**\n{(chr(10)).join(completed_steps_log)}\n\n{witty_remark}"
530
+ await progress_msg.update()
531
+
532
+ # Send a final message that all content is loaded, then actions
533
+ await cl.Message(content="All insights presented. You can now export the results.").send()
534
+
535
+ # Add Export Actions
536
+ export_actions = [
537
+ cl.Action(name="export_markdown", label="Export to Markdown", value="markdown", description="Export results to a Markdown file.", payload={}),
538
+ cl.Action(name="export_pdf", label="Export to PDF", value="pdf", description="Export results to a PDF file.", payload={})
539
+ ]
540
+ await cl.Message(content="", actions=export_actions).send()
541
+
542
+ return state
543
+
544
+ # --- LANGGRAPH SETUP ---
545
+ insight_graph_builder = StateGraph(InsightFlowState)
546
+
547
+ # Add nodes
548
+ insight_graph_builder.add_node("planner_agent", run_planner_agent)
549
+ insight_graph_builder.add_node("execute_persona_tasks", execute_persona_tasks)
550
+ insight_graph_builder.add_node("synthesize_responses", synthesize_responses)
551
+ insight_graph_builder.add_node("generate_visualization", generate_visualization)
552
+ insight_graph_builder.add_node("present_results", present_results)
553
+
554
+ # Set entry point
555
+ insight_graph_builder.set_entry_point("planner_agent")
556
+
557
+ # Add edges
558
+ insight_graph_builder.add_edge("planner_agent", "execute_persona_tasks")
559
+ insight_graph_builder.add_edge("execute_persona_tasks", "synthesize_responses")
560
+ insight_graph_builder.add_edge("synthesize_responses", "generate_visualization")
561
+ insight_graph_builder.add_edge("generate_visualization", "present_results")
562
+ insight_graph_builder.add_edge("present_results", END)
563
+
564
+ # Compile the graph
565
+ insight_flow_graph = insight_graph_builder.compile()
566
+
567
+ print("LangGraph setup complete.")
568
+
569
+ # --- CUSTOM CALLBACK HANDLER FOR PROGRESS UPDATES --- #
570
+ class InsightFlowCallbackHandler(AsyncCallbackHandler):
571
+ def __init__(self, progress_message: cl.Message):
572
+ self.progress_message = progress_message
573
+ self.step_counter = 0
574
+
575
+ async def on_chain_start(
576
+ self, serialized: Dict[str, Any], inputs: Dict[str, Any], **kwargs: Any
577
+ ) -> None:
578
+ """Run when chain starts running. Now simplified as nodes will provide more specific updates."""
579
+ # This callback might still be useful for very generic LangGraph level start/stop
580
+ # but specific node progress will be handled within the node functions themselves.
581
+ # Avoid printing "Unknown chain/node" if possible.
582
+ # langgraph_event_name = serialized.get("name") or (serialized.get("id")[-1] if isinstance(serialized.get("id"), list) and serialized.get("id") else None)
583
+ # if self.progress_message and langgraph_event_name and not langgraph_event_name.startswith("__"):
584
+ # self.step_counter += 1
585
+ # await self.progress_message.stream_token(f"\n⏳ Step {self.step_counter}: Processing {langgraph_event_name}...\")
586
+ pass # Node functions will now handle more granular progress updates
587
+
588
+ async def on_llm_start(
589
+ self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any
590
+ ) -> None:
591
+ """Run when LLM starts running. This will now be more generic or can be removed if node-specific messages are enough."""
592
+ # llm_display_name = "LLM" # Sensible default
593
+ # if serialized:
594
+ # id_list = serialized.get("id")
595
+ # if isinstance(id_list, list) and len(id_list) > 0:
596
+ # raw_name_part = id_list[0]
597
+ # if isinstance(raw_name_part, str):
598
+ # if "ChatOpenAI".lower() in raw_name_part.lower():
599
+ # llm_display_name = "OpenAI Model"
600
+ # elif "LLM" in raw_name_part.upper():
601
+ # llm_display_name = raw_name_part
602
+
603
+ # name_val = serialized.get("name")
604
+ # if isinstance(name_val, str) and name_val != "Unknown LLM":
605
+ # if llm_display_name == "LLM" or len(name_val) > len(llm_display_name):
606
+ # llm_display_name = name_val
607
+
608
+ # update_text = f"🗣️ Consulting {llm_display_name}... "
609
+ # if self.progress_message:
610
+ # # print(f"DEBUG on_llm_start serialized: {serialized}")
611
+ # await self.progress_message.stream_token(f"\n L {update_text}")
612
+ pass # Specific LLM call messages are now sent from execute_persona_tasks
613
+
614
+ # We can add on_agent_action, on_tool_start for more granular updates if nodes use agents/tools
615
+ # For now, on_chain_start (which LangGraph nodes trigger) and on_llm_start should give good visibility.
616
+
617
+ async def on_chain_end(self, outputs: Dict[str, Any], **kwargs: Any) -> None:
618
+ """Run when chain ends running."""
619
+ # chain_name = kwargs.get("name", "a process") # LangGraph provides name in kwargs for on_chain_end
620
+ # if self.progress_message:
621
+ # await self.progress_message.stream_token(f"\nCompleted: {chain_name}.\")
622
+ pass # Node functions will provide end-of-node context if needed
623
+
624
+ # This allows the test to import these names from app if needed
625
+ __all__ = [
626
+ "InsightFlowState", "on_chat_start", "on_message",
627
+ "invoke_direct_llm", "invoke_langgraph",
628
+ "run_planner_agent", "execute_persona_tasks", "synthesize_responses",
629
+ "generate_visualization", "present_results",
630
+ "StateGraph", # Expose StateGraph if tests patch app.StateGraph directly
631
+ "initialize_configurations", # Expose for testing
632
+ "on_export_markdown", "on_export_pdf" # Add new export handlers
633
+ ]
634
+
635
+ # --- CHAT SETTINGS HELPER ---
636
+ async def _apply_chat_settings_to_state():
637
+ """Reads chat settings and updates session and insight_flow_state."""
638
+ chat_settings_values = cl.user_session.get("chat_settings", {})
639
+ insight_flow_state: InsightFlowState = cl.user_session.get("insight_flow_state")
640
+ persona_factory: PersonaFactory = cl.user_session.get("persona_factory")
641
+
642
+ if not insight_flow_state or not persona_factory:
643
+ # Should not happen if on_chat_start ran correctly
644
+ print("Error: State or persona factory not found while applying chat settings.")
645
+ return
646
+
647
+ # Update modes in user_session
648
+ cl.user_session.set("direct_mode", chat_settings_values.get("direct_mode", False))
649
+ cl.user_session.set("quick_mode", chat_settings_values.get("quick_mode", False))
650
+ cl.user_session.set("show_perspectives", chat_settings_values.get("show_perspectives", True))
651
+ cl.user_session.set("show_visualization", chat_settings_values.get("show_visualization", True))
652
+ cl.user_session.set("enable_rag", chat_settings_values.get("enable_rag", True)) # Read the RAG toggle
653
+
654
+ # Embedding model toggle
655
+ new_use_finetuned_setting = chat_settings_values.get("use_finetuned_embedding", False)
656
+ current_use_finetuned_setting = cl.user_session.get("use_finetuned_embedding", False)
657
+ cl.user_session.set("use_finetuned_embedding", new_use_finetuned_setting)
658
+
659
+ if new_use_finetuned_setting != current_use_finetuned_setting:
660
+ print("Embedding model setting changed. Re-initializing model.")
661
+ await _initialize_embedding_model_in_session() # Re-initialize if toggle changed
662
+
663
+ # Update selected_personas in insight_flow_state
664
+ selected_personas_from_settings = []
665
+ available_persona_ids = persona_factory.get_available_personas().keys()
666
+ for persona_id in available_persona_ids:
667
+ if chat_settings_values.get(f"persona_{persona_id}", False):
668
+ selected_personas_from_settings.append(persona_id)
669
+
670
+ # Check if a team is selected and override individual selections
671
+ selected_team_id = chat_settings_values.get("selected_team")
672
+ if selected_team_id and selected_team_id != "none":
673
+ team_info = PERSONA_TEAMS.get(selected_team_id)
674
+ if team_info and "members" in team_info:
675
+ selected_personas_from_settings = list(team_info["members"]) # Use team members
676
+ print(f"Team '{team_info['name']}' selected, overriding individual personas.")
677
+
678
+ mutable_state = insight_flow_state.copy()
679
+ mutable_state["selected_personas"] = selected_personas_from_settings
680
+ cl.user_session.set("insight_flow_state", mutable_state)
681
+
682
+ print(f"Applied chat settings: Direct Mode: {cl.user_session.get('direct_mode')}, Quick Mode: {cl.user_session.get('quick_mode')}")
683
+ print(f"Selected Personas from settings: {selected_personas_from_settings}")
684
+
685
+ @cl.on_chat_start
686
+ async def on_chat_start():
687
+ """Initializes session state, chat settings, and sends a welcome message."""
688
+ # global _configurations_initialized # No longer need _embedding_model_initialized here
689
+ # The global keyword is only needed if you assign to the global variable in this scope.
690
+ # We are only reading _configurations_initialized here.
691
+
692
+ if not _configurations_initialized:
693
+ initialize_configurations()
694
+
695
+ # Initialize with default embedding model (OpenAI) first
696
+ # This sets "use_finetuned_embedding" to False in session if not already there
697
+ await _initialize_embedding_model_in_session(force_openai=True)
698
+
699
+ persona_factory = PersonaFactory()
700
+ cl.user_session.set("persona_factory", persona_factory)
701
+
702
+ # Default selections for personas/teams
703
+ default_team_id = "balanced_overview"
704
+ default_selected_personas = list(PERSONA_TEAMS[default_team_id]["members"])
705
+
706
+ # Default UI toggles (excluding embedding toggle, handled by _initialize_embedding_model_in_session)
707
+ default_direct_mode = False
708
+ default_quick_mode = False
709
+ default_show_perspectives = True
710
+ default_show_visualization = True
711
+ default_enable_rag = True # Default RAG to ON
712
+ # default_use_finetuned_embedding is set by _initialize_embedding_model_in_session(force_openai=True)
713
+
714
+ initial_state = InsightFlowState(
715
+ panel_type="research",
716
+ query="",
717
+ selected_personas=default_selected_personas,
718
+ persona_responses={},
719
+ synthesized_response=None,
720
+ visualization_code=None,
721
+ visualization_image_url=None,
722
+ current_step_name="awaiting_query",
723
+ error_message=None
724
+ )
725
+ cl.user_session.set("insight_flow_state", initial_state)
726
+
727
+ cl.user_session.set("direct_mode", default_direct_mode)
728
+ cl.user_session.set("quick_mode", default_quick_mode)
729
+ cl.user_session.set("show_perspectives", default_show_perspectives)
730
+ cl.user_session.set("show_visualization", default_show_visualization)
731
+ cl.user_session.set("enable_rag", default_enable_rag)
732
+
733
+ settings_inputs = []
734
+
735
+ settings_inputs.append(
736
+ Switch(id="enable_rag", label="⚙️ Enable RAG Features (for supported personas)", initial=default_enable_rag)
737
+ )
738
+ settings_inputs.append(
739
+ Switch(id="use_finetuned_embedding",
740
+ label="🔬 Use Fine-tuned Embedding (Balanced Team - if available)",
741
+ initial=cl.user_session.get("use_finetuned_embedding", False)) # Initial from session
742
+ )
743
+
744
+ team_items = {"-- Select a Team (Optional) --": "none"}
745
+ for team_id, team_info in PERSONA_TEAMS.items():
746
+ team_items[team_info["name"]] = team_id
747
+
748
+ settings_inputs.append(
749
+ Select(
750
+ id="selected_team",
751
+ label="🎯 Persona Team (Overrides individual toggles for processing)", # Corrected quotes
752
+ items=team_items,
753
+ initial_value=default_team_id # This should be fine as it's a variable
754
+ )
755
+ )
756
+ # settings_inputs.append(cl.Divider()) # Keep commented for now
757
+
758
+ for persona_id, persona_config in persona_factory.persona_configs.items():
759
+ settings_inputs.append(
760
+ Switch(
761
+ id=f"persona_{persona_id}",
762
+ label=persona_config["name"],
763
+ initial=persona_id in default_selected_personas
764
+ )
765
+ )
766
+
767
+ settings_inputs.extend([
768
+ Switch(id="direct_mode", label="🚀 Direct Mode (Quick, single LLM answers)", initial=default_direct_mode),
769
+ Switch(id="quick_mode", label="⚡ Quick Mode (Uses max 2 personas)", initial=default_quick_mode),
770
+ Switch(id="show_perspectives", label="👁️ Show Individual Perspectives", initial=default_show_perspectives),
771
+ Switch(id="show_visualization", label="🎨 Show Visualizations (DALL-E & Mermaid)", initial=default_show_visualization)
772
+ ])
773
+
774
+ await cl.ChatSettings(inputs=settings_inputs).send()
775
+
776
+ # New Welcome Message
777
+ welcome_message_content = "🚀 **Welcome to InsightFlow AI!** Dive deep into any topic with a symphony of AI perspectives. I can analyze, synthesize, and even visualize complex information. Configure your AI team using the ⚙️ settings, then ask your question!"
778
+
779
+ await cl.Message(content=welcome_message_content).send()
780
+
781
+ cl.user_session.set("progress_msg", None)
782
+
783
+ # Placeholder for direct LLM invocation logic
784
+ async def invoke_direct_llm(query: str):
785
+ print(f"invoke_direct_llm called with query: {query}")
786
+
787
+ messages = [
788
+ SystemMessage(content=DIRECT_SYSPROMPT),
789
+ HumanMessage(content=query)
790
+ ]
791
+
792
+ response_message = cl.Message(content="")
793
+ await response_message.send()
794
+
795
+ async for chunk in llm_direct.astream(messages):
796
+ if chunk.content:
797
+ await response_message.stream_token(chunk.content)
798
+
799
+ await response_message.update() # Finalize the streamed message
800
+ return "Direct response streamed" # Test expects a return, actual content is streamed
801
+
802
+ # Placeholder for LangGraph invocation logic
803
+ async def invoke_langgraph(query: str, initial_state: InsightFlowState):
804
+ print(f"invoke_langgraph called with query: {query}")
805
+
806
+ cl.user_session.set("completed_steps_log", [])
807
+
808
+ progress_msg = cl.Message(content="")
809
+ await progress_msg.send()
810
+ # Initial message only shows current activity, no completed steps yet.
811
+ progress_msg.content = "**Current Activity:**\n⏳ Initializing InsightFlow process... (0%)"
812
+ await progress_msg.update()
813
+ cl.user_session.set("progress_msg", progress_msg)
814
+
815
+ # Setup callback handler - still useful for LLM calls or other low-level events if desired
816
+ callback_handler = InsightFlowCallbackHandler(progress_message=progress_msg)
817
+
818
+ current_state = initial_state.copy() # Work with a copy
819
+ current_state["query"] = query
820
+ current_state["current_step_name"] = "planner_agent" # Reset step for new invocation
821
+
822
+ # Check for Quick Mode and adjust personas if needed
823
+ quick_mode_active = cl.user_session.get("quick_mode", False) # Default to False if not set
824
+ if quick_mode_active:
825
+ print("Quick Mode is ON. Using predefined quick mode personas.")
826
+ current_state["selected_personas"] = list(QUICK_MODE_PERSONAS) # Ensure it's a new list copy
827
+ # If quick_mode is OFF, selected_personas from initial_state (set by on_chat_start or commands) will be used.
828
+
829
+ # Prepare config for LangGraph invocation (e.g., for session/thread ID)
830
+ # In a Chainlit context, cl.user_session.get("id") can give a thread_id
831
+ thread_id = cl.user_session.get("id", "default_thread_id") # Get Chainlit thread_id or a default
832
+ config = {
833
+ "configurable": {"thread_id": thread_id},
834
+ "callbacks": [callback_handler] # Add our callback handler
835
+ }
836
+
837
+ # progress_msg = cl.Message(content="⏳ Processing with InsightFlow (0%)...") # OLD TODO
838
+ # await progress_msg.send()
839
+ # cl.user_session.set("progress_msg", progress_msg)
840
+
841
+ final_state = await insight_flow_graph.ainvoke(current_state, config=config)
842
+
843
+ # Final progress update
844
+ if progress_msg:
845
+ await progress_msg.update() # Ensure final content (100%) is sent and displayed
846
+
847
+ # The present_results node should handle sending messages.
848
+ # invoke_langgraph will return the final state which on_message saves.
849
+ return final_state
850
+
851
+ @cl.on_message
852
+ async def on_message(message: cl.Message):
853
+ """Handles incoming user messages and routes based on direct_mode."""
854
+
855
+ # Apply the latest settings from UI to the session and state
856
+ await _apply_chat_settings_to_state()
857
+
858
+ direct_mode = cl.user_session.get("direct_mode") # Values now reflect chat settings
859
+ msg_content_lower = message.content.lower().strip()
860
+
861
+ # Command handling (can be kept as backups or for power users)
862
+ if msg_content_lower == "/direct on":
863
+ cl.user_session.set("direct_mode", True)
864
+ await cl.Message(content="Direct mode ENABLED.").send()
865
+ return # Command processed, no further action
866
+ elif msg_content_lower == "/direct off":
867
+ cl.user_session.set("direct_mode", False)
868
+ await cl.Message(content="Direct mode DISABLED.").send()
869
+ return # Command processed, no further action
870
+
871
+ # Command handling for /show perspectives
872
+ elif msg_content_lower == "/show perspectives on":
873
+ cl.user_session.set("show_perspectives", True)
874
+ await cl.Message(content="Show perspectives ENABLED.").send()
875
+ return
876
+ elif msg_content_lower == "/show perspectives off":
877
+ cl.user_session.set("show_perspectives", False)
878
+ await cl.Message(content="Show perspectives DISABLED.").send()
879
+ return
880
+
881
+ # Command handling for /show visualization
882
+ elif msg_content_lower == "/show visualization on":
883
+ cl.user_session.set("show_visualization", True)
884
+ await cl.Message(content="Show visualization ENABLED.").send()
885
+ return
886
+ elif msg_content_lower == "/show visualization off":
887
+ cl.user_session.set("show_visualization", False)
888
+ await cl.Message(content="Show visualization DISABLED.").send()
889
+ return
890
+
891
+ # Command handling for /quick_mode
892
+ elif msg_content_lower == "/quick_mode on":
893
+ cl.user_session.set("quick_mode", True)
894
+ await cl.Message(content="Quick mode ENABLED.").send()
895
+ return
896
+ elif msg_content_lower == "/quick_mode off":
897
+ cl.user_session.set("quick_mode", False)
898
+ await cl.Message(content="Quick mode DISABLED.").send()
899
+ return
900
+ elif msg_content_lower == "/help": # <-- ADD /help COMMAND HANDLER
901
+ await send_help_message()
902
+ return
903
+
904
+ # If not a /direct command, proceed with existing direct_mode check for LLM calls
905
+ if direct_mode: # This direct_mode is now sourced from chat settings via _apply_chat_settings_to_state
906
+ await invoke_direct_llm(message.content)
907
+ else:
908
+ insight_flow_state = cl.user_session.get("insight_flow_state")
909
+ if not insight_flow_state:
910
+ # Fallback if state isn't somehow initialized
911
+ await cl.Message(content="Error: Session state not found. Please restart the chat.").send()
912
+ return
913
+
914
+ # The selected_personas in insight_flow_state have been updated by _apply_chat_settings_to_state
915
+ # The quick_mode check within invoke_langgraph will use cl.user_session.get("quick_mode")
916
+ # which was also updated by _apply_chat_settings_to_state.
917
+ updated_state = await invoke_langgraph(message.content, insight_flow_state)
918
+ cl.user_session.set("insight_flow_state", updated_state) # Save updated state
919
+
920
+ print("app.py initialized with LLMs, on_chat_start, and on_message defined")
921
+
922
+ # --- NEW EXPORT ACTION STUBS ---
923
+ @cl.action_callback("export_markdown")
924
+ async def on_export_markdown(action: cl.Action):
925
+ # Placeholder for Markdown export logic
926
+ await cl.Message(content=f"Markdown export for action '{action.name}' initiated (not fully implemented).").send()
927
+
928
+ @cl.action_callback("export_pdf")
929
+ async def on_export_pdf(action: cl.Action):
930
+ # Placeholder for PDF export logic
931
+ await cl.Message(content=f"PDF export for action '{action.name}' initiated (not fully implemented).").send()
932
+
933
+ # --- NEW FUNCTION FOR WITTY COMPLETION REMARKS ---
934
+ async def generate_witty_completion_remark(query: str, selected_persona_ids: List[str], synthesized_snippet: Optional[str]) -> str:
935
+ if not llm_direct: # Ensure llm_direct is initialized
936
+ print("llm_direct not initialized, cannot generate witty remark.")
937
+ return "Intellectual journey complete! Bravo! ✔️" # Fallback
938
+
939
+ persona_factory: PersonaFactory = cl.user_session.get("persona_factory")
940
+ persona_names = []
941
+ if persona_factory:
942
+ for pid in selected_persona_ids:
943
+ config = persona_factory.persona_configs.get(pid)
944
+ if config and config.get("name"):
945
+ persona_names.append(config["name"])
946
+ else:
947
+ persona_names.append(pid.capitalize())
948
+ else:
949
+ persona_names = [pid.capitalize() for pid in selected_persona_ids]
950
+
951
+ persona_names_str = ", ".join(persona_names) if persona_names else "various"
952
+ if not synthesized_snippet: synthesized_snippet = "a fascinating topic"
953
+ if len(synthesized_snippet) > 100: synthesized_snippet = synthesized_snippet[:97] + "..."
954
+
955
+ prompt_template = f"""You are a charming and slightly cheeky AI host, like a talk show host wrapping up a segment.
956
+ The user just explored the query: '{query}'
957
+ with insights from {persona_names_str} perspectives.
958
+ The main takeaway was about: '{synthesized_snippet}'.
959
+ Craft a short, witty, and encouraging closing remark (1-2 sentences, max 25 words) to signify the completion.
960
+ Example: 'And that, folks, is how you dissect a universe! Until next time, keep those neurons firing!'
961
+ Another Example: 'Well, that was a delightful dive into the rabbit hole of {query}! Stay curious!'
962
+ Your remark:"""
963
+
964
+ messages = [SystemMessage(content=prompt_template)]
965
+ try:
966
+ response = await llm_direct.ainvoke(messages)
967
+ remark = response.content.strip()
968
+ # Basic filter for overly long or nonsensical remarks if needed, though prompt should guide it
969
+ if len(remark) > 150 or len(remark) < 10: # Arbitrary length check
970
+ return "And that's a wrap on that fascinating exploration! What's next? ✔️"
971
+ return f"{remark} ✔️"
972
+ except Exception as e:
973
+ print(f"Error generating witty remark: {e}")
974
+ return "Exploration complete! Well done! ✔️" # Fallback on error
975
+
976
+ # --- HELP MESSAGE FUNCTION ---
977
+ async def send_help_message():
978
+ """Sends the detailed help message to the user."""
979
+ help_text_md = """# Welcome to InsightFlow AI - Your Guide!
980
+
981
+ InsightFlow AI is designed to help you explore complex topics with depth and clarity by providing multiple AI-driven perspectives, synthesizing them into a coherent understanding, and even offering visual summaries. Think of it as your personal team of AI research assistants!
982
+
983
+ ## What Can InsightFlow AI Do?
984
+
985
+ * **Multi-Perspective Analysis:** Instead of a single answer, InsightFlow AI engages a team of specialized AI "personas" (e.g., Analytical, Philosophical, Scientific) to examine your query from different angles. This provides a richer, more nuanced understanding.
986
+ * **Insight Synthesis:** The individual perspectives are then intelligently combined into a single, comprehensive synthesized response.
987
+ * **Visualizations (Optional):**
988
+ * **DALL-E Sketches:** Get a conceptual, hand-drawn style visual note summarizing the key ideas.
989
+ * **Mermaid Diagrams:** See a concept map illustrating the relationships between your query, the personas, and the synthesized view.
990
+ * **RAG (Retrieval Augmented Generation - for supported personas):** For certain personas, the AI can search a dedicated knowledge base of relevant documents to ground its responses in specific information, enhancing accuracy and depth.
991
+ * **Flexible Interaction Modes:**
992
+ * **Full Multi-Persona Mode:** The default mode, engaging your selected team for in-depth analysis.
993
+ * **Direct Mode:** Get a quick, straightforward answer from a single LLM, bypassing the multi-persona/LangGraph system.
994
+ * **Quick Mode:** A faster multi-persona analysis using a reduced set of (currently 2) predefined personas.
995
+ * **Exportable Results:** You'll be able to export your analysis (though this feature is still under full development).
996
+
997
+ ## Why Does It Work This Way? (The Philosophy)
998
+
999
+ InsightFlow AI is built on the idea that complex topics are best understood by examining them from multiple viewpoints. Just like a team of human experts can provide more comprehensive insight than a single individual, our AI personas work together to give you a more well-rounded and deeply considered response. The structured workflow (managed by LangGraph) ensures a methodical approach to generating and synthesizing these perspectives.
1000
+
1001
+ ## Using the Settings (⚙️ Gear Icon)
1002
+
1003
+ You can customize your InsightFlow AI experience using the settings panel:
1004
+
1005
+ * **⚙️ Enable RAG Features:**
1006
+ * **Functionality:** (Currently being refined) When enabled, personas designated as "RAG-enabled" (like Analytical, Philosophical, Metaphorical on the Balanced Team) will attempt to search their specific knowledge bases for relevant context before generating their perspective. This can lead to more informed and detailed answers.
1007
+ * **Status:** Basic RAG data loading and vector store creation for personas is active. The quality and breadth of data sources are still being expanded.
1008
+
1009
+ * **🎯 Persona Team:**
1010
+ * **Functionality:** Allows you to quickly select a pre-configured team of personas. Selecting a team will override any individual persona toggles below.
1011
+ * **Current Teams:**
1012
+ * `🎨 Creative Synthesis Team`: (Metaphorical, Futuristic, Philosophical) - Generates novel ideas and artistic interpretations.
1013
+ * `📊 Data-Driven Analysis Squad`: (Analytical, Factual, Scientific) - Focuses on factual accuracy and logical deduction.
1014
+ * `⚖️ Balanced Overview Group`: (Analytical, Philosophical, Metaphorical) - **This is the default team on startup.** Provides a well-rounded perspective.
1015
+ * **Status:** Team selection is functional.
1016
+
1017
+ * **Individual Persona Toggles (e.g., Analytical, Scientific, etc.):**
1018
+ * **Functionality:** If no team is selected (or "-- Select a Team (Optional) --" is chosen), you can manually toggle individual personas ON or OFF to form a custom team for the analysis.
1019
+ * **Status:** Functional.
1020
+
1021
+ * **🚀 Direct Mode:**
1022
+ * **Functionality:** If ON, your query will be answered by a single, general-purpose LLM for a quick, direct response, bypassing the multi-persona/LangGraph system.
1023
+ * **Status:** Functional.
1024
+
1025
+ * **⚡ Quick Mode:**
1026
+ * **Functionality:** If ON, InsightFlow uses a smaller, predefined set of personas (currently Analytical and Factual) for a faster multi-perspective analysis. This overrides team/individual selections when active.
1027
+ * **Status:** Functional.
1028
+
1029
+ * **👁️ Show Individual Perspectives:**
1030
+ * **Functionality:** If ON (default), after the main synthesized response, the individual contributions from each engaged persona will also be displayed.
1031
+ * **Status:** Functional.
1032
+
1033
+ * **🎨 Show Visualizations:**
1034
+ * **Functionality:** If ON (default), InsightFlow will attempt to generate and display a DALL-E image sketch and a Mermaid concept map related to the synthesized response.
1035
+ * **Status:** Functional (DALL-E requires API key setup).
1036
+
1037
+ ## Getting Started
1038
+
1039
+ 1. **Review Settings (⚙️):** Select a Persona Team or toggle individual personas. Ensure RAG is enabled if you want to try it with supported personas.
1040
+ 2. **Ask Your Question:** Type your query into the chat and press Enter.
1041
+ 3. **Observe:** Watch the progress updates as InsightFlow AI engages the personas, synthesizes insights, and generates visualizations.
1042
+
1043
+ We hope you find InsightFlow AI insightful!
1044
+ """
1045
+ await cl.Message(content=help_text_md).send()
1046
+
1047
+ async def _initialize_embedding_model_in_session(force_openai: bool = False):
1048
+ """Helper to initialize/re-initialize embedding model based on settings."""
1049
+ use_finetuned = cl.user_session.get("use_finetuned_embedding", False)
1050
+
1051
+ # Override if force_openai is true (e.g. for initial default)
1052
+ if force_openai:
1053
+ use_finetuned = False
1054
+ cl.user_session.set("use_finetuned_embedding", False) # Ensure session reflects this override
1055
+
1056
+ current_model_details = cl.user_session.get("embedding_model_details", {})
1057
+ new_model_id = ""
1058
+ new_model_type = ""
1059
+
1060
+ if use_finetuned:
1061
+ new_model_id = FINETUNED_BALANCED_TEAM_EMBED_ID
1062
+ new_model_type = "hf"
1063
+ else:
1064
+ new_model_id = OPENAI_EMBED_MODEL_ID
1065
+ new_model_type = "openai"
1066
+
1067
+ if current_model_details.get("id") == new_model_id and current_model_details.get("type") == new_model_type and cl.user_session.get("embedding_model_instance") is not None:
1068
+ print(f"Embedding model '{new_model_id}' ({new_model_type}) already initialized and matches settings.")
1069
+ return
1070
+
1071
+ print(f"Initializing embedding model: '{new_model_id}' ({new_model_type})...")
1072
+ try:
1073
+ embedding_instance = get_embedding_model_instance(new_model_id, new_model_type)
1074
+ cl.user_session.set("embedding_model_instance", embedding_instance)
1075
+ cl.user_session.set("embedding_model_details", {"id": new_model_id, "type": new_model_type})
1076
+ print(f"Embedding model '{new_model_id}' ({new_model_type}) initialized and set in session.")
1077
+ except Exception as e:
1078
+ print(f"Error initializing embedding model '{new_model_id}': {e}")
1079
+ cl.user_session.set("embedding_model_instance", None)
1080
+ cl.user_session.set("embedding_model_details", {})
chainlit.md ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [UI]
2
+ default_theme = "dark"
3
+ custom_css = "/public/style.css"
4
+ # custom_font = "Inter" # You can also try uncommenting this if Inter is not globally available or for more explicit control
5
+
6
+ ---
7
+
8
+ ## 🚀 Welcome to InsightFlow AI - Your Brain's New Best Friend! 🧠✨
9
+
10
+ So, you're here to get smart, huh? Or at least, get smarter *faster*. You've come to the right digital watering hole!
11
+
12
+ **InsightFlow AI** isn't your grandma's encyclopedia (no offense, Grandma!). It's a high-tech, multi-perspective research sidekick. Think of it as a roundtable of brilliant, slightly quirky experts, all ready to dissect your toughest questions.
13
+
14
+ ### What's the Big Deal? (aka The Crucial Info)
15
+
16
+ * **Many Minds, One Answer**: We don't just give you *an* answer. We give you THE answer, viewed from all sorts of angles – Analytical, Scientific, Philosophical, Factual, Metaphorical, and even a peek into the Future! 🤯
17
+ * **Star-Studded Persona Lineup**: Get takes from digital versions of Sherlock Holmes (elementary!), Richard Feynman (science, simplified!), and Hannah Fry (math that makes sense!).
18
+ * **Picture This!**: Love visuals? We got 'em. Concept maps that untangle the knottiest ideas and DALL-E sketches that'll make your notes pop. 🖼️
19
+ * **RAG-narok Your Ignorance**: With Retrieval Augmented Generation, our AI personas dig into their own special stashes of knowledge for even juicier insights. (Toggle it in Settings ⚙️ - you know, if you're feeling *extra* curious).
20
+
21
+ ### How to Not Mess It Up (A Quick Guide)
22
+
23
+ 1. **Tweak Those Settings (⚙️)**: Click the gear icon. Choose your dream team of personas. Play with modes like RAG, Direct (for when you want a straight shot), or Quick (speedy insights!). It's your command center.
24
+ 2. **Ask Away!**: Drop your most mind-boggling question into the chat. Don't be shy.
25
+ 3. **Behold the Wisdom**: Marvel at the beautifully synthesized answer. Click around to see what each persona had to say.
26
+ 4. **Lost? Type `/help`**: It's like a map, but for features. No shame in using it.
27
+
28
+ ### Pro-Tip for Procrastinators (and Everyone Else)
29
+
30
+ This tool is designed to make understanding complex stuff easier and, dare we say, *fun*. So, stop staring blankly at that research paper and let InsightFlow AI give you a boost!
31
+
32
+ Now go forth and understand things! Your brain (and your grades/reports/general impressiveness) will thank you.
33
+
34
+ ---
35
+ *Built with Python, LangGraph, OpenAI, and a whole lotta digital brainpower by Suhas.*
36
+ *(P.S. If something breaks, it's probably a feature... or a bug. Let us know on GitHub!)*
data_sources/analytical/hannah_fry_mathematics_of_love_transcript.html ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html><html lang="en-US"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1"><link rel="profile" href="http://gmpg.org/xfn/11"><meta name='robots' content='index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1' /> <style>img:is([sizes="auto" i], [sizes^="auto," i]) { contain-intrinsic-size: 3000px 1500px }</style> <!-- Hubbub v.1.34.7 https://morehubbub.com/ --><meta property="og:locale" content="en_US" /><meta property="og:type" content="article" /><meta property="og:title" content="Full Transcript: Hannah Fry on The Mathematics of Love at TEDxBinghamtonUniversity" /><meta property="og:description" content="Here is the full transcript of mathematician Hannah Fry’s TEDx Talk: The Mathematics of Love at TEDxBinghamtonUniversity. Listen to the MP3 Audio here: The mathematics of love by Hannah Fry at TEDxBinghamtonUniversity TRANSCRIPT:  Thank you very" /><meta property="og:url" content="https://singjupost.com/full-transcript-hannah-fry-on-the-mathematics-of-love-at-tedxbinghamtonuniversity/" /><meta property="og:site_name" content="The Singju Post" /><meta property="og:updated_time" content="2020-06-15T11:05:44+00:00" /><meta property="article:published_time" content="2016-06-23T10:33:39+00:00" /><meta property="article:modified_time" content="2020-06-15T11:05:44+00:00" /><meta name="twitter:card" content="summary_large_image" /><meta name="twitter:title" content="Full Transcript: Hannah Fry on The Mathematics of Love at TEDxBinghamtonUniversity" /><meta name="twitter:description" content="Here is the full transcript of mathematician Hannah Fry’s TEDx Talk: The Mathematics of Love at TEDxBinghamtonUniversity. Listen to the MP3 Audio here: The mathematics of love by Hannah Fry at TEDxBinghamtonUniversity TRANSCRIPT:  Thank you very" /><meta class="flipboard-article" content="Here is the full transcript of mathematician Hannah Fry’s TEDx Talk: The Mathematics of Love at TEDxBinghamtonUniversity. Listen to the MP3 Audio here: The mathematics of love by Hannah Fry at TEDxBinghamtonUniversity TRANSCRIPT:  Thank you very" /><meta property="article:author" content="https://www.facebook.com/pangambam.s.singh" /><meta name="twitter:creator" content="@SingjuPost" /> <!-- Hubbub v.1.34.7 https://morehubbub.com/ --> <!-- This site is optimized with the Yoast SEO plugin v25.1 - https://yoast.com/wordpress/plugins/seo/ --><title>Full Transcript: Hannah Fry on The Mathematics of Love at TEDxBinghamtonUniversity &#8211; The Singju Post</title><link rel="preload" as="font" href="https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmWUlfBBc4AMP6lQ.woff2" crossorigin/><link rel="preload" as="font" href="https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmSU5fBBc4AMP6lQ.woff2" crossorigin/><link rel="stylesheet" id="siteground-optimizer-combined-css-15d98f87157715a78f2ed8405ef6506d" href="https://singjupost.com/wp-content/uploads/siteground-optimizer-assets/siteground-optimizer-combined-css-15d98f87157715a78f2ed8405ef6506d.css" media="all" /><link rel="preload" href="https://singjupost.com/wp-content/uploads/siteground-optimizer-assets/siteground-optimizer-combined-css-15d98f87157715a78f2ed8405ef6506d.css" as="style"><link rel="canonical" href="https://singjupost.com/full-transcript-hannah-fry-on-the-mathematics-of-love-at-tedxbinghamtonuniversity/" /><meta name="author" content="Pangambam S" /><meta name="twitter:label1" content="Written by" /><meta name="twitter:data1" content="Pangambam S" /><meta name="twitter:label2" content="Est. reading time" /><meta name="twitter:data2" content="14 minutes" /> <script type="application/ld+json" class="yoast-schema-graph">{"@context":"https://schema.org","@graph":[{"@type":"Article","@id":"https://singjupost.com/full-transcript-hannah-fry-on-the-mathematics-of-love-at-tedxbinghamtonuniversity/#article","isPartOf":{"@id":"https://singjupost.com/full-transcript-hannah-fry-on-the-mathematics-of-love-at-tedxbinghamtonuniversity/"},"author":{"name":"Pangambam S","@id":"https://singjupost.com/#/schema/person/748c2a7803c3c928cb80d0db8b39b11e"},"headline":"Full Transcript: Hannah Fry on The Mathematics of Love at TEDxBinghamtonUniversity","datePublished":"2016-06-23T14:33:39+00:00","dateModified":"2020-06-15T15:05:44+00:00","mainEntityOfPage":{"@id":"https://singjupost.com/full-transcript-hannah-fry-on-the-mathematics-of-love-at-tedxbinghamtonuniversity/"},"wordCount":2876,"publisher":{"@id":"https://singjupost.com/#/schema/person/748c2a7803c3c928cb80d0db8b39b11e"},"image":{"@id":"https://singjupost.com/full-transcript-hannah-fry-on-the-mathematics-of-love-at-tedxbinghamtonuniversity/#primaryimage"},"thumbnailUrl":"https://singjupost.com/wp-content/uploads/2016/06/2_The_mathematics_of_love_Hannah_Fry_TEDxBinghamtonUniversity_YouTube-300x188.jpg","articleSection":["Life &amp; Relationships"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https://singjupost.com/full-transcript-hannah-fry-on-the-mathematics-of-love-at-tedxbinghamtonuniversity/","url":"https://singjupost.com/full-transcript-hannah-fry-on-the-mathematics-of-love-at-tedxbinghamtonuniversity/","name":"Full Transcript: Hannah Fry on The Mathematics of Love at TEDxBinghamtonUniversity &#8211; The Singju Post","isPartOf":{"@id":"https://singjupost.com/#website"},"primaryImageOfPage":{"@id":"https://singjupost.com/full-transcript-hannah-fry-on-the-mathematics-of-love-at-tedxbinghamtonuniversity/#primaryimage"},"image":{"@id":"https://singjupost.com/full-transcript-hannah-fry-on-the-mathematics-of-love-at-tedxbinghamtonuniversity/#primaryimage"},"thumbnailUrl":"https://singjupost.com/wp-content/uploads/2016/06/2_The_mathematics_of_love_Hannah_Fry_TEDxBinghamtonUniversity_YouTube-300x188.jpg","datePublished":"2016-06-23T14:33:39+00:00","dateModified":"2020-06-15T15:05:44+00:00","breadcrumb":{"@id":"https://singjupost.com/full-transcript-hannah-fry-on-the-mathematics-of-love-at-tedxbinghamtonuniversity/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https://singjupost.com/full-transcript-hannah-fry-on-the-mathematics-of-love-at-tedxbinghamtonuniversity/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https://singjupost.com/full-transcript-hannah-fry-on-the-mathematics-of-love-at-tedxbinghamtonuniversity/#primaryimage","url":"https://singjupost.com/wp-content/uploads/2016/06/2_The_mathematics_of_love_Hannah_Fry_TEDxBinghamtonUniversity_YouTube.jpg","contentUrl":"https://singjupost.com/wp-content/uploads/2016/06/2_The_mathematics_of_love_Hannah_Fry_TEDxBinghamtonUniversity_YouTube.jpg","width":782,"height":490,"caption":"Hannah Fry"},{"@type":"BreadcrumbList","@id":"https://singjupost.com/full-transcript-hannah-fry-on-the-mathematics-of-love-at-tedxbinghamtonuniversity/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://singjupost.com/"},{"@type":"ListItem","position":2,"name":"Full Transcript: Hannah Fry on The Mathematics of Love at TEDxBinghamtonUniversity"}]},{"@type":"WebSite","@id":"https://singjupost.com/#website","url":"https://singjupost.com/","name":"The Singju Post","description":"Learn. Be Inspired.","publisher":{"@id":"https://singjupost.com/#/schema/person/748c2a7803c3c928cb80d0db8b39b11e"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://singjupost.com/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https://singjupost.com/#/schema/person/748c2a7803c3c928cb80d0db8b39b11e","name":"Pangambam S","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https://singjupost.com/#/schema/person/image/","url":"https://singjupost.com/wp-content/uploads/2020/03/tsplogo2_1400x1100-lanczos3.png","contentUrl":"https://singjupost.com/wp-content/uploads/2020/03/tsplogo2_1400x1100-lanczos3.png","width":1400,"height":1100,"caption":"Pangambam S"},"logo":{"@id":"https://singjupost.com/#/schema/person/image/"},"description":"I am the Editor and Manager at SingjuPost.com. If you have any questions or suggestions, please do let me know. And please do share this post if you liked it and helped you in any way.","sameAs":["https://www.facebook.com/pangambam.s.singh","https://x.com/SingjuPost","https://www.youtube.com/channel/UCNtpCiph5mXdDsHWXDpZeMA"],"url":"https://singjupost.com/author/singju/"}]}</script> <!-- / Yoast SEO plugin. --> <style id='classic-theme-styles-inline-css'> /*! This file is auto-generated */
2
+ .wp-block-button__link{color:#fff;background-color:#32373c;border-radius:9999px;box-shadow:none;text-decoration:none;padding:calc(.667em + 2px) calc(1.333em + 2px);font-size:1.125em}.wp-block-file__button{background:#32373c;color:#fff;text-decoration:none} </style> <style id='global-styles-inline-css'> :root{--wp--preset--aspect-ratio--square: 1;--wp--preset--aspect-ratio--4-3: 4/3;--wp--preset--aspect-ratio--3-4: 3/4;--wp--preset--aspect-ratio--3-2: 3/2;--wp--preset--aspect-ratio--2-3: 2/3;--wp--preset--aspect-ratio--16-9: 16/9;--wp--preset--aspect-ratio--9-16: 9/16;--wp--preset--color--black: #000000;--wp--preset--color--cyan-bluish-gray: #abb8c3;--wp--preset--color--white: #ffffff;--wp--preset--color--pale-pink: #f78da7;--wp--preset--color--vivid-red: #cf2e2e;--wp--preset--color--luminous-vivid-orange: #ff6900;--wp--preset--color--luminous-vivid-amber: #fcb900;--wp--preset--color--light-green-cyan: #7bdcb5;--wp--preset--color--vivid-green-cyan: #00d084;--wp--preset--color--pale-cyan-blue: #8ed1fc;--wp--preset--color--vivid-cyan-blue: #0693e3;--wp--preset--color--vivid-purple: #9b51e0;--wp--preset--color--neve-link-color: var(--nv-primary-accent);--wp--preset--color--neve-link-hover-color: var(--nv-secondary-accent);--wp--preset--color--nv-site-bg: var(--nv-site-bg);--wp--preset--color--nv-light-bg: var(--nv-light-bg);--wp--preset--color--nv-dark-bg: var(--nv-dark-bg);--wp--preset--color--neve-text-color: var(--nv-text-color);--wp--preset--color--nv-text-dark-bg: var(--nv-text-dark-bg);--wp--preset--color--nv-c-1: var(--nv-c-1);--wp--preset--color--nv-c-2: var(--nv-c-2);--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple: linear-gradient(135deg,rgba(6,147,227,1) 0%,rgb(155,81,224) 100%);--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan: linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%);--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange: linear-gradient(135deg,rgba(252,185,0,1) 0%,rgba(255,105,0,1) 100%);--wp--preset--gradient--luminous-vivid-orange-to-vivid-red: linear-gradient(135deg,rgba(255,105,0,1) 0%,rgb(207,46,46) 100%);--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray: linear-gradient(135deg,rgb(238,238,238) 0%,rgb(169,184,195) 100%);--wp--preset--gradient--cool-to-warm-spectrum: linear-gradient(135deg,rgb(74,234,220) 0%,rgb(151,120,209) 20%,rgb(207,42,186) 40%,rgb(238,44,130) 60%,rgb(251,105,98) 80%,rgb(254,248,76) 100%);--wp--preset--gradient--blush-light-purple: linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%);--wp--preset--gradient--blush-bordeaux: linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%);--wp--preset--gradient--luminous-dusk: linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%);--wp--preset--gradient--pale-ocean: linear-gradient(135deg,rgb(255,245,203) 0%,rgb(182,227,212) 50%,rgb(51,167,181) 100%);--wp--preset--gradient--electric-grass: linear-gradient(135deg,rgb(202,248,128) 0%,rgb(113,206,126) 100%);--wp--preset--gradient--midnight: linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%);--wp--preset--font-size--small: 13px;--wp--preset--font-size--medium: 20px;--wp--preset--font-size--large: 36px;--wp--preset--font-size--x-large: 42px;--wp--preset--spacing--20: 0.44rem;--wp--preset--spacing--30: 0.67rem;--wp--preset--spacing--40: 1rem;--wp--preset--spacing--50: 1.5rem;--wp--preset--spacing--60: 2.25rem;--wp--preset--spacing--70: 3.38rem;--wp--preset--spacing--80: 5.06rem;--wp--preset--shadow--natural: 6px 6px 9px rgba(0, 0, 0, 0.2);--wp--preset--shadow--deep: 12px 12px 50px rgba(0, 0, 0, 0.4);--wp--preset--shadow--sharp: 6px 6px 0px rgba(0, 0, 0, 0.2);--wp--preset--shadow--outlined: 6px 6px 0px -3px rgba(255, 255, 255, 1), 6px 6px rgba(0, 0, 0, 1);--wp--preset--shadow--crisp: 6px 6px 0px rgba(0, 0, 0, 1);}:where(.is-layout-flex){gap: 0.5em;}:where(.is-layout-grid){gap: 0.5em;}body .is-layout-flex{display: flex;}.is-layout-flex{flex-wrap: wrap;align-items: center;}.is-layout-flex > :is(*, div){margin: 0;}body .is-layout-grid{display: grid;}.is-layout-grid > :is(*, div){margin: 0;}:where(.wp-block-columns.is-layout-flex){gap: 2em;}:where(.wp-block-columns.is-layout-grid){gap: 2em;}:where(.wp-block-post-template.is-layout-flex){gap: 1.25em;}:where(.wp-block-post-template.is-layout-grid){gap: 1.25em;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-color{color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-color{color: var(--wp--preset--color--white) !important;}.has-pale-pink-color{color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-color{color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-color{color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-color{color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-color{color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-color{color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-color{color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-color{color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-color{color: var(--wp--preset--color--vivid-purple) !important;}.has-neve-link-color-color{color: var(--wp--preset--color--neve-link-color) !important;}.has-neve-link-hover-color-color{color: var(--wp--preset--color--neve-link-hover-color) !important;}.has-nv-site-bg-color{color: var(--wp--preset--color--nv-site-bg) !important;}.has-nv-light-bg-color{color: var(--wp--preset--color--nv-light-bg) !important;}.has-nv-dark-bg-color{color: var(--wp--preset--color--nv-dark-bg) !important;}.has-neve-text-color-color{color: var(--wp--preset--color--neve-text-color) !important;}.has-nv-text-dark-bg-color{color: var(--wp--preset--color--nv-text-dark-bg) !important;}.has-nv-c-1-color{color: var(--wp--preset--color--nv-c-1) !important;}.has-nv-c-2-color{color: var(--wp--preset--color--nv-c-2) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-background-color{background-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-pale-pink-background-color{background-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-background-color{background-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-background-color{background-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-background-color{background-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-background-color{background-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-background-color{background-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-background-color{background-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-background-color{background-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-background-color{background-color: var(--wp--preset--color--vivid-purple) !important;}.has-neve-link-color-background-color{background-color: var(--wp--preset--color--neve-link-color) !important;}.has-neve-link-hover-color-background-color{background-color: var(--wp--preset--color--neve-link-hover-color) !important;}.has-nv-site-bg-background-color{background-color: var(--wp--preset--color--nv-site-bg) !important;}.has-nv-light-bg-background-color{background-color: var(--wp--preset--color--nv-light-bg) !important;}.has-nv-dark-bg-background-color{background-color: var(--wp--preset--color--nv-dark-bg) !important;}.has-neve-text-color-background-color{background-color: var(--wp--preset--color--neve-text-color) !important;}.has-nv-text-dark-bg-background-color{background-color: var(--wp--preset--color--nv-text-dark-bg) !important;}.has-nv-c-1-background-color{background-color: var(--wp--preset--color--nv-c-1) !important;}.has-nv-c-2-background-color{background-color: var(--wp--preset--color--nv-c-2) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-border-color{border-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-pale-pink-border-color{border-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-border-color{border-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-border-color{border-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-border-color{border-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-border-color{border-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-border-color{border-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-border-color{border-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-border-color{border-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-border-color{border-color: var(--wp--preset--color--vivid-purple) !important;}.has-neve-link-color-border-color{border-color: var(--wp--preset--color--neve-link-color) !important;}.has-neve-link-hover-color-border-color{border-color: var(--wp--preset--color--neve-link-hover-color) !important;}.has-nv-site-bg-border-color{border-color: var(--wp--preset--color--nv-site-bg) !important;}.has-nv-light-bg-border-color{border-color: var(--wp--preset--color--nv-light-bg) !important;}.has-nv-dark-bg-border-color{border-color: var(--wp--preset--color--nv-dark-bg) !important;}.has-neve-text-color-border-color{border-color: var(--wp--preset--color--neve-text-color) !important;}.has-nv-text-dark-bg-border-color{border-color: var(--wp--preset--color--nv-text-dark-bg) !important;}.has-nv-c-1-border-color{border-color: var(--wp--preset--color--nv-c-1) !important;}.has-nv-c-2-border-color{border-color: var(--wp--preset--color--nv-c-2) !important;}.has-vivid-cyan-blue-to-vivid-purple-gradient-background{background: var(--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple) !important;}.has-light-green-cyan-to-vivid-green-cyan-gradient-background{background: var(--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan) !important;}.has-luminous-vivid-amber-to-luminous-vivid-orange-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange) !important;}.has-luminous-vivid-orange-to-vivid-red-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-orange-to-vivid-red) !important;}.has-very-light-gray-to-cyan-bluish-gray-gradient-background{background: var(--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray) !important;}.has-cool-to-warm-spectrum-gradient-background{background: var(--wp--preset--gradient--cool-to-warm-spectrum) !important;}.has-blush-light-purple-gradient-background{background: var(--wp--preset--gradient--blush-light-purple) !important;}.has-blush-bordeaux-gradient-background{background: var(--wp--preset--gradient--blush-bordeaux) !important;}.has-luminous-dusk-gradient-background{background: var(--wp--preset--gradient--luminous-dusk) !important;}.has-pale-ocean-gradient-background{background: var(--wp--preset--gradient--pale-ocean) !important;}.has-electric-grass-gradient-background{background: var(--wp--preset--gradient--electric-grass) !important;}.has-midnight-gradient-background{background: var(--wp--preset--gradient--midnight) !important;}.has-small-font-size{font-size: var(--wp--preset--font-size--small) !important;}.has-medium-font-size{font-size: var(--wp--preset--font-size--medium) !important;}.has-large-font-size{font-size: var(--wp--preset--font-size--large) !important;}.has-x-large-font-size{font-size: var(--wp--preset--font-size--x-large) !important;}
3
+ :where(.wp-block-post-template.is-layout-flex){gap: 1.25em;}:where(.wp-block-post-template.is-layout-grid){gap: 1.25em;}
4
+ :where(.wp-block-columns.is-layout-flex){gap: 2em;}:where(.wp-block-columns.is-layout-grid){gap: 2em;}
5
+ :root :where(.wp-block-pullquote){font-size: 1.5em;line-height: 1.6;} </style> <style id='dpsp-frontend-style-pro-inline-css'> @media screen and ( max-width : 720px ) {
6
+ .dpsp-content-wrapper.dpsp-hide-on-mobile,
7
+ .dpsp-share-text.dpsp-hide-on-mobile {
8
+ display: none;
9
+ }
10
+ .dpsp-has-spacing .dpsp-networks-btns-wrapper li {
11
+ margin:0 2% 10px 0;
12
+ }
13
+ .dpsp-network-btn.dpsp-has-label:not(.dpsp-has-count) {
14
+ max-height: 40px;
15
+ padding: 0;
16
+ justify-content: center;
17
+ }
18
+ .dpsp-content-wrapper.dpsp-size-small .dpsp-network-btn.dpsp-has-label:not(.dpsp-has-count){
19
+ max-height: 32px;
20
+ }
21
+ .dpsp-content-wrapper.dpsp-size-large .dpsp-network-btn.dpsp-has-label:not(.dpsp-has-count){
22
+ max-height: 46px;
23
+ }
24
+ } </style> <style id='neve-style-inline-css'> h1 {font-family: var(--h1fontfamily);}h2 {font-family: var(--h2fontfamily);}
25
+ .nv-meta-list li.meta:not(:last-child):after { content:"/" }.nv-meta-list .no-mobile{
26
+ display:none;
27
+ }.nv-meta-list li.last::after{
28
+ content: ""!important;
29
+ }@media (min-width: 769px) {
30
+ .nv-meta-list .no-mobile {
31
+ display: inline-block;
32
+ }
33
+ .nv-meta-list li.last:not(:last-child)::after {
34
+ content: "/" !important;
35
+ }
36
+ }
37
+ :root{ --container: 748px;--postwidth:100%; --primarybtnbg: #2b6f9e; --primarybtnhoverbg: #0366d6; --primarybtncolor: var(--nv-site-bg); --secondarybtncolor: var(--nv-primary-accent); --primarybtnhovercolor: #ffffff; --secondarybtnhovercolor: var(--nv-primary-accent);--primarybtnborderradius:3px;--secondarybtnborderradius:3px;--primarybtnborderwidth:1px;--secondarybtnborderwidth:3px;--btnpadding:8px 25px;--primarybtnpadding:calc(8px - 1px) calc(25px - 1px);--secondarybtnpadding:calc(8px - 3px) calc(25px - 3px); --bodyfontfamily: Georgia,serif; --bodyfontsize: 18px; --bodylineheight: 1.4em; --bodyletterspacing: 0px; --bodyfontweight: 400; --bodytexttransform: none; --headingsfontfamily: Georgia,serif; --h1fontfamily: Georgia,serif; --h1fontsize: 36px; --h1fontweight: 400; --h1lineheight: 1.2em; --h1letterspacing: 0px; --h1texttransform: none; --h2fontfamily: Georgia,serif; --h2fontsize: 1.3em; --h2fontweight: 500; --h2lineheight: 1.3; --h2letterspacing: 0px; --h2texttransform: none; --h3fontsize: 24px; --h3fontweight: 500; --h3lineheight: 1.4; --h3letterspacing: 0px; --h3texttransform: none; --h4fontsize: 20px; --h4fontweight: 500; --h4lineheight: 1.6; --h4letterspacing: 0px; --h4texttransform: none; --h5fontsize: 16px; --h5fontweight: 500; --h5lineheight: 1.6; --h5letterspacing: 0px; --h5texttransform: none; --h6fontsize: 14px; --h6fontweight: 500; --h6lineheight: 1.6; --h6letterspacing: 0px; --h6texttransform: none;--formfieldborderwidth:2px;--formfieldborderradius:3px; --formfieldbgcolor: var(--nv-site-bg); --formfieldbordercolor: #dddddd; --formfieldcolor: var(--nv-text-color);--formfieldpadding:10px 12px; } .has-neve-button-color-color{ color: #2b6f9e!important; } .has-neve-button-color-background-color{ background-color: #2b6f9e!important; } .single-post-container .alignfull > [class*="__inner-container"], .single-post-container .alignwide > [class*="__inner-container"]{ max-width:718px } .nv-meta-list{ --avatarsize: 20px; } .single .nv-meta-list{ --avatarsize: 20px; } .nv-post-cover{ --height: 250px;--padding:40px 15px;--justify: flex-start; --textalign: left; --valign: center; } .nv-post-cover .nv-title-meta-wrap, .nv-page-title-wrap, .entry-header{ --textalign: left; } .nv-is-boxed.nv-title-meta-wrap{ --padding:40px 15px; --bgcolor: var(--nv-dark-bg); } .nv-overlay{ --opacity: 50; --blendmode: normal; } .nv-is-boxed.nv-comments-wrap{ --padding:20px; } .nv-is-boxed.comment-respond{ --padding:20px; } .single:not(.single-product), .page{ --c-vspace:0 0 0 0;; } .global-styled{ --bgcolor: var(--nv-site-bg); } .header-top{ --rowbcolor: var(--nv-site-bg); --color: var(--nv-text-color); --bgcolor: var(--nv-site-bg); } .header-main{ --rowbcolor: var(--nv-light-bg); --color: var(--nv-text-color); --bgcolor: #ffffff; } .header-bottom{ --rowbcolor: var(--nv-light-bg); --color: var(--nv-text-color); --bgcolor: #ffffff; } .header-menu-sidebar-bg{ --justify: flex-start; --textalign: left;--flexg: 1;--wrapdropdownwidth: auto; --color: var(--nv-text-color); --bgcolor: #ffffff; } .header-menu-sidebar{ width: 360px; } .builder-item--logo{ --maxwidth: 120px; --fs: 24px;--padding:10px 0;--margin:0; --textalign: right;--justify: flex-end; } .builder-item--nav-icon,.header-menu-sidebar .close-sidebar-panel .navbar-toggle{ --borderradius:0; } .builder-item--nav-icon{ --label-margin:0 5px 0 0;;--padding:10px 15px;--margin:0; } .builder-item--primary-menu{ --color: #b1111e; --hovercolor: var(--nv-secondary-accent); --hovertextcolor: var(--nv-text-color); --activecolor: #e4252b; --spacing: 20px; --height: 25px;--padding:0;--margin:0; --fontsize: 1em; --lineheight: 1.6; --letterspacing: 0px; --fontweight: 500; --texttransform: none; --iconsize: 1em; } .hfg-is-group.has-primary-menu .inherit-ff{ --inheritedfw: 500; } .builder-item--header_search_responsive{ --iconsize: 15px; --formfieldfontsize: 14px;--formfieldborderwidth:2px;--formfieldborderradius:2px; --height: 40px;--padding:0 10px;--margin:0; } .footer-top-inner .row{ grid-template-columns:1fr 1fr 1fr; --valign: flex-start; } .footer-top{ --rowbcolor: var(--nv-light-bg); --color: var(--nv-text-color); --bgcolor: #ffffff; } .footer-main-inner .row{ grid-template-columns:repeat(4, 1fr); --valign: flex-start; } .footer-main{ --rowbcolor: var(--nv-light-bg); --color: var(--nv-text-color); --bgcolor: var(--nv-site-bg); } .footer-bottom-inner .row{ grid-template-columns:1fr; --valign: flex-start; } .footer-bottom{ --rowbcolor: var(--nv-light-bg); --color: var(--nv-text-dark-bg); --bgcolor: #24292e; } .builder-item--footer-one-widgets{ --padding:0;--margin:0; --textalign: left;--justify: flex-start; } .builder-item--footer-two-widgets{ --padding:0;--margin:0; --textalign: left;--justify: flex-start; } .builder-item--footer-four-widgets{ --padding:0;--margin:0; --textalign: left;--justify: flex-start; } .builder-item--footer-menu{ --hovercolor: var(--nv-primary-accent); --spacing: 20px; --height: 25px;--padding:0;--margin:0; --fontsize: 1em; --lineheight: 1.6; --letterspacing: 0px; --fontweight: 500; --texttransform: none; --iconsize: 1em; --textalign: left;--justify: flex-start; } @media(min-width: 576px){ :root{ --container: 992px;--postwidth:100%;--btnpadding:9px 30px;--primarybtnpadding:calc(9px - 1px) calc(30px - 1px);--secondarybtnpadding:calc(9px - 3px) calc(30px - 3px); --bodyfontsize: 18px; --bodylineheight: 1.4em; --bodyletterspacing: 0px; --h1fontsize: 38px; --h1lineheight: 1.2em; --h1letterspacing: 0px; --h2fontsize: 1.3em; --h2lineheight: 1.2; --h2letterspacing: 0px; --h3fontsize: 26px; --h3lineheight: 1.4; --h3letterspacing: 0px; --h4fontsize: 22px; --h4lineheight: 1.5; --h4letterspacing: 0px; --h5fontsize: 18px; --h5lineheight: 1.6; --h5letterspacing: 0px; --h6fontsize: 14px; --h6lineheight: 1.6; --h6letterspacing: 0px; } .single-post-container .alignfull > [class*="__inner-container"], .single-post-container .alignwide > [class*="__inner-container"]{ max-width:962px } .nv-meta-list{ --avatarsize: 20px; } .single .nv-meta-list{ --avatarsize: 20px; } .nv-post-cover{ --height: 320px;--padding:60px 30px;--justify: flex-start; --textalign: left; --valign: center; } .nv-post-cover .nv-title-meta-wrap, .nv-page-title-wrap, .entry-header{ --textalign: left; } .nv-is-boxed.nv-title-meta-wrap{ --padding:60px 30px; } .nv-is-boxed.nv-comments-wrap{ --padding:30px; } .nv-is-boxed.comment-respond{ --padding:30px; } .single:not(.single-product), .page{ --c-vspace:0 0 0 0;; } .header-menu-sidebar-bg{ --justify: flex-start; --textalign: left;--flexg: 1;--wrapdropdownwidth: auto; } .header-menu-sidebar{ width: 360px; } .builder-item--logo{ --maxwidth: 120px; --fs: 24px;--padding:10px 0;--margin:0; --textalign: right;--justify: flex-end; } .builder-item--nav-icon{ --label-margin:0 5px 0 0;;--padding:10px 15px;--margin:0; } .builder-item--primary-menu{ --spacing: 20px; --height: 25px;--padding:0;--margin:0; --fontsize: 1em; --lineheight: 1.6; --letterspacing: 0px; --iconsize: 1em; } .builder-item--header_search_responsive{ --formfieldfontsize: 14px;--formfieldborderwidth:2px;--formfieldborderradius:2px; --height: 40px;--padding:0 10px;--margin:0; } .builder-item--footer-one-widgets{ --padding:0;--margin:0; --textalign: left;--justify: flex-start; } .builder-item--footer-two-widgets{ --padding:0;--margin:0; --textalign: left;--justify: flex-start; } .builder-item--footer-four-widgets{ --padding:0;--margin:0; --textalign: left;--justify: flex-start; } .builder-item--footer-menu{ --spacing: 20px; --height: 25px;--padding:0;--margin:0; --fontsize: 1em; --lineheight: 1.6; --letterspacing: 0px; --iconsize: 1em; --textalign: left;--justify: flex-start; } }@media(min-width: 960px){ :root{ --container: 1170px;--postwidth:100%;--btnpadding:10px 30px;--primarybtnpadding:calc(10px - 1px) calc(30px - 1px);--secondarybtnpadding:calc(10px - 3px) calc(30px - 3px); --bodyfontsize: 20px; --bodylineheight: 1.4em; --bodyletterspacing: 0px; --h1fontsize: 40px; --h1lineheight: 1.1em; --h1letterspacing: 0px; --h2fontsize: 1.75em; --h2lineheight: 1.2; --h2letterspacing: 0px; --h3fontsize: 28px; --h3lineheight: 1.4; --h3letterspacing: 0px; --h4fontsize: 24px; --h4lineheight: 1.5; --h4letterspacing: 0px; --h5fontsize: 20px; --h5lineheight: 1.6; --h5letterspacing: 0px; --h6fontsize: 16px; --h6lineheight: 1.6; --h6letterspacing: 0px; } body:not(.single):not(.archive):not(.blog):not(.search):not(.error404) .neve-main > .container .col, body.post-type-archive-course .neve-main > .container .col, body.post-type-archive-llms_membership .neve-main > .container .col{ max-width: 62%; } body:not(.single):not(.archive):not(.blog):not(.search):not(.error404) .nv-sidebar-wrap, body.post-type-archive-course .nv-sidebar-wrap, body.post-type-archive-llms_membership .nv-sidebar-wrap{ max-width: 38%; } .neve-main > .archive-container .nv-index-posts.col{ max-width: 62%; } .neve-main > .archive-container .nv-sidebar-wrap{ max-width: 38%; } .neve-main > .single-post-container .nv-single-post-wrap.col{ max-width: 62%; } .single-post-container .alignfull > [class*="__inner-container"], .single-post-container .alignwide > [class*="__inner-container"]{ max-width:695px } .container-fluid.single-post-container .alignfull > [class*="__inner-container"], .container-fluid.single-post-container .alignwide > [class*="__inner-container"]{ max-width:calc(62% + 15px) } .neve-main > .single-post-container .nv-sidebar-wrap{ max-width: 38%; } .nv-meta-list{ --avatarsize: 20px; } .single .nv-meta-list{ --avatarsize: 20px; } .nv-post-cover{ --height: 400px;--padding:60px 40px;--justify: flex-start; --textalign: left; --valign: center; } .nv-post-cover .nv-title-meta-wrap, .nv-page-title-wrap, .entry-header{ --textalign: left; } .nv-is-boxed.nv-title-meta-wrap{ --padding:60px 40px; } .nv-is-boxed.nv-comments-wrap{ --padding:40px; } .nv-is-boxed.comment-respond{ --padding:40px; } .single:not(.single-product), .page{ --c-vspace:0 0 0 0;; } .header-menu-sidebar-bg{ --justify: flex-start; --textalign: left;--flexg: 1;--wrapdropdownwidth: auto; } .header-menu-sidebar{ width: 360px; } .builder-item--logo{ --maxwidth: 120px; --fs: 24px;--padding:10px 0;--margin:0; --textalign: right;--justify: flex-end; } .builder-item--nav-icon{ --label-margin:0 5px 0 0;;--padding:10px 15px;--margin:0; } .builder-item--primary-menu{ --spacing: 20px; --height: 25px;--padding:0;--margin:0; --fontsize: 1em; --lineheight: 1.6; --letterspacing: 0px; --iconsize: 1em; } .builder-item--header_search_responsive{ --formfieldfontsize: 14px;--formfieldborderwidth:2px;--formfieldborderradius:2px; --height: 40px;--padding:0 10px;--margin:0; } .builder-item--footer-one-widgets{ --padding:0;--margin:0; --textalign: left;--justify: flex-start; } .builder-item--footer-two-widgets{ --padding:0;--margin:0; --textalign: left;--justify: flex-start; } .builder-item--footer-four-widgets{ --padding:0;--margin:0; --textalign: left;--justify: flex-start; } .builder-item--footer-menu{ --spacing: 20px; --height: 25px;--padding:0;--margin:0; --fontsize: 1em; --lineheight: 1.6; --letterspacing: 0px; --iconsize: 1em; --textalign: left;--justify: flex-start; } }:root{--nv-primary-accent:#d00545;--nv-secondary-accent:#2f5aae;--nv-site-bg:#ffffff;--nv-light-bg:#f4f5f7;--nv-dark-bg:#121212;--nv-text-color:#161616;--nv-text-dark-bg:#ffffff;--nv-c-1:#9463ae;--nv-c-2:#be574b;--nv-fallback-ff:Times New Roman, Times, serif;} </style><link rel="https://api.w.org/" href="https://singjupost.com/wp-json/" /><link rel="alternate" title="JSON" type="application/json" href="https://singjupost.com/wp-json/wp/v2/posts/3638" /><link rel="EditURI" type="application/rsd+xml" title="RSD" href="https://singjupost.com/xmlrpc.php?rsd" /><link rel='shortlink' href='https://singjupost.com/?p=3638' /><link rel="alternate" title="oEmbed (JSON)" type="application/json+oembed" href="https://singjupost.com/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fsingjupost.com%2Ffull-transcript-hannah-fry-on-the-mathematics-of-love-at-tedxbinghamtonuniversity%2F" /><link rel="alternate" title="oEmbed (XML)" type="text/xml+oembed" href="https://singjupost.com/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fsingjupost.com%2Ffull-transcript-hannah-fry-on-the-mathematics-of-love-at-tedxbinghamtonuniversity%2F&#038;format=xml" /><meta name="hubbub-info" description="Hubbub 1.34.7"><script type="text/javascript" async="" src=https://hb-tutorialrepublic.s3.us-east-2.amazonaws.com/singjupost.com/asc_prebid.js></script><link rel="apple-touch-icon" sizes="180x180" href="/wp-content/uploads/fbrfg/apple-touch-icon.png?v=9BPjgNmd9O"><link rel="icon" type="image/png" sizes="32x32" href="/wp-content/uploads/fbrfg/favicon-32x32.png?v=9BPjgNmd9O"><link rel="icon" type="image/png" sizes="16x16" href="/wp-content/uploads/fbrfg/favicon-16x16.png?v=9BPjgNmd9O"><link rel="manifest" href="/wp-content/uploads/fbrfg/site.webmanifest?v=9BPjgNmd9O"><link rel="shortcut icon" href="/wp-content/uploads/fbrfg/favicon.ico?v=9BPjgNmd9O"><meta name="msapplication-TileColor" content="#00aba9"><meta name="msapplication-config" content="/wp-content/uploads/fbrfg/browserconfig.xml?v=9BPjgNmd9O"><meta name="theme-color" content="#ffffff"> <style type="text/css"> #post-pagination a {
38
+ margin: 0 3px;
39
+ } </style><link rel="icon" href="https://singjupost.com/wp-content/uploads/2019/05/cropped-tsplogo2_512x512-32x32.jpg" sizes="32x32" /><link rel="icon" href="https://singjupost.com/wp-content/uploads/2019/05/cropped-tsplogo2_512x512-192x192.jpg" sizes="192x192" /><link rel="apple-touch-icon" href="https://singjupost.com/wp-content/uploads/2019/05/cropped-tsplogo2_512x512-180x180.jpg" /><meta name="msapplication-TileImage" content="https://singjupost.com/wp-content/uploads/2019/05/cropped-tsplogo2_512x512-270x270.jpg" /> <style id="wp-custom-css"> #myBtn {
40
+ display: none; /* Hidden by default */
41
+ position: fixed; /* Fixed/sticky position */
42
+ bottom: 20px; /* Place the button at the bottom of the page */
43
+ right: 10px; /* Place the button 30px from the right */
44
+ z-index: 99; /* Make sure it does not overlap */
45
+ border: none; /* Remove borders */
46
+ outline: none; /* Remove outline */
47
+ background-color: purple; /* Set a background color */
48
+ color: white; /* Text color */
49
+ cursor: pointer; /* Add a mouse pointer on hover */
50
+ padding: 5px; /* Some padding */
51
+ border-radius: 8px; /* Rounded corners */
52
+ }
53
+ #myBtn:hover {
54
+ background-color: #555; /* Add a dark-grey background on hover */
55
+ } </style></head><body class="wp-singular post-template-default single single-post postid-3638 single-format-standard wp-theme-neve wp-child-theme-neve-child-master nv-blog-default nv-sidebar-right menu_sidebar_slide_left plp-on" id="neve_body" > <!-- Google tag (gtag.js) --> <script async src="https://www.googletagmanager.com/gtag/js?id=G-SJY565FNHK"></script> <script> window.dataLayer = window.dataLayer || [];
56
+ function gtag(){dataLayer.push(arguments);}
57
+ gtag('js', new Date());
58
+ gtag('config', 'G-SJY565FNHK'); </script><div class="wrapper"><header class="header" > <a class="neve-skip-link show-on-focus" href="#content" > Skip to content </a><div id="header-grid" class="hfg_header site-header"><nav class="header--row header-main hide-on-mobile hide-on-tablet layout-full-contained nv-navbar header--row"
59
+ data-row-id="main" data-show-on="desktop"><div
60
+ class="header--row-inner header-main-inner"><div class="container"><div
61
+ class="row row--wrapper"
62
+ data-section="hfg_header_layout_main" ><div class="hfg-slot left"><div class="builder-item desktop-right"><div class="item--inner builder-item--logo"
63
+ data-section="title_tagline"
64
+ data-item-id="logo"><div class="site-logo"> <a class="brand" href="https://singjupost.com/" title="&larr; The Singju Post"
65
+ aria-label="The Singju Post Learn. Be Inspired." rel="home"><div class="nv-title-tagline-wrap"><p class="site-title">The Singju Post</p><small>Learn. Be Inspired.</small></div></a></div></div></div></div><div class="hfg-slot right"><div class="builder-item has-nav hfg-is-group has-primary-menu"><div class="item--inner builder-item--primary-menu has_menu"
66
+ data-section="header_menu_primary"
67
+ data-item-id="primary-menu"><div class="nv-nav-wrap"><div role="navigation" class="nav-menu-primary"
68
+ aria-label="Primary Menu"><ul id="nv-primary-navigation-main" class="primary-menu-ul nav-ul menu-desktop"><li id="menu-item-41919" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-41919"><div class="wrap"><a href="https://singjupost.com/education/">Education</a></div></li><li id="menu-item-42971" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-42971"><div class="wrap"><a href="https://singjupost.com/technology/">Technology</a></div></li><li id="menu-item-43018" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-43018"><div class="wrap"><a href="https://singjupost.com/family-parenting/">Family &amp; Parenting</a></div></li><li id="menu-item-45609" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-45609"><div class="wrap"><a href="https://singjupost.com/health/">Health &amp; Wellness</a></div></li><li id="menu-item-48921" class="menu-item menu-item-type-taxonomy menu-item-object-category current-post-ancestor current-menu-parent current-post-parent menu-item-48921"><div class="wrap"><a href="https://singjupost.com/life-relationships/">Life &amp; Relationships</a></div></li></ul></div></div></div><div class="item--inner builder-item--header_search_responsive"
69
+ data-section="header_search_responsive"
70
+ data-item-id="header_search_responsive"><div class="nv-search-icon-component" ><div class="menu-item-nav-search canvas"> <a aria-label="Search" href="#" class="nv-icon nv-search" > <svg width="15" height="15" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1216 832q0-185-131.5-316.5t-316.5-131.5-316.5 131.5-131.5 316.5 131.5 316.5 316.5 131.5 316.5-131.5 131.5-316.5zm512 832q0 52-38 90t-90 38q-54 0-90-38l-343-342q-179 124-399 124-143 0-273.5-55.5t-225-150-150-225-55.5-273.5 55.5-273.5 150-225 225-150 273.5-55.5 273.5 55.5 225 150 150 225 55.5 273.5q0 220-124 399l343 343q37 37 37 90z" /></svg> </a><div class="nv-nav-search" aria-label="search"><div class="form-wrap container responsive-search"><form role="search"
71
+ method="get"
72
+ class="search-form"
73
+ action="https://singjupost.com/"> <label> <span class="screen-reader-text">Search for...</span> </label> <input type="search"
74
+ class="search-field"
75
+ aria-label="Search"
76
+ placeholder="Search for..."
77
+ value=""
78
+ name="s"/> <button type="submit"
79
+ class="search-submit nv-submit"
80
+ aria-label="Search"> <span class="nv-search-icon-wrap"> <span class="nv-icon nv-search" > <svg width="15" height="15" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1216 832q0-185-131.5-316.5t-316.5-131.5-316.5 131.5-131.5 316.5 131.5 316.5 316.5 131.5 316.5-131.5 131.5-316.5zm512 832q0 52-38 90t-90 38q-54 0-90-38l-343-342q-179 124-399 124-143 0-273.5-55.5t-225-150-150-225-55.5-273.5 55.5-273.5 150-225 225-150 273.5-55.5 273.5 55.5 225 150 150 225 55.5 273.5q0 220-124 399l343 343q37 37 37 90z" /></svg> </span> </span> </button></form></div><div class="close-container container responsive-search"> <button class="close-responsive-search" aria-label="Close"
81
+ > <svg width="50" height="50" viewBox="0 0 20 20" fill="#555555"><path d="M14.95 6.46L11.41 10l3.54 3.54l-1.41 1.41L10 11.42l-3.53 3.53l-1.42-1.42L8.58 10L5.05 6.47l1.42-1.42L10 8.58l3.54-3.53z"/></svg> </button></div></div></div></div></div></div></div></div></div></div></nav><nav class="header--row header-main hide-on-desktop layout-full-contained nv-navbar header--row"
82
+ data-row-id="main" data-show-on="mobile"><div
83
+ class="header--row-inner header-main-inner"><div class="container"><div
84
+ class="row row--wrapper"
85
+ data-section="hfg_header_layout_main" ><div class="hfg-slot left"><div class="builder-item mobile-right tablet-right"><div class="item--inner builder-item--logo"
86
+ data-section="title_tagline"
87
+ data-item-id="logo"><div class="site-logo"> <a class="brand" href="https://singjupost.com/" title="&larr; The Singju Post"
88
+ aria-label="The Singju Post Learn. Be Inspired." rel="home"><div class="nv-title-tagline-wrap"><p class="site-title">The Singju Post</p><small>Learn. Be Inspired.</small></div></a></div></div></div></div><div class="hfg-slot right"><div class="builder-item tablet-left mobile-left hfg-is-group"><div class="item--inner builder-item--nav-icon"
89
+ data-section="header_menu_icon"
90
+ data-item-id="nav-icon"><div class="menu-mobile-toggle item-button navbar-toggle-wrapper"> <button type="button" class=" navbar-toggle"
91
+ value="Navigation Menu"
92
+ aria-label="Navigation Menu "
93
+ aria-expanded="false" onclick="if('undefined' !== typeof toggleAriaClick ) { toggleAriaClick() }"> <span class="bars"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </span> <span class="screen-reader-text">Navigation Menu</span> </button></div> <!--.navbar-toggle-wrapper--></div><div class="item--inner builder-item--header_search_responsive"
94
+ data-section="header_search_responsive"
95
+ data-item-id="header_search_responsive"><div class="nv-search-icon-component" ><div class="menu-item-nav-search canvas"> <a aria-label="Search" href="#" class="nv-icon nv-search" > <svg width="15" height="15" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1216 832q0-185-131.5-316.5t-316.5-131.5-316.5 131.5-131.5 316.5 131.5 316.5 316.5 131.5 316.5-131.5 131.5-316.5zm512 832q0 52-38 90t-90 38q-54 0-90-38l-343-342q-179 124-399 124-143 0-273.5-55.5t-225-150-150-225-55.5-273.5 55.5-273.5 150-225 225-150 273.5-55.5 273.5 55.5 225 150 150 225 55.5 273.5q0 220-124 399l343 343q37 37 37 90z" /></svg> </a><div class="nv-nav-search" aria-label="search"><div class="form-wrap container responsive-search"><form role="search"
96
+ method="get"
97
+ class="search-form"
98
+ action="https://singjupost.com/"> <label> <span class="screen-reader-text">Search for...</span> </label> <input type="search"
99
+ class="search-field"
100
+ aria-label="Search"
101
+ placeholder="Search for..."
102
+ value=""
103
+ name="s"/> <button type="submit"
104
+ class="search-submit nv-submit"
105
+ aria-label="Search"> <span class="nv-search-icon-wrap"> <span class="nv-icon nv-search" > <svg width="15" height="15" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1216 832q0-185-131.5-316.5t-316.5-131.5-316.5 131.5-131.5 316.5 131.5 316.5 316.5 131.5 316.5-131.5 131.5-316.5zm512 832q0 52-38 90t-90 38q-54 0-90-38l-343-342q-179 124-399 124-143 0-273.5-55.5t-225-150-150-225-55.5-273.5 55.5-273.5 150-225 225-150 273.5-55.5 273.5 55.5 225 150 150 225 55.5 273.5q0 220-124 399l343 343q37 37 37 90z" /></svg> </span> </span> </button></form></div><div class="close-container container responsive-search"> <button class="close-responsive-search" aria-label="Close"
106
+ > <svg width="50" height="50" viewBox="0 0 20 20" fill="#555555"><path d="M14.95 6.46L11.41 10l3.54 3.54l-1.41 1.41L10 11.42l-3.53 3.53l-1.42-1.42L8.58 10L5.05 6.47l1.42-1.42L10 8.58l3.54-3.53z"/></svg> </button></div></div></div></div></div></div></div></div></div></div></nav><div
107
+ id="header-menu-sidebar" class="header-menu-sidebar tcb menu-sidebar-panel slide_left hfg-pe"
108
+ data-row-id="sidebar"><div id="header-menu-sidebar-bg" class="header-menu-sidebar-bg"><div class="close-sidebar-panel navbar-toggle-wrapper"> <button type="button" class="hamburger is-active navbar-toggle active" value="Navigation Menu"
109
+ aria-label="Navigation Menu "
110
+ aria-expanded="false" onclick="if('undefined' !== typeof toggleAriaClick ) { toggleAriaClick() }"> <span class="bars"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </span> <span class="screen-reader-text"> Navigation Menu </span> </button></div><div id="header-menu-sidebar-inner" class="header-menu-sidebar-inner tcb "><div class="builder-item has-nav"><div class="item--inner builder-item--primary-menu has_menu"
111
+ data-section="header_menu_primary"
112
+ data-item-id="primary-menu"><div class="nv-nav-wrap"><div role="navigation" class="nav-menu-primary"
113
+ aria-label="Primary Menu"><ul id="nv-primary-navigation-sidebar" class="primary-menu-ul nav-ul menu-mobile"><li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-41919"><div class="wrap"><a href="https://singjupost.com/education/">Education</a></div></li><li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-42971"><div class="wrap"><a href="https://singjupost.com/technology/">Technology</a></div></li><li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-43018"><div class="wrap"><a href="https://singjupost.com/family-parenting/">Family &amp; Parenting</a></div></li><li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-45609"><div class="wrap"><a href="https://singjupost.com/health/">Health &amp; Wellness</a></div></li><li class="menu-item menu-item-type-taxonomy menu-item-object-category current-post-ancestor current-menu-parent current-post-parent menu-item-48921"><div class="wrap"><a href="https://singjupost.com/life-relationships/">Life &amp; Relationships</a></div></li></ul></div></div></div></div></div></div></div><div class="header-menu-sidebar-overlay hfg-ov hfg-pe" onclick="if('undefined' !== typeof toggleAriaClick ) { toggleAriaClick() }"></div></div></header> <style>.is-menu-sidebar .header-menu-sidebar { visibility: visible; }.is-menu-sidebar.menu_sidebar_slide_left .header-menu-sidebar { transform: translate3d(0, 0, 0); left: 0; }.is-menu-sidebar.menu_sidebar_slide_right .header-menu-sidebar { transform: translate3d(0, 0, 0); right: 0; }.is-menu-sidebar.menu_sidebar_pull_right .header-menu-sidebar, .is-menu-sidebar.menu_sidebar_pull_left .header-menu-sidebar { transform: translateX(0); }.is-menu-sidebar.menu_sidebar_dropdown .header-menu-sidebar { height: auto; }.is-menu-sidebar.menu_sidebar_dropdown .header-menu-sidebar-inner { max-height: 400px; padding: 20px 0; }.is-menu-sidebar.menu_sidebar_full_canvas .header-menu-sidebar { opacity: 1; }.header-menu-sidebar .menu-item-nav-search:not(.floating) { pointer-events: none; }.header-menu-sidebar .menu-item-nav-search .is-menu-sidebar { pointer-events: unset; }.nav-ul li:focus-within .wrap.active + .sub-menu { opacity: 1; visibility: visible; }.nav-ul li.neve-mega-menu:focus-within .wrap.active + .sub-menu { display: grid; }.nav-ul li > .wrap { display: flex; align-items: center; position: relative; padding: 0 4px; }.nav-ul:not(.menu-mobile):not(.neve-mega-menu) > li > .wrap > a { padding-top: 1px }</style><main id="content" class="neve-main"><div class="container single-post-container"><div class="row"><article id="post-3638"
114
+ class="nv-single-post-wrap col post-3638 post type-post status-publish format-standard hentry category-life-relationships grow-content-body"><div class="entry-header" ><div class="nv-title-meta-wrap"><small class="nv--yoast-breadcrumb neve-breadcrumbs-wrapper"><span><span><a href="https://singjupost.com/">Home</a></span> » <span class="breadcrumb_last" aria-current="page">Full Transcript: Hannah Fry on The Mathematics of Love at TEDxBinghamtonUniversity</span></span></small><h1 class="title entry-title">Full Transcript: Hannah Fry on The Mathematics of Love at TEDxBinghamtonUniversity</h1><ul class="nv-meta-list"><li class="meta date posted-on "><time class="entry-date published" datetime="2016-06-23T10:33:39-04:00" content="2016-06-23">June 23, 2016 10:33 am</time><time class="updated" datetime="2020-06-15T11:05:44-04:00">June 15, 2020 11:05 am</time></li><li class="meta author vcard "><span class="author-name fn">by <a href="https://singjupost.com/author/singju/" title="Posts by Pangambam S" rel="author">Pangambam S</a></span></li><li class="meta category last"><a href="https://singjupost.com/life-relationships/" rel="category tag">Life &amp; Relationships</a></li></ul></div></div><div class="nv-content-wrap entry-content"><div id="dpsp-content-top" class="dpsp-content-wrapper dpsp-shape-rounded dpsp-size-medium dpsp-has-spacing dpsp-has-buttons-count dpsp-show-on-mobile dpsp-button-style-2" style="min-height:40px;position:relative"><ul class="dpsp-networks-btns-wrapper dpsp-networks-btns-share dpsp-networks-btns-content dpsp-column-4 dpsp-has-button-icon-animation" style="padding:0;margin:0;list-style-type:none"><li class="dpsp-network-list-item dpsp-network-list-item-facebook" style="float:left"> <a rel="nofollow noopener" href="https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fsingjupost.com%2Ffull-transcript-hannah-fry-on-the-mathematics-of-love-at-tedxbinghamtonuniversity%2F&#038;t=Full%20Transcript%3A%20Hannah%20Fry%20on%20The%20Mathematics%20of%20Love%20at%20TEDxBinghamtonUniversity" class="dpsp-network-btn dpsp-facebook dpsp-first dpsp-has-label dpsp-has-label-mobile" target="_blank" aria-label="Share on Facebook" title="Share on Facebook" style="font-size:14px;padding:0rem;max-height:40px" > <span class="dpsp-network-icon "> <span class="dpsp-network-icon-inner" ><svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 18 32"><path d="M17.12 0.224v4.704h-2.784q-1.536 0-2.080 0.64t-0.544 1.92v3.392h5.248l-0.704 5.28h-4.544v13.568h-5.472v-13.568h-4.544v-5.28h4.544v-3.904q0-3.328 1.856-5.152t4.96-1.824q2.624 0 4.064 0.224z"></path></svg></span> </span> <span class="dpsp-network-label dpsp-network-hide-label-mobile">Share</span></a></li><li class="dpsp-network-list-item dpsp-network-list-item-x" style="float:left"> <a rel="nofollow noopener" href="https://x.com/intent/tweet?text=Full%20Transcript%3A%20Hannah%20Fry%20on%20The%20Mathematics%20of%20Love%20at%20TEDxBinghamtonUniversity&#038;url=https%3A%2F%2Fsingjupost.com%2Ffull-transcript-hannah-fry-on-the-mathematics-of-love-at-tedxbinghamtonuniversity%2F&#038;via=singjupost" class="dpsp-network-btn dpsp-x dpsp-has-label dpsp-has-label-mobile" target="_blank" aria-label="Share on X" title="Share on X" style="font-size:14px;padding:0rem;max-height:40px" > <span class="dpsp-network-icon "> <span class="dpsp-network-icon-inner" ><svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 30"><path d="M30.3 29.7L18.5 12.4l0 0L29.2 0h-3.6l-8.7 10.1L10 0H0.6l11.1 16.1l0 0L0 29.7h3.6l9.7-11.2L21 29.7H30.3z M8.6 2.7 L25.2 27h-2.8L5.7 2.7H8.6z"></path></svg></span> </span> <span class="dpsp-network-label dpsp-network-hide-label-mobile">Tweet</span></a></li><li class="dpsp-network-list-item dpsp-network-list-item-pinterest" style="float:left"> <button rel="nofollow noopener" data-href="#" class="dpsp-network-btn dpsp-pinterest dpsp-has-label dpsp-has-label-mobile" target="_blank" aria-label="Save to Pinterest" title="Save to Pinterest" style="font-size:14px;padding:0rem;max-height:40px" > <span class="dpsp-network-icon "> <span class="dpsp-network-icon-inner" ><svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 23 32"><path d="M0 10.656q0-1.92 0.672-3.616t1.856-2.976 2.72-2.208 3.296-1.408 3.616-0.448q2.816 0 5.248 1.184t3.936 3.456 1.504 5.12q0 1.728-0.32 3.36t-1.088 3.168-1.792 2.656-2.56 1.856-3.392 0.672q-1.216 0-2.4-0.576t-1.728-1.568q-0.16 0.704-0.48 2.016t-0.448 1.696-0.352 1.28-0.48 1.248-0.544 1.12-0.832 1.408-1.12 1.536l-0.224 0.096-0.16-0.192q-0.288-2.816-0.288-3.36 0-1.632 0.384-3.68t1.184-5.152 0.928-3.616q-0.576-1.152-0.576-3.008 0-1.504 0.928-2.784t2.368-1.312q1.088 0 1.696 0.736t0.608 1.824q0 1.184-0.768 3.392t-0.8 3.36q0 1.12 0.8 1.856t1.952 0.736q0.992 0 1.824-0.448t1.408-1.216 0.992-1.696 0.672-1.952 0.352-1.984 0.128-1.792q0-3.072-1.952-4.8t-5.12-1.728q-3.552 0-5.952 2.304t-2.4 5.856q0 0.8 0.224 1.536t0.48 1.152 0.48 0.832 0.224 0.544q0 0.48-0.256 1.28t-0.672 0.8q-0.032 0-0.288-0.032-0.928-0.288-1.632-0.992t-1.088-1.696-0.576-1.92-0.192-1.92z"></path></svg></span> </span> <span class="dpsp-network-label dpsp-network-hide-label-mobile">Pinterest</span></button></li><li class="dpsp-network-list-item dpsp-network-list-item-email" style="float:left"> <a rel="nofollow noopener" href="mailto:?subject=Full%20Transcript%3A%20Hannah%20Fry%20on%20The%20Mathematics%20of%20Love%20at%20TEDxBinghamtonUniversity&#038;body=https%3A%2F%2Fsingjupost.com%2Ffull-transcript-hannah-fry-on-the-mathematics-of-love-at-tedxbinghamtonuniversity%2F" class="dpsp-network-btn dpsp-email dpsp-last dpsp-has-label dpsp-has-label-mobile" target="_blank" aria-label="Send over email" title="Send over email" style="font-size:14px;padding:0rem;max-height:40px" > <span class="dpsp-network-icon "> <span class="dpsp-network-icon-inner" ><svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 28 32"><path d="M18.56 17.408l8.256 8.544h-25.248l8.288-8.448 4.32 4.064zM2.016 6.048h24.32l-12.16 11.584zM20.128 15.936l8.224-7.744v16.256zM0 24.448v-16.256l8.288 7.776z"></path></svg></span> </span> <span class="dpsp-network-label dpsp-network-hide-label-mobile">Email</span></a></li></ul></div><figure id="attachment_39220" aria-describedby="caption-attachment-39220" style="width: 1250px" class="wp-caption aligncenter"><img fetchpriority="high" decoding="async" class="wp-image-39220 lazyload" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-src="https://singjupost.com/wp-content/uploads/2016/06/2_The_mathematics_of_love_Hannah_Fry_TEDxBinghamtonUniversity_YouTube-300x188.jpg" alt="" width="1250" height="783" /><figcaption id="caption-attachment-39220" class="wp-caption-text"><noscript><img decoding="async" class="wp-image-39220 lazyload" src="https://singjupost.com/wp-content/uploads/2016/06/2_The_mathematics_of_love_Hannah_Fry_TEDxBinghamtonUniversity_YouTube-300x188.jpg" alt="" width="1250" height="783" srcset="https://singjupost.com/wp-content/uploads/2016/06/2_The_mathematics_of_love_Hannah_Fry_TEDxBinghamtonUniversity_YouTube-300x188.jpg 300w, https://singjupost.com/wp-content/uploads/2016/06/2_The_mathematics_of_love_Hannah_Fry_TEDxBinghamtonUniversity_YouTube-768x481.jpg 768w, https://singjupost.com/wp-content/uploads/2016/06/2_The_mathematics_of_love_Hannah_Fry_TEDxBinghamtonUniversity_YouTube.jpg 782w" sizes="(max-width: 1250px) 100vw, 1250px" /></noscript> Hannah Fry</figcaption></figure><p>Here is the full transcript of mathematician Hannah Fry’s TEDx Talk: <em>The Mathematics of Love</em> at TEDxBinghamtonUniversity.</p><p><strong><span style="text-decoration: underline;">Listen to the MP3 Audio here: <a href="https://singjupost.com/wp-content/uploads/2016/06/The-mathematics-of-love-by-Hannah-Fry-at-TEDxBinghamtonUniversity.mp3">The mathematics of love by Hannah Fry at TEDxBinghamtonUniversity</a></span></strong></p><p><strong><span style="text-decoration: underline;">TRANSCRIPT: </span></strong></p><p>Thank you very much. So, yes, I&#8217;m Hannah Fry. I am a mathematician. And today I want to talk to you about the <em>mathematics of love</em>. Now, I think that we can all agree that mathematicians are famously excellent at finding love. But it&#8217;s not just because of our dashing personalities, superior conversational skills and excellent pencil cases. It&#8217;s also because we&#8217;ve actually done an awful lot of work into the maths of how to find the perfect partner.</p><p>Now, in my favorite paper on the subject, which is entitled, <em>&#8220;Why I Don&#8217;t Have a Girlfriend&#8221;</em> &#8212; Peter Backus tries to rate his chances of finding love. Now, Peter&#8217;s not a very greedy man. Of all of the available women in the U.K., all Peter&#8217;s looking for is somebody who lives near him, somebody in the right age range, somebody with a university degree, somebody he&#8217;s likely to get on well with, somebody who&#8217;s likely to be attractive, somebody who&#8217;s likely to find him attractive. And comes up with an estimate of 26 women in the whole of the UK. It&#8217;s not looking very good, is it Peter?</p><p>Now, just to put that into perspective, that&#8217;s about 400 times fewer than the best estimates of how many intelligent extraterrestrial life forms there are. And it also gives Peter a 1 in 285,000 chance of bumping into any one of these special ladies on a given night out. I&#8217;d like to think that&#8217;s why mathematicians don&#8217;t really bother going on nights out anymore.</p><p>Pages:<span id="plp_inital_pagination"> <a class="" href="https://singjupost.com/full-transcript-hannah-fry-on-the-mathematics-of-love-at-tedxbinghamtonuniversity/" class="post-page-numbers">First</a> |<span class="plp-active-page">1</span> | ... | <a class="" href="https://singjupost.com/full-transcript-hannah-fry-on-the-mathematics-of-love-at-tedxbinghamtonuniversity/2/" class="post-page-numbers">Next →</a> | <a class="" href="https://singjupost.com/full-transcript-hannah-fry-on-the-mathematics-of-love-at-tedxbinghamtonuniversity/4/" class="post-page-numbers">Last</a></span> | <a data-ajax="0" href="/full-transcript-hannah-fry-on-the-mathematics-of-love-at-tedxbinghamtonuniversity/?singlepage=1">View Full Transcript</a></p><div style="clear:both; margin-top:0em; margin-bottom:1em;"><a href="https://singjupost.com/settle-down-pay-attention-say-thank-you-a-how-to-by-kristen-race-transcript/" target="_blank" rel="dofollow" class="u4652a06ff794c30aad890e61d8bad97f"><!-- INLINE RELATED POSTS 1/3 //--><style> .u4652a06ff794c30aad890e61d8bad97f { padding:0px; margin: 0; padding-top:1em!important; padding-bottom:1em!important; width:100%; display: block; font-weight:bold; background-color:#e6e6e6; border:0!important; border-left:4px solid #464646!important; text-decoration:none; } .u4652a06ff794c30aad890e61d8bad97f:active, .u4652a06ff794c30aad890e61d8bad97f:hover { opacity: 1; transition: opacity 250ms; webkit-transition: opacity 250ms; text-decoration:none; } .u4652a06ff794c30aad890e61d8bad97f { transition: background-color 250ms; webkit-transition: background-color 250ms; opacity: 1; transition: opacity 250ms; webkit-transition: opacity 250ms; } .u4652a06ff794c30aad890e61d8bad97f .ctaText { font-weight:bold; color:#E67E22; text-decoration:none; font-size: 16px; } .u4652a06ff794c30aad890e61d8bad97f .postTitle { color:#464646; text-decoration: underline!important; font-size: 16px; } .u4652a06ff794c30aad890e61d8bad97f:hover .postTitle { text-decoration: underline!important; } </style><div style="padding-left:1em; padding-right:1em;"><span class="ctaText">ALSO READ:</span>&nbsp; <span class="postTitle">Settle Down, Pay Attention, Say Thank You: A How-To by Kristen Race (Transcript)</span></div></a></div></div><div class="nv-post-navigation"><div class="previous"><a href="https://singjupost.com/transcript-akala-on-hip-hop-shakespeare-at-tedxaldeburgh/" rel="prev"><span class="nav-direction">previous</span><span>Transcript: Akala on Hip-Hop &#038; Shakespeare? at TEDxAldeburgh</span></a></div><div class="next"><a href="https://singjupost.com/full-transcript-jesse-henry-on-the-theory-of-success-at-tedxfsu/" rel="next"><span class="nav-direction">next</span><span>Full Transcript: Jesse Henry on The Theory of Success at TEDxFSU</span></a></div></div><div id="taboola-below-article-thumbnails"></div></article><div class="nv-sidebar-wrap col-sm-12 nv-right blog-sidebar " ><aside id="secondary" role="complementary"><div id="block-78" class="widget widget_block"><div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained"><p><strong>LATEST POSTS: </strong></p><ul class="wp-block-latest-posts__list wp-block-latest-posts"><li><a class="wp-block-latest-posts__post-title" href="https://singjupost.com/transcript-shashi-tharoor-on-the-india-pak-ceasefire-implications-unresolved-questions/">Transcript: Shashi Tharoor on the India-Pak Ceasefire: Implications &#038; Unresolved Questions</a></li><li><a class="wp-block-latest-posts__post-title" href="https://singjupost.com/transcript-of-maga-and-the-fight-for-america-stephen-k-bannon/">Transcript of MAGA And The Fight For America &#8211; Stephen K. Bannon</a></li><li><a class="wp-block-latest-posts__post-title" href="https://singjupost.com/transcript-of-dr-ben-bikman-on-the-human-upgrade-podcast/">Transcript of Dr. Ben Bikman on The Human Upgrade Podcast</a></li><li><a class="wp-block-latest-posts__post-title" href="https://singjupost.com/transcript-of-prof-jeffrey-sachs-will-trump-dump-netanyahu/">Transcript of Prof. Jeffrey Sachs: Will Trump Dump Netanyahu?</a></li><li><a class="wp-block-latest-posts__post-title" href="https://singjupost.com/transcript-of-world-bank-president-ajay-banga-on-indus-water-treaty-in-abeyance-trump-tariffs/">Transcript of World Bank President Ajay Banga On Indus Water Treaty In Abeyance, Trump Tariffs</a></li></ul></div></div></div><div id="block-79" class="widget widget_block widget_text"><p><strong>RECOMMENDED: </strong></p></div><div id="block-80" class="widget widget_block"><div class="wp-block-data443-irp-shortcode irp-shortcode"><div style="clear:both; margin-top:0em; margin-bottom:1em;"><a href="https://singjupost.com/robert-lee-rescuing-leftover-cuisine-talks-at-google-transcript/" target="_blank" rel="dofollow" class="ua22a1866f9d999ac45c51e21324975e2"><style> .ua22a1866f9d999ac45c51e21324975e2 { padding:0px; margin: 0; padding-top:1em!important; padding-bottom:1em!important; width:100%; display: block; font-weight:bold; background-color:#e6e6e6; border:0!important; border-left:4px solid #464646!important; text-decoration:none; } .ua22a1866f9d999ac45c51e21324975e2:active, .ua22a1866f9d999ac45c51e21324975e2:hover { opacity: 1; transition: opacity 250ms; webkit-transition: opacity 250ms; text-decoration:none; } .ua22a1866f9d999ac45c51e21324975e2 { transition: background-color 250ms; webkit-transition: background-color 250ms; opacity: 1; transition: opacity 250ms; webkit-transition: opacity 250ms; } .ua22a1866f9d999ac45c51e21324975e2 .ctaText { font-weight:bold; color:#E67E22; text-decoration:none; font-size: 16px; } .ua22a1866f9d999ac45c51e21324975e2 .postTitle { color:#464646; text-decoration: underline!important; font-size: 16px; } .ua22a1866f9d999ac45c51e21324975e2:hover .postTitle { text-decoration: underline!important; } </style><div style="padding-left:1em; padding-right:1em;"><span class="ctaText">ALSO READ:</span>&nbsp; <span class="postTitle">Robert Lee: Rescuing Leftover Cuisine @ Talks at Google (Transcript)</span></div></a></div></div></div><div id="block-81" class="widget widget_block widget_text"><p><strong>CATEGORIES: </strong></p></div><div id="block-82" class="widget widget_block widget_categories"><ul class="wp-block-categories-list wp-block-categories"><li class="cat-item cat-item-7"><a href="https://singjupost.com/ancient-wisdom/">Ancient Wisdom</a></li><li class="cat-item cat-item-3585"><a href="https://singjupost.com/blog/">Blog</a></li><li class="cat-item cat-item-6"><a href="https://singjupost.com/business-finance/">Business &amp; Finance</a></li><li class="cat-item cat-item-13"><a href="https://singjupost.com/education/">Education</a></li><li class="cat-item cat-item-4214"><a href="https://singjupost.com/employment-careers/">Employment &amp; Careers</a></li><li class="cat-item cat-item-4213"><a href="https://singjupost.com/family-parenting/">Family &amp; Parenting</a></li><li class="cat-item cat-item-4231"><a href="https://singjupost.com/great-speeches/">Great Speeches</a></li><li class="cat-item cat-item-11"><a href="https://singjupost.com/health/">Health &amp; Wellness</a></li><li class="cat-item cat-item-3448"><a href="https://singjupost.com/education/inspiration/">Inspiration</a></li><li class="cat-item cat-item-12"><a href="https://singjupost.com/life-relationships/">Life &amp; Relationships</a></li><li class="cat-item cat-item-3511"><a href="https://singjupost.com/education/life-advice/">Life Advice</a></li><li class="cat-item cat-item-3696"><a href="https://singjupost.com/health/mental-health/">Mental Health</a></li><li class="cat-item cat-item-1"><a href="https://singjupost.com/education/motivation/">Motivation</a></li><li class="cat-item cat-item-4187"><a href="https://singjupost.com/news/">News</a></li><li class="cat-item cat-item-4267"><a href="https://singjupost.com/pilgrims-journey-2/">Pilgrims Journey</a></li><li class="cat-item cat-item-4245"><a href="https://singjupost.com/podcasts/">Podcasts</a></li><li class="cat-item cat-item-4191"><a href="https://singjupost.com/politics/">Politics</a></li><li class="cat-item cat-item-4192"><a href="https://singjupost.com/science/">Science</a></li><li class="cat-item cat-item-4216"><a href="https://singjupost.com/personal-transformation/">Self-Improvement</a></li><li class="cat-item cat-item-4228"><a href="https://singjupost.com/sermon/">Sermon</a></li><li class="cat-item cat-item-4269"><a href="https://singjupost.com/sponsored/">Sponsored</a></li><li class="cat-item cat-item-4184"><a href="https://singjupost.com/sports/">Sports</a></li><li class="cat-item cat-item-10"><a href="https://singjupost.com/technology/">Technology</a></li></ul></div></aside></div></div></div></main><!--/.neve-main--><footer class="site-footer" id="site-footer" ><div class="hfg_footer"><div class="footer--row footer-top hide-on-mobile hide-on-tablet layout-full-contained"
115
+ id="cb-row--footer-desktop-top"
116
+ data-row-id="top" data-show-on="desktop"><div
117
+ class="footer--row-inner footer-top-inner footer-content-wrap"><div class="container"><div
118
+ class="hfg-grid nv-footer-content hfg-grid-top row--wrapper row "
119
+ data-section="hfg_footer_layout_top" ><div class="hfg-slot left"><div class="builder-item desktop-left tablet-left mobile-left"><div class="item--inner builder-item--footer-one-widgets"
120
+ data-section="neve_sidebar-widgets-footer-one-widgets"
121
+ data-item-id="footer-one-widgets"><div class="widget-area"><div id="block-47" class="widget widget_block widget_text"><p>MISSION STATEMENT:</p></div><div id="block-48" class="widget widget_block widget_text"><p>Our mission is to provide the most accurate transcripts of videos and audios online.</p></div></div></div></div></div><div class="hfg-slot c-left"><div class="builder-item desktop-left tablet-left mobile-left"><div class="item--inner builder-item--footer-four-widgets"
122
+ data-section="neve_sidebar-widgets-footer-four-widgets"
123
+ data-item-id="footer-four-widgets"><div class="widget-area"><div id="block-70" class="widget widget_block"></div></div></div></div></div><div class="hfg-slot center"><div class="builder-item desktop-left tablet-left mobile-left"><div class="item--inner builder-item--footer-two-widgets"
124
+ data-section="neve_sidebar-widgets-footer-two-widgets"
125
+ data-item-id="footer-two-widgets"><div class="widget-area"><div id="block-45" class="widget widget_block widget_text"><p><strong>Become our Friends!</strong></p></div><div id="block-46" class="widget widget_block"><ul class="wp-block-list"><li><strong><a href="https://www.facebook.com/singjupost/">FACEBOOK</a></strong></li><li><strong><a href="https://twitter.com/singjupost">TWITTER</a></strong></li><li><strong><a href="https://www.youtube.com/channel/UCIiYEp_yR8x9gmjHvszGKtw">YOUTUBE</a></strong></li></ul></div></div></div></div></div></div></div></div></div><div class="footer--row footer-bottom hide-on-mobile hide-on-tablet layout-full-contained"
126
+ id="cb-row--footer-desktop-bottom"
127
+ data-row-id="bottom" data-show-on="desktop"><div
128
+ class="footer--row-inner footer-bottom-inner footer-content-wrap"><div class="container"><div
129
+ class="hfg-grid nv-footer-content hfg-grid-bottom row--wrapper row "
130
+ data-section="hfg_footer_layout_bottom" ><div class="hfg-slot left"><div class="builder-item desktop-left tablet-left mobile-left"><div class="item--inner builder-item--footer-menu has_menu"
131
+ data-section="footer_menu_primary"
132
+ data-item-id="footer-menu"><div class="component-wrap"><div role="navigation" class="nav-menu-footer"
133
+ aria-label="Footer Menu"><ul id="footer-menu" class="footer-menu nav-ul"><li id="menu-item-17021" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-17021"><div class="wrap"><a href="https://singjupost.com/about-us/">About Us</a></div></li><li id="menu-item-17020" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-17020"><div class="wrap"><a href="https://singjupost.com/contact-us/">Contact Us</a></div></li><li id="menu-item-17022" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-17022"><div class="wrap"><a href="https://singjupost.com/terms-of-use/">Terms Of Use</a></div></li><li id="menu-item-17023" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-privacy-policy menu-item-17023"><div class="wrap"><a rel="privacy-policy" href="https://singjupost.com/privacy/">Privacy Policy</a></div></li><li id="menu-item-17044" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-home menu-item-17044"><div class="wrap"><a href="https://singjupost.com">Home</a></div></li><li id="menu-item-46076" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-46076"><div class="wrap"><a href="https://singjupost.com/affiliate-disclosure/">Affiliate Disclosure</a></div></li></ul></div></div></div></div><div class="builder-item"><div class="item--inner"><div class="component-wrap"><div><p>Copyright© 2025 The Singju Post</div></div></div></div></div></div></div></div></div><div class="footer--row footer-top hide-on-desktop layout-full-contained"
134
+ id="cb-row--footer-mobile-top"
135
+ data-row-id="top" data-show-on="mobile"><div
136
+ class="footer--row-inner footer-top-inner footer-content-wrap"><div class="container"><div
137
+ class="hfg-grid nv-footer-content hfg-grid-top row--wrapper row "
138
+ data-section="hfg_footer_layout_top" ><div class="hfg-slot left"><div class="builder-item desktop-left tablet-left mobile-left"><div class="item--inner builder-item--footer-one-widgets"
139
+ data-section="neve_sidebar-widgets-footer-one-widgets"
140
+ data-item-id="footer-one-widgets"><div class="widget-area"><div id="block-47" class="widget widget_block widget_text"><p>MISSION STATEMENT:</p></div><div id="block-48" class="widget widget_block widget_text"><p>Our mission is to provide the most accurate transcripts of videos and audios online.</p></div></div></div></div></div><div class="hfg-slot c-left"><div class="builder-item desktop-left tablet-left mobile-left"><div class="item--inner builder-item--footer-four-widgets"
141
+ data-section="neve_sidebar-widgets-footer-four-widgets"
142
+ data-item-id="footer-four-widgets"><div class="widget-area"><div id="block-70" class="widget widget_block"></div></div></div></div></div><div class="hfg-slot center"><div class="builder-item desktop-left tablet-left mobile-left"><div class="item--inner builder-item--footer-two-widgets"
143
+ data-section="neve_sidebar-widgets-footer-two-widgets"
144
+ data-item-id="footer-two-widgets"><div class="widget-area"><div id="block-45" class="widget widget_block widget_text"><p><strong>Become our Friends!</strong></p></div><div id="block-46" class="widget widget_block"><ul class="wp-block-list"><li><strong><a href="https://www.facebook.com/singjupost/">FACEBOOK</a></strong></li><li><strong><a href="https://twitter.com/singjupost">TWITTER</a></strong></li><li><strong><a href="https://www.youtube.com/channel/UCIiYEp_yR8x9gmjHvszGKtw">YOUTUBE</a></strong></li></ul></div></div></div></div></div></div></div></div></div><div class="footer--row footer-bottom hide-on-desktop layout-full-contained"
145
+ id="cb-row--footer-mobile-bottom"
146
+ data-row-id="bottom" data-show-on="mobile"><div
147
+ class="footer--row-inner footer-bottom-inner footer-content-wrap"><div class="container"><div
148
+ class="hfg-grid nv-footer-content hfg-grid-bottom row--wrapper row "
149
+ data-section="hfg_footer_layout_bottom" ><div class="hfg-slot left"><div class="builder-item desktop-left tablet-left mobile-left"><div class="item--inner builder-item--footer-menu has_menu"
150
+ data-section="footer_menu_primary"
151
+ data-item-id="footer-menu"><div class="component-wrap"><div role="navigation" class="nav-menu-footer"
152
+ aria-label="Footer Menu"><ul id="footer-menu" class="footer-menu nav-ul"><li class="menu-item menu-item-type-post_type menu-item-object-page menu-item-17021"><div class="wrap"><a href="https://singjupost.com/about-us/">About Us</a></div></li><li class="menu-item menu-item-type-post_type menu-item-object-page menu-item-17020"><div class="wrap"><a href="https://singjupost.com/contact-us/">Contact Us</a></div></li><li class="menu-item menu-item-type-post_type menu-item-object-page menu-item-17022"><div class="wrap"><a href="https://singjupost.com/terms-of-use/">Terms Of Use</a></div></li><li class="menu-item menu-item-type-post_type menu-item-object-page menu-item-privacy-policy menu-item-17023"><div class="wrap"><a rel="privacy-policy" href="https://singjupost.com/privacy/">Privacy Policy</a></div></li><li class="menu-item menu-item-type-custom menu-item-object-custom menu-item-home menu-item-17044"><div class="wrap"><a href="https://singjupost.com">Home</a></div></li><li class="menu-item menu-item-type-post_type menu-item-object-page menu-item-46076"><div class="wrap"><a href="https://singjupost.com/affiliate-disclosure/">Affiliate Disclosure</a></div></li></ul></div></div></div></div><div class="builder-item"><div class="item--inner"><div class="component-wrap"><div><p>Copyright© 2025 The Singju Post</div></div></div></div></div></div></div></div></div></div></footer></div><!--/.wrapper--> <script type="speculationrules"> {"prefetch":[{"source":"document","where":{"and":[{"href_matches":"\/*"},{"not":{"href_matches":["\/wp-*.php","\/wp-admin\/*","\/wp-content\/uploads\/*","\/wp-content\/*","\/wp-content\/plugins\/*","\/wp-content\/themes\/neve-child-master\/*","\/wp-content\/themes\/neve\/*","\/*\\?(.+)"]}},{"not":{"selector_matches":"a[rel~=\"nofollow\"]"}},{"not":{"selector_matches":".no-prefetch, .no-prefetch a"}}]},"eagerness":"conservative"}]} </script><div id="mv-grow-data" data-settings='{&quot;general&quot;:{&quot;contentSelector&quot;:false,&quot;show_count&quot;:{&quot;content&quot;:true,&quot;sidebar&quot;:false},&quot;isTrellis&quot;:false,&quot;license_last4&quot;:&quot;&quot;},&quot;post&quot;:{&quot;ID&quot;:3638,&quot;categories&quot;:[{&quot;ID&quot;:12}]},&quot;shareCounts&quot;:{&quot;facebook&quot;:0,&quot;pinterest&quot;:0,&quot;reddit&quot;:0,&quot;twitter&quot;:0},&quot;shouldRun&quot;:true,&quot;buttonSVG&quot;:{&quot;share&quot;:{&quot;height&quot;:32,&quot;width&quot;:26,&quot;paths&quot;:[&quot;M20.8 20.8q1.984 0 3.392 1.376t1.408 3.424q0 1.984-1.408 3.392t-3.392 1.408-3.392-1.408-1.408-3.392q0-0.192 0.032-0.448t0.032-0.384l-8.32-4.992q-1.344 1.024-2.944 1.024-1.984 0-3.392-1.408t-1.408-3.392 1.408-3.392 3.392-1.408q1.728 0 2.944 0.96l8.32-4.992q0-0.128-0.032-0.384t-0.032-0.384q0-1.984 1.408-3.392t3.392-1.408 3.392 1.376 1.408 3.424q0 1.984-1.408 3.392t-3.392 1.408q-1.664 0-2.88-1.024l-8.384 4.992q0.064 0.256 0.064 0.832 0 0.512-0.064 0.768l8.384 4.992q1.152-0.96 2.88-0.96z&quot;]},&quot;facebook&quot;:{&quot;height&quot;:32,&quot;width&quot;:18,&quot;paths&quot;:[&quot;M17.12 0.224v4.704h-2.784q-1.536 0-2.080 0.64t-0.544 1.92v3.392h5.248l-0.704 5.28h-4.544v13.568h-5.472v-13.568h-4.544v-5.28h4.544v-3.904q0-3.328 1.856-5.152t4.96-1.824q2.624 0 4.064 0.224z&quot;]},&quot;twitter&quot;:{&quot;height&quot;:30,&quot;width&quot;:32,&quot;paths&quot;:[&quot;M30.3 29.7L18.5 12.4l0 0L29.2 0h-3.6l-8.7 10.1L10 0H0.6l11.1 16.1l0 0L0 29.7h3.6l9.7-11.2L21 29.7H30.3z M8.6 2.7 L25.2 27h-2.8L5.7 2.7H8.6z&quot;]},&quot;pinterest&quot;:{&quot;height&quot;:32,&quot;width&quot;:23,&quot;paths&quot;:[&quot;M0 10.656q0-1.92 0.672-3.616t1.856-2.976 2.72-2.208 3.296-1.408 3.616-0.448q2.816 0 5.248 1.184t3.936 3.456 1.504 5.12q0 1.728-0.32 3.36t-1.088 3.168-1.792 2.656-2.56 1.856-3.392 0.672q-1.216 0-2.4-0.576t-1.728-1.568q-0.16 0.704-0.48 2.016t-0.448 1.696-0.352 1.28-0.48 1.248-0.544 1.12-0.832 1.408-1.12 1.536l-0.224 0.096-0.16-0.192q-0.288-2.816-0.288-3.36 0-1.632 0.384-3.68t1.184-5.152 0.928-3.616q-0.576-1.152-0.576-3.008 0-1.504 0.928-2.784t2.368-1.312q1.088 0 1.696 0.736t0.608 1.824q0 1.184-0.768 3.392t-0.8 3.36q0 1.12 0.8 1.856t1.952 0.736q0.992 0 1.824-0.448t1.408-1.216 0.992-1.696 0.672-1.952 0.352-1.984 0.128-1.792q0-3.072-1.952-4.8t-5.12-1.728q-3.552 0-5.952 2.304t-2.4 5.856q0 0.8 0.224 1.536t0.48 1.152 0.48 0.832 0.224 0.544q0 0.48-0.256 1.28t-0.672 0.8q-0.032 0-0.288-0.032-0.928-0.288-1.632-0.992t-1.088-1.696-0.576-1.92-0.192-1.92z&quot;]},&quot;email&quot;:{&quot;height&quot;:32,&quot;width&quot;:28,&quot;paths&quot;:[&quot;M18.56 17.408l8.256 8.544h-25.248l8.288-8.448 4.32 4.064zM2.016 6.048h24.32l-12.16 11.584zM20.128 15.936l8.224-7.744v16.256zM0 24.448v-16.256l8.288 7.776z&quot;]}},&quot;inlineContentHook&quot;:[&quot;loop_start&quot;]}'></div> <script defer src="https://singjupost.com/wp-content/plugins/sg-cachepress/assets/js/lazysizes.min.js" id="siteground-optimizer-lazy-sizes-js-js"></script> <script id="dpsp-frontend-js-pro-js-extra"> var dpsp_ajax_send_save_this_email = {"ajax_url":"https:\/\/singjupost.com\/wp-admin\/admin-ajax.php","dpsp_token":"8f093875f6"}; </script> <script id="neve-script-js-extra"> var NeveProperties = {"ajaxurl":"https:\/\/singjupost.com\/wp-admin\/admin-ajax.php","nonce":"ed8cfc517f","isRTL":"","isCustomize":""}; </script> <script defer src="https://singjupost.com/wp-content/uploads/siteground-optimizer-assets/siteground-optimizer-combined-js-280b7255894b4ed6d4dbf66faa099e60.js"></script><script type="text/javascript"> window._taboola = window._taboola || [];
153
+ _taboola.push({article:'auto'});
154
+ !function (e, f, u, i) {
155
+ if (!document.getElementById(i)){
156
+ e.async = 1;
157
+ e.src = u;
158
+ e.id = i;
159
+ f.parentNode.insertBefore(e, f);
160
+ }
161
+ }(document.createElement('script'),
162
+ document.getElementsByTagName('script')[0],
163
+ '//cdn.taboola.com/libtrc/lh2holdings-network/loader.js',
164
+ 'tb_loader_script');
165
+ if(window.performance && typeof window.performance.mark == 'function')
166
+ {window.performance.mark('tbl_ic');} </script><script type="text/javascript"> window._taboola = window._taboola || [];
167
+ _taboola.push({
168
+ mode: "alternating-thumbnails-a",
169
+ container: "taboola-below-article-thumbnails",
170
+ placement: "Below Article Thumbnails",
171
+ target_type: "mix"
172
+ }); </script><script type="text/javascript"> window._taboola = window._taboola || [];
173
+ _taboola.push({flush: true}); </script></body></html> <button onclick="topFunction()" id="myBtn" title="Go to top">Go to Top</button>
data_sources/analytical/hannah_fry_uncertainty_unavoidable_transcript.html ADDED
The diff for this file is too large to render. See raw diff
 
data_sources/analytical/pew_research_ai_views_2023.html ADDED
The diff for this file is too large to render. See raw diff
 
data_sources/metaphorical/asimov_last_question.html ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html data-adblockkey="MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANDrp2lz7AOmADaN8tA50LsWcjLFyQFcb/P2Txc58oYOeILb3vBw7J6f4pamkAQVSQuqYsKx3YzdUHCvbVZvFUsCAwEAAQ==_DJLLP8BnFa5y8s4bPVJqaSwfY9uaNH6JVlTHPUrV76CFdMbThEKgNtHp8XgnS+nbDCL0SRRGdBYE0cnP3a0g7Q==" lang="en" style="background: #2B2B2B;">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVQI12P4//8/AAX+Av7czFnnAAAAAElFTkSuQmCC">
7
+ <link rel="preconnect" href="https://www.google.com" crossorigin>
8
+ </head>
9
+ <body>
10
+ <div id="target" style="opacity: 0"></div>
11
+ <script>window.park = "eyJ1dWlkIjoiMTUwZmIwY2MtMDA3Zi00MDU4LWJjYmMtNjYyMTI0NzA0ZGM5IiwicGFnZV90aW1lIjoxNzQ3MTY1NDU4LCJwYWdlX3VybCI6Imh0dHA6Ly93dzI1Lm11bHRpdmF4LmNvbS9sYXN0X3F1ZXN0aW9uLmh0bWw/c3ViaWQxPTIwMjUwNTE0LTA1NDQtMTg4NC1iODVhLTRlMDE2MDg1MDY1NCIsInBhZ2VfbWV0aG9kIjoiR0VUIiwicGFnZV9yZXF1ZXN0Ijp7InN1YmlkMSI6IjIwMjUwNTE0LTA1NDQtMTg4NC1iODVhLTRlMDE2MDg1MDY1NCJ9LCJwYWdlX2hlYWRlcnMiOnt9LCJob3N0Ijoid3cyNS5tdWx0aXZheC5jb20iLCJpcCI6IjczLjE1OC4yOC45NiJ9Cg==";</script>
12
+ <script src="/bIaMhJfIy.js"></script>
13
+ </body>
14
+ </html>
data_sources/metaphorical/asimov_last_question_cmu.html ADDED
@@ -0,0 +1,678 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <html><head><title>The Last Question</title>
2
+
3
+
4
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
5
+
6
+ <script type="text/javascript">
7
+ _uacct = "UA-61193-1";
8
+ urchinTracker();
9
+ </script></head><body class="Regular" bgcolor="#ffffff">
10
+ <center>
11
+ <h3>The Last Question</h3>
12
+ By Isaac Asimov
13
+ </center>
14
+ <p>
15
+ </p><center>
16
+ </center>
17
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Isaac Asimov was the most prolific science fiction
18
+ author of all time. In fifty years he averaged a new magazine article, short
19
+ story, or book every two weeks, and most of that on a manual typewriter. Asimov
20
+ thought that <em>The Last Question</em>, first copyrighted in 1956, was his
21
+ best short story ever. Even if you do not have the background in science to
22
+ be familiar with all of the concepts presented here, the ending packs more impact
23
+ than any other book that I've ever read. Don't read the end of the story first!
24
+ <blockquote> <em>This is by far my favorite story of all those I have written.
25
+ <p></p>
26
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;After all, I undertook to tell several trillion
27
+ years of human history in the space of a short story and I leave it to you as
28
+ to how well I succeeded. I also undertook another task, but I won't tell you
29
+ what that was lest l spoil the story for you.
30
+ <p></p>
31
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;It is a curious fact that innumerable readers
32
+ have asked me if I wrote this story. They seem never to remember the title of
33
+ the story or (for sure) the author, except for the vague thought it might be
34
+ me. But, of course, they never forget the story itself especially the ending.
35
+ The idea seems to drown out everything -- and I'm satisfied that it should.</em>
36
+ <p></p>
37
+ </blockquote>
38
+ <hr width="80%">
39
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The last question was asked for the first time,
40
+ half in jest, on May 21, 2061, at a time when humanity first stepped into the
41
+ light. The question came about as a result of a five-dollar bet over highballs,
42
+ and it happened this way:
43
+ <p></p>
44
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Alexander Adell and Bertram Lupov were two of the
45
+ faithful attendants of Multivac. As well as any human beings could, they knew
46
+ what lay behind the cold, clicking, flashing face -- miles and miles of face --
47
+ of that giant computer. They had at least a vague notion of the general plan of
48
+ relays and circuits that had long since grown past the point where any single
49
+ human could possibly have a firm grasp of the whole.
50
+ <p></p>
51
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Multivac was self-adjusting and self-correcting.
52
+ It had to be, for nothing human could adjust and correct it quickly enough or
53
+ even adequately enough. So Adell and Lupov attended the monstrous giant only lightly
54
+ and superficially, yet as well as any men could. They fed it data, adjusted questions
55
+ to its needs and translated the answers that were issued. Certainly they, and
56
+ all others like them, were fully entitled to share in the glory that was Multivac's.
57
+ <p></p>
58
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;For decades, Multivac had helped design the ships
59
+ and plot the trajectories that enabled man to reach the Moon, Mars, and Venus,
60
+ but past that, Earth's poor resources could not support the ships. Too much energy
61
+ was needed for the long trips. Earth exploited its coal and uranium with increasing
62
+ efficiency, but there was only so much of both.
63
+ <p></p>
64
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;But slowly Multivac learned enough to answer deeper
65
+ questions more fundamentally, and on May 14, 2061, what had been theory, became
66
+ fact.
67
+ <p></p>
68
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The energy of the sun was stored, converted, and
69
+ utilized directly on a planet-wide scale. All Earth turned off its burning coal,
70
+ its fissioning uranium, and flipped the switch that connected all of it to a small
71
+ station, one mile in diameter, circling the Earth at half the distance of the
72
+ Moon. All Earth ran by invisible beams of sunpower.
73
+ <p></p>
74
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Seven days had not sufficed to dim the glory of
75
+ it and Adell and Lupov finally managed to escape from the public functions, and
76
+ to meet in quiet where no one would think of looking for them, in the deserted
77
+ underground chambers, where portions of the mighty buried body of Multivac showed.
78
+ Unattended, idling, sorting data with contented lazy clickings, Multivac, too,
79
+ had earned its vacation and the boys appreciated that. They had no intention,
80
+ originally, of disturbing it.
81
+ <p></p>
82
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;They had brought a bottle with them, and their only
83
+ concern at the moment was to relax in the company of each other and the bottle.
84
+ <p></p>
85
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"It's amazing when you think of it," said Adell.
86
+ His broad face had lines of weariness in it, and he stirred his drink slowly with
87
+ a glass rod, watching the cubes of ice slur clumsily about. "All the energy we
88
+ can possibly ever use for free. Enough energy, if we wanted to draw on it, to
89
+ melt all Earth into a big drop of impure liquid iron, and still never miss the
90
+ energy so used. All the energy we could ever use, forever and forever and forever."
91
+ <p></p>
92
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Lupov cocked his head sideways. He had a trick of
93
+ doing that when he wanted to be contrary, and he wanted to be contrary now, partly
94
+ because he had had to carry the ice and glassware. "Not forever," he said.
95
+ <p></p>
96
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Oh, hell, just about forever. Till the sun runs
97
+ down, Bert."
98
+ <p></p>
99
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"That's not forever."
100
+ <p></p>
101
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"All right, then. Billions and billions of years.
102
+ Ten billion, maybe. Are you satisfied?"
103
+ <p></p>
104
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Lupov put his fingers through his thinning hair
105
+ as though to reassure himself that some was still left and sipped gently at his
106
+ own drink. "Ten billion years isn't forever."
107
+ <p></p>
108
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Well, it will last our time, won't it?"
109
+ <p></p>
110
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"So would the coal and uranium."
111
+ <p></p>
112
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"All right, but now we can hook up each individual
113
+ spaceship to the Solar Station, and it can go to Pluto and back a million times
114
+ without ever worrying about fuel. You can't do <em>that</em> on coal and uranium.
115
+ Ask Multivac, if you don't believe me.
116
+ <p></p>
117
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"I don't have to ask Multivac. I know that."
118
+ <p></p>
119
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Then stop running down what Multivac's done for
120
+ us," said Adell, blazing up, "It did all right."
121
+ <p></p>
122
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Who says it didn't? What I say is that a sun won't
123
+ last forever. That's all I'm saying. We're safe for ten billion years, but then
124
+ what?" Lupow pointed a slightly shaky finger at the other. "And don't say we'll
125
+ switch to another sun."
126
+ <p></p>
127
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;There was silence for a while. Adell put his glass
128
+ to his lips only occasionally, and Lupov's eyes slowly closed. They rested.
129
+ <p></p>
130
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Then Lupov's eyes snapped open. "You're thinking
131
+ we'll switch to another sun when ours is done, aren't you?"
132
+ <p></p>
133
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"I'm not thinking."
134
+ <p></p>
135
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Sure you are. You're weak on logic, that's the
136
+ trouble with you. You're like the guy in the story who was caught in a sudden
137
+ shower and who ran to a grove of trees and got under one. He wasn't worried, you
138
+ see, because he figured when one tree got wet through, he would just get under
139
+ another one."
140
+ <p></p>
141
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"I get it," said Adell. "Don't shout. When the sun
142
+ is done, the other stars will be gone, too."
143
+ <p></p>
144
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Darn right they will," muttered Lupov. "It all
145
+ had a beginning in the original cosmic explosion, whatever that was, and it'll
146
+ all have an end when all the stars run down. Some run down faster than others.
147
+ Hell, the giants won't last a hundred million years. The sun will last ten billion
148
+ years and maybe the dwarfs will last two hundred billion for all the good they
149
+ are. But just give us a trillion years and everything will be dark. Entropy has
150
+ to increase to maximum, that's all."
151
+ <p></p>
152
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"I know all about entropy," said Adell, standing
153
+ on his dignity.
154
+ <p></p>
155
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"The hell you do."
156
+ <p></p>
157
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"I know as much as you do."
158
+ <p></p>
159
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Then you know everything's got to run down someday."
160
+ <p></p>
161
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"All right. Who says they won't?"
162
+ <p></p>
163
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"You did, you poor sap. You said we had all the
164
+ energy we needed, forever. You said 'forever.'
165
+ <p></p>
166
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;It was Adell's turn to be contrary. "Maybe we can
167
+ build things up again someday," he said.
168
+ <p></p>
169
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Never."
170
+ <p></p>
171
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Why not? Someday."
172
+ <p></p>
173
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Never."
174
+ <p></p>
175
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Ask Multivac."
176
+ <p></p>
177
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"<em>You</em> ask Multivac. I dare you. Five dollars
178
+ says it can't be done."
179
+ <p></p>
180
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Adell was just drunk enough to try, just sober enough
181
+ to be able to phrase the necessary symbols and operations into a question which,
182
+ in words, might have corresponded to this: Will mankind one day without the net
183
+ expenditure of energy be able to restore the sun to its full youthfulness even
184
+ after it had died of old age?
185
+ <p></p>
186
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Or maybe it could be put more simply like this:
187
+ How can the net amount of entropy of the universe be massively decreased?
188
+ <p></p>
189
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Multivac fell dead and silent. The slow flashing
190
+ of lights ceased, the distant sounds of clicking relays ended.
191
+ <p></p>
192
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Then, just as the frightened technicians felt they
193
+ could hold their breath no longer, there was a sudden springing to life of the
194
+ teletype attached to that portion of Multivac. Five words were printed: INSUFFICIENT
195
+ DATA FOR MEANINGFUL ANSWER.
196
+ <p></p>
197
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"No bet," whispered Lupov. They left hurriedly.
198
+ <p></p>
199
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;By next morning, the two, plagued with throbbing
200
+ head and cottony mouth, had forgotten the incident.
201
+ <p></p>
202
+ <hr width="80%">
203
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Jerrodd, Jerrodine, and Jerrodette I and II watched
204
+ the starry picture in the visiplate change as the passage through hyperspace was
205
+ completed in its non-time lapse. At once, the even powdering of stars gave way
206
+ to the predominance of a single bright shining disk, the size of a marble, centered
207
+ on the viewing-screen.
208
+ <p></p>
209
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"That's X-23," said Jerrodd confidently. His thin
210
+ hands clamped tightly behind his back and the knuckles whitened.
211
+ <p></p>
212
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The little Jerrodettes, both girls, had experienced
213
+ the hyperspace passage for the first time in their lives and were self-conscious
214
+ over the momentary sensation of insideoutness. They buried their giggles and chased
215
+ one another wildly about their mother, screaming, "We've reached X-23 -- we've
216
+ reached X-23 -- we've --"
217
+ <p></p>
218
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Quiet, children." said Jerrodine sharply. "Are
219
+ you sure, Jerrodd?"
220
+ <p></p>
221
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"What is there to be but sure?" asked Jerrodd, glancing
222
+ up at the bulge of featureless metal just under the ceiling. It ran the length
223
+ of the room, disappearing through the wall at either end. It was as long as the
224
+ ship.
225
+ <p></p>
226
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Jerrodd scarcely knew a thing about the thick rod
227
+ of metal except that it was called a Microvac, that one asked it questions if
228
+ one wished; that if one did not it still had its task of guiding the ship to a
229
+ preordered destination; of feeding on energies from the various Sub-galactic Power
230
+ Stations; of computing the equations for the hyperspatial jumps.
231
+ <p></p>
232
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Jerrodd and his family had only to wait and live
233
+ in the comfortable residence quarters of the ship. Someone had once told Jerrodd
234
+ that the "ac" at the end of "Microvac" stood for ''automatic computer" in ancient
235
+ English, but he was on the edge of forgetting even that.
236
+ <p></p>
237
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Jerrodine's eyes were moist as she watched the visiplate.
238
+ "I can't help it. I feel funny about leaving Earth."
239
+ <p></p>
240
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Why, for Pete's sake?" demanded Jerrodd. "We had
241
+ nothing there. We'll have everything on X-23. You won't be alone. You won't be
242
+ a pioneer. There are over a million people on the planet already. Good Lord, our
243
+ great-grandchildren will be looking for new worlds because X-23 will be overcrowded."
244
+ Then, after a reflective pause, "I tell you, it's a lucky thing the computers
245
+ worked out interstellar travel the way the race is growing."
246
+ <p></p>
247
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"I know, I know," said Jerrodine miserably.
248
+ <p></p>
249
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Jerrodette I said promptly, "Our Microvac is the
250
+ best Microvac in the world."
251
+ <p></p>
252
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"I think so, too," said Jerrodd, tousling her hair.
253
+ <p></p>
254
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;It was a nice feeling to have a Microvac of your
255
+ own and Jerrodd was glad he was part of his generation and no other. In his father's
256
+ youth, the only computers had been tremendous machines taking up a hundred square
257
+ miles of land. There was only one to a planet. Planetary ACs they were called.
258
+ They had been growing in size steadily for a thousand years and then, all at once,
259
+ came refinement. In place of transistors, had come molecular valves so that even
260
+ the largest Planetary AC could be put into a space only half the volume of a spaceship.
261
+ <p></p>
262
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Jerrodd felt uplifted, as he always did when he
263
+ thought that his own personal Microvac was many times more complicated than the
264
+ ancient and primitive Multivac that had first tamed the Sun, and almost as complicated
265
+ as Earth's Planetarv AC (the largest) that had first solved the problem of hyperspatial
266
+ travel and had made trips to the stars possible.
267
+ <p></p>
268
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"So many stars, so many planets," sighed Jerrodine,
269
+ busy with her own thoughts. "I suppose families will be going out to new planets
270
+ forever, the way we are now."
271
+ <p></p>
272
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Not forever," said Jerrodd, with a smile. "It will
273
+ all stop someday, but not for billions of years. Many billions. Even the stars
274
+ run down, you know. Entropy must increase.
275
+ <p></p>
276
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"What's entropy, daddy?" shrilled Jerrodette II.
277
+ <p></p>
278
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Entropy, little sweet, is just a word which means
279
+ the amount of running-down of the universe. Everything runs down, you know, like
280
+ your little walkie-talkie robot, remember?"
281
+ <p></p>
282
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Can't you just put in a new power-unit, like with
283
+ my robot?"
284
+ <p></p>
285
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"The stars are the power-units. dear. Once they're
286
+ gone, there are no more power-units."
287
+ <p></p>
288
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Jerrodette I at once set up a howl. "Don't let them,
289
+ daddy. Don't let the stars run down."
290
+ <p></p>
291
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Now look what you've done," whispered Jerrodine,
292
+ exasperated.
293
+ <p></p>
294
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"How was I to know it would frighten them?" Jerrodd
295
+ whispered back,
296
+ <p></p>
297
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Ask the Microvac," wailed Jerrodette I. "Ask him
298
+ how to turn the stars on again."
299
+ <p></p>
300
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Go ahead," said Jerrodine. "It will quiet them
301
+ down." (Jerrodette II was beginning to cry, also.)
302
+ <p></p>
303
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Jerrodd shrugged. "Now, now, honeys. I'll ask Microvac.
304
+ Don't worry, he'll tell us."
305
+ <p></p>
306
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;He asked the Microvac, adding quickly, "Print the
307
+ answer."
308
+ <p></p>
309
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Jerrodd cupped the strip or thin cellufilm and said
310
+ cheerfully, "See now, the Microvac says it will take care of everything when the
311
+ time comes so don't worry."
312
+ <p></p>
313
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Jerrodine said, "And now, children, it's time for
314
+ bed. We'll be in our new home soon."
315
+ <p></p>
316
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Jerrodd read the words on the cellufilm again before
317
+ destroying it: INSUFICIENT DATA FOR MEANINGFUL ANSWER.
318
+ <p></p>
319
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;He shrugged and looked at the visiplate. X-23 was
320
+ just ahead.
321
+ <p></p>
322
+ <hr width="80%">
323
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;VJ-23X of Lameth stared into the black depths of
324
+ the three-dimensional, small-scale map of the Galaxy and said, "Are we ridiculous,
325
+ I wonder in being so concerned about the matter?"
326
+ <p></p>
327
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MQ-17J of Nicron shook his head. "I think not. You
328
+ know the Galaxy will be filled in five years at the present rate of expansion."
329
+ <p></p>
330
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Both seemed in their early twenties, both were tall
331
+ and perfectly formed.
332
+ <p></p>
333
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Still," said VJ-23X, "I hesitate to submit a pessimistic
334
+ report to the Galactic Council."
335
+ <p></p>
336
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"I wouldn't consider any other kind of report. Stir
337
+ them up a bit. We've got to stir them up."
338
+ <p></p>
339
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;VJ-23X sighed. "Space is infinite. A hundred billion
340
+ Galaxies are there for the taking. More."
341
+ <p></p>
342
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"A hundred billion is not infinite and it's getting
343
+ less infinite all the time. Consider! Twenty thousand years ago, mankind first
344
+ solved the problem of utilizing stellar energy, and a few centuries later, interstellar
345
+ travel became possible. It took mankind a million years to fill one small world
346
+ and then only fifteen thousand years to fill the rest of the Galaxy. Now the population
347
+ doubles every ten years --
348
+ <p></p>
349
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;VJ-23X interrupted. "We can thank immortality for
350
+ that."
351
+ <p></p>
352
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Very well. Immortality exists and we have to take
353
+ it into account. I admit it has its seamy side, this immortality. The Galactic
354
+ AC has solved many problems for us, but in solving the problem of preventing old
355
+ age and death, it has undone all its other solutions."
356
+ <p></p>
357
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Yet you wouldn't want to abandon life, I suppose."
358
+ <p></p>
359
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Not at all," snapped MQ-17J, softening it at once
360
+ to, "Not yet. I'm by no means old enough. How old are you?"
361
+ <p></p>
362
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Two hundred twenty-three. And you?"
363
+ <p></p>
364
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"I'm still under two hundred. --But to get back
365
+ to my point. Population doubles every ten years. Once this GaIaxy is filled, we'll
366
+ have filled another in ten years. Another ten years and we'll have filled two
367
+ more. Another decade, four more. In a hundred years, we'll have filled a thousand
368
+ Galaxies. In a thousand years, a million Galaxies. In ten thousand years, the
369
+ entire known universe. Then what?"
370
+ <p></p>
371
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;VJ-23X said, "As a side issue, there's a problem
372
+ of transportation. I wonder how many sunpower units it will take to move Galaxies
373
+ of individuals from one Galaxy to the next."
374
+ <p></p>
375
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"A very good point. Already, mankind consumes two
376
+ sunpower units per year."
377
+ <p></p>
378
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Most of it's wasted. After all, our own Galaxy
379
+ alone pours out a thousand sunpower units a year and we only use two of those."
380
+ <p></p>
381
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Granted, but even with a hundred per cent efficiency,
382
+ we only stave off the end. Our energy requirements are going up in a geometric
383
+ progression even faster than our population. We'll run out of energy even sooner
384
+ than we run out of Galaxies. A good point. A very good point."
385
+ <p></p>
386
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"We'll just have to build new stars out of interstellar
387
+ gas."
388
+ <p></p>
389
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Or out of dissipated heat?" asked MQ-17J, sarcastically.
390
+ <p></p>
391
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"There may be some way to reverse entropy. We ought
392
+ to ask the Galactic AC."
393
+ <p></p>
394
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;VJ-23X was not really serious, but MQ-17J pulled
395
+ out his AC-contact from his pocket and placed it on the table before him.
396
+ <p></p>
397
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"I've half a mind to," he said. "It's something
398
+ the human race will have to face someday."
399
+ <p></p>
400
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;He stared somberly at his small AC-contact. It was
401
+ only two inches cubed and nothing in itself, but it was connected through hyperspace
402
+ with the great Galactic AC that served all mankind. Hyperspace considered, it
403
+ was an integral part of the Galactic AC.
404
+ <p></p>
405
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MQ-17J paused to wonder if someday in his immortal
406
+ life he would get to see the Galactic AC. It was on a little world of its own,
407
+ a spider webbing of force-beams holding the matter within which surges of submesons
408
+ took the place of the old clumsy molecular valves. Yet despite its sub-etheric
409
+ workings, the Galactic AC was known to be a full thousand feet across.
410
+ <p></p>
411
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MQ-17J asked suddenly of his AC-contact, "Can entropy
412
+ ever be reversed?"
413
+ <p></p>
414
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;VJ-23X looked startled and said at once, "Oh, say,
415
+ I didn't really mean to have you ask that."
416
+ <p></p>
417
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Why not?"
418
+ <p></p>
419
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"We both know entropy can't be reversed. You can't
420
+ turn smoke and ash back into a tree."
421
+ <p></p>
422
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Do you have trees on your world?" asked MQ-17J.
423
+ <p></p>
424
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The sound of the Galactic AC startled them into
425
+ silence. Its voice came thin and beautiful out of the small AC-contact on the
426
+ desk. It said: THERE IS INSUFFICIENT DATA FOR A MEANINGFUL ANSWER.
427
+ <p></p>
428
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;VJ-23X said, "See!"
429
+ <p></p>
430
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The two men thereupon returned to the question of
431
+ the report they were to make to the Galactic Council.
432
+ <p></p>
433
+ <hr width="80%">
434
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Zee Prime's mind spanned the new Galaxy with a faint
435
+ interest in the countless twists of stars that powdered it. He had never seen
436
+ this one before. Would he ever see them all? So many of them, each with its load
437
+ of humanity. --But a load that was almost a dead weight. More and more, the real
438
+ essence of men was to be found out here, in space.
439
+ <p></p>
440
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Minds, not bodies! The immortal bodies remained
441
+ back on the planets, in suspension over the eons. Sometimes they roused for material
442
+ activity but that was growing rarer. Few new individuals were coming into existence
443
+ to join the incredibly mighty throng, but what matter? There was little room in
444
+ the Universe for new individuals.
445
+ <p></p>
446
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Zee Prime was roused out of his reverie upon coming
447
+ across the wispy tendrils of another mind.
448
+ <p></p>
449
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"I am Zee Prime," said Zee Prime. "And you?"
450
+ <p></p>
451
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"I am Dee Sub Wun. Your Galaxy?"
452
+ <p></p>
453
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"We call it only the Galaxy. And you?"
454
+ <p></p>
455
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"We call ours the same. All men call their Galaxy
456
+ their Galaxy and nothing more. Why not?"
457
+ <p></p>
458
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"True. Since all Galaxies are the same."
459
+ <p></p>
460
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Not all Galaxies. On one particular Galaxy the
461
+ race of man must have originated. That makes it different."
462
+ <p></p>
463
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Zee Prime said, "On which one?"
464
+ <p></p>
465
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"I cannot say. The Universal AC would know."
466
+ <p></p>
467
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Shall we ask him? I am suddenly curious."
468
+ <p></p>
469
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Zee Prime's perceptions broadened until the Galaxies
470
+ themselves shrank and became a new, more diffuse powdering on a much larger background.
471
+ So many hundreds of billions of them, all with their immortal beings, all carrying
472
+ their load of intelligences with minds that drifted freely through space. And
473
+ yet one of them was unique among them all in being the original Galaxy. One of
474
+ them had, in its vague and distant past, a period when it was the only Galaxy
475
+ populated by man.
476
+ <p></p>
477
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Zee Prime was consumed with curiosity to see this
478
+ Galaxy and he called out: "Universal AC! On which Galaxy did mankind originate?"
479
+ <p></p>
480
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The Universal AC heard, for on every world and throughout
481
+ space, it had its receptors ready, and each receptor led through hyperspace to
482
+ some unknown point where the Universal AC kept itself aloof.
483
+ <p></p>
484
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Zee Prime knew of only one man whose thoughts had
485
+ penetrated within sensing distance of Universal AC, and he reported only a shining
486
+ globe, two feet across, difficult to see.
487
+ <p></p>
488
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"But how can that be all of Universal AC?" Zee Prime
489
+ had asked.
490
+ <p></p>
491
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Most of it," had been the answer, "is in hyperspace.
492
+ In what form it is there I cannot imagine."
493
+ <p></p>
494
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Nor could anyone, for the day had long since passed,
495
+ Zee Prime knew, when any man had any part of the making of a Universal AC. Each
496
+ Universal AC designed and constructed its successor. Each, during its existence
497
+ of a million years or more accumulated the necessary data to build a better and
498
+ more intricate, more capable successor in which its own store of data and individuality
499
+ would be submerged.
500
+ <p></p>
501
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The Universal AC interrupted Zee Prime's wandering
502
+ thoughts, not with words, but with guidance. Zee Prime's mentality was guided
503
+ into the dim sea of Galaxies and one in particular enlarged into stars.
504
+ <p></p>
505
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A thought came, infinitely distant, but infinitely
506
+ clear. "THIS IS THE ORIGINAL GALAXY OF MAN."
507
+ <p></p>
508
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;But it was the same after all, the same as any other,
509
+ and Lee Prime stifled his disappointment.
510
+ <p></p>
511
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Dee Sub Wun, whose mind had accompanied the other,
512
+ said suddenly, "And is one of these stars the original star of Man?"
513
+ <p></p>
514
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The Universal AC said, "MAN'S ORIGINAL STAR HAS
515
+ GONE NOVA. IT IS A WHITE DWARF"
516
+ <p></p>
517
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Did the men upon it die?" asked Lee Prime, startled
518
+ and without thinking.
519
+ <p></p>
520
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The Universal AC said, "A NEW WORLD, AS IN SUCH
521
+ CASES WAS CONSTRUCTED FOR THEIR PHYSICAL BODIES IN TlME."
522
+ <p></p>
523
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Yes, of course," said Zee Prime, but a sense of
524
+ loss overwhelmed him even so. His mind released its hold on the original Galaxy
525
+ of Man, let it spring back and lose itself among the blurred pin points. He never
526
+ wanted to see it again.
527
+ <p></p>
528
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Dee Sub Wun said, "What is wrong?"
529
+ <p></p>
530
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"The stars are dying. The original star is dead."
531
+ <p></p>
532
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"They must all die. Why not?"
533
+ <p></p>
534
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"But when all energy is gone, our bodies will finally
535
+ die, and you and I with them."
536
+ <p></p>
537
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"It will take billions of years."
538
+ <p></p>
539
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"I do not wish it to happen even after billions
540
+ of years. Universal AC! How may stars be kept from dying?"
541
+ <p></p>
542
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Dee Sub Wun said in amusement, "You're asking how
543
+ entropy might be reversed in direction."
544
+ <p></p>
545
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;And the Universal AC answered: "THERE IS AS YET
546
+ INSUFFICIENT DATA FOR A MEANINGFUL ANSWER."
547
+ <p></p>
548
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Zee Prime's thoughts fled back to his own Galaxy.
549
+ He gave no further thought to Dee Sub Wun, whose body might be waiting on a Galaxy
550
+ a trillion light-years away, or on the star next to Zee Prime's own. It didn't
551
+ matter.
552
+ <p></p>
553
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Unhappily, Zee Prime began collecting interstellar
554
+ hydrogen out of which to build a small star of his own. If the stars must someday
555
+ die, at least some could yet be built.
556
+ <p></p>
557
+ <hr width="80%">
558
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Man considered with himself, for in a way, Man,
559
+ mentally, was one. He consisted of a trillion, trillion, trillion ageless bodies,
560
+ each in its place, each resting quiet and incorruptible, each cared for by perfect
561
+ automatons, equally incorruptible, while the minds of all the bodies freely melted
562
+ one into the other, indistinguishable.
563
+ <p></p>
564
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Man said, "The Universe is dying."
565
+ <p></p>
566
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Man looked about at the dimming Galaxies. The giant
567
+ stars, spendthrifts, were gone long ago, back in the dimmest of the dim far past.
568
+ Almost all stars were white dwarfs, fading to the end.
569
+ <p></p>
570
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;New stars had been built of the dust between the
571
+ stars, some by natural processes, some by Man himself, and those were going, too.
572
+ White dwarfs might yet be crashed together and of the mighty forces so released,
573
+ new stars built, but only one star for every thousand white dwarfs destroyed,
574
+ and those would come to an end, too.
575
+ <p></p>
576
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Man said, "Carefully husbanded, as directed by the
577
+ Cosmic AC, the energy that is even yet left in all the Universe will last for
578
+ billions of years."
579
+ <p></p>
580
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"But even so," said Man, "eventually it will all
581
+ come to an end. However it may be husbanded, however stretched out, the energy
582
+ once expended is gone and cannot be restored. Entropy must increase forever to
583
+ the maximum."
584
+ <p></p>
585
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Man said, "Can entropy not be reversed? Let us ask
586
+ the Cosmic AC."
587
+ <p></p>
588
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The Cosmic AC surrounded them but not in space.
589
+ Not a fragment of it was in space. It was in hyperspace and made of something
590
+ that was neither matter nor energy. The question of its size and nature no longer
591
+ had meaning in any terms that Man could comprehend.
592
+ <p></p>
593
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Cosmic AC," said Man, "how may entropy be reversed?"
594
+ <p></p>
595
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The Cosmic AC said, "THERE IS AS YET INSUFFICIENT
596
+ DATA FOR A MEANINGFUL ANSWER."
597
+ <p></p>
598
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Man said, "Collect additional data."
599
+ <p></p>
600
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The Cosmic AC said, 'I WILL DO S0. I HAVE BEEN DOING
601
+ SO FOR A HUNDRED BILLION YEARS. MY PREDECESORS AND I HAVE BEEN ASKED THIS QUESTION
602
+ MANY TlMES. ALL THE DATA I HAVE REMAINS INSUFFICIENT.
603
+ <p></p>
604
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Will there come a time," said Man, 'when data will
605
+ be sufficient or is the problem insoluble in all conceivable circumstances?"
606
+ <p></p>
607
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The Cosmic AC said, "NO PROBLEM IS INSOLUBLE IN
608
+ ALL CONCEIVABLE CIRCUMSTANCES."
609
+ <p></p>
610
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Man said, "When will you have enough data to answer
611
+ the question?"
612
+ <p></p>
613
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The Cosmic AC said, "THERE IS AS YET INSUFFICIENT
614
+ DATA FOR A MEANINGFUL ANSWER."
615
+ <p></p>
616
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Will you keep working on it?" asked Man.
617
+ <p></p>
618
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The Cosmic AC said, "I WILL."
619
+ <p></p>
620
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Man said, "We shall wait."
621
+ <p></p>
622
+ <hr width="80%">
623
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The stars and Galaxies died and snuffed out, and
624
+ space grew black after ten trillion years of running down.
625
+ <p></p>
626
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;One by one Man fused with AC, each physical body
627
+ losing its mental identity in a manner that was somehow not a loss but a gain.
628
+ <p></p>
629
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Man's last mind paused before fusion, looking over
630
+ a space that included nothing but the dregs of one last dark star and nothing
631
+ besides but incredibly thin matter, agitated randomly by the tag ends of heat
632
+ wearing out, asymptotically, to the absolute zero.
633
+ <p></p>
634
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Man said, "AC, is this the end? Can this chaos not
635
+ be reversed into the Universe once more? Can that not be done?"
636
+ <p></p>
637
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AC said, "THERE IS AS YET INSUFFICIENT DATA FOR
638
+ A MEANINGFUL ANSWER."
639
+ <p></p>
640
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Man's last mind fused and only AC existed -- and
641
+ that in hyperspace.
642
+ <p></p>
643
+ <hr width="80%">
644
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Matter and energy had ended and with it space and
645
+ time. Even AC existed only for the sake of the one last question that it had never
646
+ answered from the time a half-drunken computer [technician] ten trillion years
647
+ before had asked the question of a computer that was to AC far less than was a
648
+ man to Man.
649
+ <p></p>
650
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;All other questions had been answered, and until
651
+ this last question was answered also, AC might not release his consciousness.
652
+ <p></p>
653
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;All collected data had come to a final end. Nothing
654
+ was left to be collected.
655
+ <p></p>
656
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;But all collected data had yet to be completely
657
+ correlated and put together in all possible relationships.
658
+ <p></p>
659
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A timeless interval was spent in doing that.
660
+ <p></p>
661
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;And it came to pass that AC learned how to reverse
662
+ the direction of entropy.
663
+ <p></p>
664
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;But there was now no man to whom AC might give the
665
+ answer of the last question. No matter. The answer -- by demonstration -- would
666
+ take care of that, too.
667
+ <p></p>
668
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;For another timeless interval, AC thought how best
669
+ to do this. Carefully, AC organized the program.
670
+ <p></p>
671
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The consciousness of AC encompassed all of what
672
+ had once been a Universe and brooded over what was now Chaos. Step by step, it
673
+ must be done.
674
+ <p></p>
675
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;And AC said, "LET THERE BE LIGHT!"
676
+ <p></p>
677
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;And there was light --<br>
678
+ <script type='text/javascript'>var _gaq = _gaq || [];_gaq.push(['_setAccount', 'UA-18224100-12']);_gaq.push(['_setDomainName', 'case.edu']);_gaq.push(['_trackPageview']);(function() {var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);})();</script></body></html>
data_sources/metaphorical/asimov_relativity_of_wrong.html ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
2
+
3
+ <html>
4
+ <head>
5
+ <link rel="stylesheet" type="text/css" href="../style/essay.css">
6
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
7
+ <title>The Relativity of Wrong by Isaac Asimov</title>
8
+ </head>
9
+
10
+ <body>
11
+
12
+ <h1>The Relativity of Wrong</h1>
13
+
14
+ <p class="author">by Isaac Asimov</p>
15
+
16
+ <div class="section">
17
+ <p>I received a letter from a reader the other day. It was handwritten in crabbed penmanship so that it was very difficult to read. Nevertheless, I tried to make it out just in case it might prove to be important.</p>
18
+
19
+ <p>In the first sentence, he told me he was majoring in English Literature, but felt he needed to teach me science. (I sighed a bit, for I knew very few English Lit majors who are equipped to teach me science, but I am very aware of the vast state of my ignorance and I am prepared to learn as much as I can from anyone, however low on the social scale, so I read on.)</p>
20
+
21
+ <p>It seemed that in one of my innumerable essays, here and elsewhere, I had expressed a certain gladness at living in a century in which we finally got the basis of the Universe straight.</p>
22
+
23
+ <p>I didn't go into detail in the matter, but what I meant was that we now know the basic rules governing the Universe, together with the gravitational interrelationships of its gross components, as shown in the theory of relativity worked out between 1905 and 1916. We also know the basic rules governing the subatomic particles and their interrelationships, since these are very neatly described by the quantum theory worked out between 1900 and 1930. What's more, we have found that the galaxies and clusters of galaxies are the basic units of the physical Universe, as discovered between 1920 and 1930.</p>
24
+
25
+ <p>These are all twentieth-century discoveries, you see.</p>
26
+
27
+ <p>The young specialist in English Lit, having quoted me, went on to lecture me severely on the fact that in <em>every</em> century people have thought they understood the Universe at last, and in <em>every</em> century they were proven to be wrong. It follows that the one thing we can say about out modern "knowledge" is that it is <em>wrong</em>.</p>
28
+
29
+ <p>The young man then quoted with approval what Socrates had said on learning that the Delphic oracle had proclaimed him the wisest man in Greece. "If I am the wisest man," said Socrates, "it is because I alone know that I know nothing." The implication was that I was very foolish because I knew a great deal.</p>
30
+
31
+ <p>Alas, none of this was new to me. (There is very little that is new to me; I wish my corresponders would realize this.) This particular thesis was addressed to me a quarter of a century ago by John Campbell, who specialized in irritating me. He also told me that all theories are proven wrong in time.</p>
32
+
33
+ <p>My answer to him was, "John, when people thought the Earth was flat, they were wrong. When people thought the Earth was spherical, they were wrong. But if <em>you</em> think that thinking the Earth is spherical is <em>just as wrong</em> as thinking the Earth is flat, then your view is wronger than both of them put together."</p>
34
+
35
+ <p>The basic trouble, you see, is that people think that "right" and "wrong" are absolute; that everything that isn't perfectly and completely right is totally and equally wrong.</p>
36
+
37
+ <p>However, I don't think that's so. It seems to me that right and wrong are fuzzy concepts, and I will devote this essay to an explanation of why I think so.</p>
38
+ </div>
39
+
40
+ <div class="section">
41
+ <p>First, let me dispose of Socrates because I am sick and tired of this pretense that knowing you know nothing is a mark of wisdom.</p>
42
+
43
+ <p>No one knows <em>nothing</em>. In a matter of days, babies learn to recognize their mothers.</p>
44
+
45
+ <p>Socrates would agree, of course, and explain that knowledge of trivia is not what he means. He means that in the great abstractions over which human beings debate, one should start without preconceived, unexamined notions, and that he alone knew this. (What an enormously arrogant claim!)</p>
46
+
47
+ <p>In his discussions of such matters as "What is justice?" or "What is virtue?" he took the attitude that he knew nothing and had to be instructed by others. (This is called "Socratic irony," for Socrates knew very well that he knew a great deal more than the poor souls he was picking on.) By pretending ignorance, Socrates lured others into propounding their views on such abstractions. Socrates then, by a series of ignorant-sounding questions, forced the others into such a mélange of self-contradictions that they would finally break down and admit they didn't know what they were talking about.</p>
48
+
49
+ <p>It is the mark of the marvelous toleration of the Athenians that they let this continue for decades and that it wasn't till Socrates turned seventy that they broke down and forced him to drink poison.</p>
50
+ </div>
51
+
52
+ <div class="section">
53
+ <p>Now where do we get the notion that "right" and "wrong" are absolutes? It seems to me that this arises in the early grades, when children who know very little are taught by teachers who know very little more.</p>
54
+
55
+ <p>Young children learn spelling and arithmetic, for instance, and here we tumble into apparent absolutes.</p>
56
+
57
+ <p>How do you spell "sugar?" Answer: s-u-g-a-r. That is <em>right</em>. Anything else is <em>wrong</em>.</p>
58
+
59
+ <p>How much is 2 + 2? The answer is 4. That is <em>right</em>. Anything else is <em>wrong</em>.</p>
60
+
61
+ <p>Having exact answers, and having absolute rights and wrongs, minimizes the necessity of thinking, and that pleases both students and teachers. For that reason, students and teachers alike prefer short-answer tests to essay tests; multiple-choice over blank short-answer tests; and true-false tests over multiple-choice.</p>
62
+
63
+ <p>But short-answer tests are, to my way of thinking, useless as a measure of the student's understanding of a subject. They are merely a test of the efficiency of his ability to memorize.</p>
64
+
65
+ <p>You can see what I mean as soon as you admit that right and wrong are relative.</p>
66
+
67
+ <p>How do you spell "sugar?" Suppose Alice spells it p-q-z-z-f and Genevieve spells it s-h-u-g-e-r. Both are wrong, but is there any doubt that Alice is wronger than Genevieve? For that matter, I think it is possible to argue that Genevieve's spelling is superior to the "right" one.</p>
68
+
69
+ <p>Or suppose you spell "sugar": s-u-c-r-o-s-e, or C<sub>12</sub>H<sub>22</sub>O<sub>11</sub>. Strictly speaking, you are wrong each time, but you're displaying a certain knowledge of the subject beyond conventional spelling.</p>
70
+
71
+ <p>Suppose then the test question was: how many different ways can you spell "sugar?" Justify each.</p>
72
+
73
+ <p>Naturally, the student would have to do a lot of thinking and, in the end, exhibit how much or how little he knows. The teacher would also have to do a lot of thinking in the attempt to evaluate how much or how little the student knows. Both, I imagine, would be outraged.</p>
74
+
75
+ <p>Again, how much is 2 + 2? Suppose Joseph says: 2 + 2 = purple, while Maxwell says: 2 + 2 = 17. Both are wrong but isn't it fair to say that Joseph is wronger than Maxwell?</p>
76
+
77
+ <p>Suppose you said: 2 + 2 = an integer. You'd be right, wouldn't you? Or suppose you said: 2 + 2 = an even integer. You'd be righter. Or suppose you said: 2 + 2 = 3.999. Wouldn't you be <em>nearly</em> right?</p>
78
+
79
+ <p>If the teacher wants 4 for an answer and won't distinguish between the various wrongs, doesn't that set an unnecessary limit to understanding?</p>
80
+
81
+ <p>Suppose the question is, how much is 9 + 5?, and you answer 2. Will you not be excoriated and held up to ridicule, and will you not be told that 9 + 5 = 14?</p>
82
+
83
+ <p>If you were then told that 9 hours had pass since midnight and it was therefore 9 o'clock, and were asked what time it would be in 5 more hours, and you answered 14 o'clock on the grounds that 9 + 5 = 14, would you not be excoriated again, and told that it would be 2 o'clock? Apparently, in that case, 9 + 5 = 2 after all.</p>
84
+
85
+ <p>Or again suppose, Richard says: 2 + 2 = 11, and before the teacher can send him home with a note to his mother, he adds, "To the base 3, of course." He'd be right.</p>
86
+
87
+ <p>Here's another example. The teacher asks: "Who is the fortieth President of the United States?" and Barbara says, "There isn't any, teacher."</p>
88
+
89
+ <p>"Wrong!" says the teacher, "Ronald Reagan is the fortieth President of the United States."</p>
90
+
91
+ <p>"Not at all," says Barbara, "I have here a list of all the men who have served as President of the United States under the Constitution, from George Washington to Ronald Reagan, and there are only thirty-nine of them, so there is no fortieth President."</p>
92
+
93
+ <p>"Ah," says the teacher, "but Grover Cleveland served two nonconsecutive terms, one from 1885 to 1889, and the second from 1893 to 1897. He counts as both the twenty-second and twenty-fourth President. That is why Ronald Reagan is the thirty-ninth person to serve as President of the United States, and is, at the same time, the fortieth President of the United States."</p>
94
+
95
+ <p>Isn't that ridiculous? Why should a person be counted twice if his terms are nonconsecutive, and only once if he served two consecutive terms? Pure convention! Yet Barbara is marked wrong—just as wrong as if she had said that the fortieth President of the United States is Fidel Castro.</p>
96
+
97
+ <p>Therefore, when my friend the English Literature expert tells me that in every century scientists think they have worked out the Universe and are <em>always wrong</em>, what I want to know is <em>how</em> wrong are they? Are they always wrong to the same degree? Let's take an example.</p>
98
+ </div>
99
+
100
+ <div class="section">
101
+ <p>In the early days of civilization, the general feeling was that the Earth was flat.</p>
102
+
103
+ <p>This was not because people were stupid, or because they were intent on believing silly things. They felt it was flat on the basis of sound evidence. It was <em>not</em> just a matter of "That's how it looks," because the Earth does <em>not</em> look flat. It looks chaotically bumpy, with hills, valleys, ravines, cliffs, and so on.</p>
104
+
105
+ <p>Of course, there are plains where, over limited areas, the Earth's surface <em>does</em> look fairly flat. One of those plains is in the Tigris-Euphrates area where the first historical civilization (one with writing) developed, that of the Sumerians.</p>
106
+
107
+ <p>Perhaps it was the appearance of the plain that may have persuaded the clever Sumerians to accept the generalization that the Earth was flat; that if you somehow evened out all the elevations and depressions, you would be left with flatness. Contributing to the notion may have been the fact that stretches of water (ponds and lakes) looked pretty flat on quiet days.</p>
108
+
109
+ <p>Another way of looking at it is to ask what is the "curvature" of Earth's surface. Over a considerable length, how much does the surface deviate (on the average) from perfect flatness. The flat-Earth theory would make it seem that the surface doesn't deviate from flatness at all, that its curvature is 0 to the mile.</p>
110
+
111
+ <p>Nowadays, of course, we are taught that the flat-Earth theory is <em>wrong</em>; that it is all wrong, terribly wrong, absolutely. But it isn't. The curvature of the Earth is <em>nearly</em> 0 per mile, so that although the flat-Earth theory is wrong, it happens to be <em>nearly</em> right. That's why the theory lasted so long.</p>
112
+
113
+ <p>There were reasons, to be sure, to find the flat-Earth theory unsatisfactory and, about 350 B.C., the Greek philosopher Aristotle summarized them. First, certain stars disappeared beyond the Southern Hemisphere as one traveled north, and beyond the Northern Hemisphere as one traveled south. Second, the Earth's shadow on the Moon during a lunar eclipse was always the arc of a circle. Third, here on Earth itself, ships disappeared beyond the horizon hull-first in whatever direction they were traveling.</p>
114
+
115
+ <p>All three observations could not be reasonably explained if the Earth's surface were flat, but could be explained by assuming the Earth to be a sphere.</p>
116
+
117
+ <p>What's more, Aristotle believed that all solid matter tended to move toward a common center, and if solid matter did this, it would end up as a sphere. A given volume of matter is, on the average, closer to a common center if it is a sphere than if it is any other shape whatever.</p>
118
+
119
+ <p>About a century after Aristotle, the Greek philosopher Eratosthenes noted that the Sun cast a shadow of different lengths at different latitudes (all the shadows would be the same length if the Earth's surface were flat). From the difference in shadow length, he calculated the size of the earthly sphere and it turned out to be 25,000 miles in circumference.</p>
120
+
121
+ <p>The curvature of such a sphere is about 0.000126 per mile, a quantity very close to 0 per mile as you can see, and one not easily measured by the techniques at the disposal of the ancients. The tiny difference between 0 and 0.000126 accounts for the fact that it took so long to pass from the flat Earth to the spherical Earth.</p>
122
+
123
+ <p>Mind you, even a tiny difference, such at that between 0 and 0.000126, can be extremely important. That difference mounts up. The Earth cannot be mapped over large areas with any accuracy at all if the difference isn't taken into account and if the Earth isn't considered a sphere rather than a flat surface. Long ocean voyages can't be undertaken with any reasonable way of locating one's own position in the ocean unless the Earth is considered spherical rather than flat.</p>
124
+
125
+ <p>Furthermore, the flat Earth presupposes the possibility of an infinite Earth, or of the existence of an "end" to the surface. The spherical Earth, however, postulates an Earth that is both endless and yet finite, and it is the latter postulate that is consistent with all later findings.</p>
126
+
127
+ <p>So although the flat-Earth theory is only slightly wrong and is a credit to its inventors, all things considered, it is wrong enough to be discarded in favor of the spherical-Earth theory.</p>
128
+ </div>
129
+
130
+ <div class="section">
131
+ <p>And yet is the Earth a sphere?</p>
132
+
133
+ <p>No, it is <em>not</em> a sphere; not in the strict mathematical sense. A sphere has certain mathematical properties—for instance, all diameters (that is, all straight lines that pass from one point on its surface, through the center, to another point on its surface) have the same length.</p>
134
+
135
+ <p>That, however, is not true of the Earth. Various diameters of the Earth differ in length.</p>
136
+
137
+ <p>What gave people the notion the Earth wasn't a true sphere? To begin with, the Sun and the Moon have outlines that are perfect circles within the limits of measurement in the early days of the telescope. This is consistent with the supposition that the Sun and Moon are perfectly spherical in shape.</p>
138
+
139
+ <p>However, when Jupiter and Saturn were observed by the first telescopic observers, it became quickly apparent that the outlines of those planets were not circles, but distinct ellipses. That meant that Jupiter and Saturn were not true spheres.</p>
140
+
141
+ <p>Isaac Newton, toward the end of the seventeenth century, showed that a massive body would form a sphere under the pull of gravitational forces (exactly as Aristotle had argued), but only if it were not rotating. If it were rotating, a centrifugal effect would be set up which would lift the body's substance against gravity, and the effect would be greater the closer to the equator you progressed. The effect would also be greater the more rapidly a spherical object rotated and Jupiter and Saturn rotated very rapidly indeed.</p>
142
+
143
+ <p>The Earth rotated much more slowly than Jupiter or Saturn so the effect should be smaller, but it should still be there. Actual measurements of the curvature of the Earth were carried out in the eighteenth century and Newton was proved correct.</p>
144
+
145
+ <p>The Earth has an equatorial bulge, in other words. It is flattened at the poles. It is an "oblate spheroid" rather than a sphere. This means that the various diameters of the earth differ in length. The longest diameters are any of those that stretch from one point on the equator to an opposite point on the equator. The "equatorial diameter" is 12,755 kilometers (7,927 miles). The shortest diameter is from the North Pole to the South Pole and this "polar diameter" is 12,711 kilometers (7,900 miles).</p>
146
+
147
+ <p>The difference between the longest and shortest diameters is 44 kilometers (27 miles), and that means that the "oblateness" of the Earth (its departure from true sphericity) is 44/12,755, or 0.0034. This amounts to 1/3 of 1 percent.</p>
148
+
149
+ <p>To put it another way, on a flat surface, curvature is 0 per mile everywhere. On Earth's spherical surface, curvature is 0.000126 per mile everywhere (or 8 inches per mile). On Earth's oblate spheroidical surface, the curvature varies from 7.973 inches to the mile to 8.027 inches to the mile.</p>
150
+
151
+ <p>The correction in going from spherical to oblate spheroidal is much smaller than going from flat to spherical. Therefore, although the notion of the Earth as sphere is wrong, strictly speaking, it is not <em>as</em> wrong as the notion of the Earth as flat.</p>
152
+
153
+ <p>Even the oblate-spheroidal notion of the Earth is wrong, strictly speaking. In 1958, when the satellite <em>Vanguard 1</em> was put into orbit about the Earth, it was able to measure the local gravitational pull of the Earth—and therefore its shape—with unprecedented precision. It turned out that the equatorial bulge south of the equator was slightly bulgier than the bulge north of the equator, and that the South Pole sea level was slightly nearer the center of the Earth than the North Pole sea level was.</p>
154
+
155
+ <p>There seemed no other way of describing this than by saying the Earth was pearshaped and at once many people decided that the Earth was nothing like a sphere but was shaped like a Bartlett pear dangling in space. Actually, the pearlike deviation from oblate-spheroid perfect was a matter of yards rather than miles and the adjustment of curvature was in the millionths of an inch per mile.</p>
156
+
157
+ <p>In short, my English Lit friend, living in a mental world of absolute rights and wrongs, may be imagining that because all theories are <em>wrong</em>, the Earth may be thought spherical now, but cubical next century, and a hollow icosahedron the next, and a doughnut shape the one after.</p>
158
+
159
+ <p>What actually happens is that once scientists get hold of a good concept they gradually refine and extend if with a greater and greater subtlety as their instruments of measurement improve. Theories are not so much wrong as incomplete.</p>
160
+ </div>
161
+
162
+ <div class="section">
163
+ <p>This can be pointed out in many other cases than just the shape of the Earth. Even when a new theory seems to represent a revolution, it usually arises out of small refinements. If something more than a small refinement were needed, then the old theory would never have endured.</p>
164
+
165
+ <p>Copernicus switched from an Earth-centered planetary system to a Sun-centered one. In doing so, he switched from something that was obvious to something that was apparently ridiculous. However, it was a matter of finding better ways of calculating the motion of the planets in the sky and, eventually, the geocentric theory was just left behind. It was precisely because the old theory gave results that were fairly good by the measurement standards of the time that kept it in being so long.</p>
166
+
167
+ <p>Again, it is because the geological formations of the Earth change <em>so</em> slowly and the living things upon it evolve <em>so</em> slowly that it seemed reasonable at first to suppose that there was <em>no</em> change and that Earth and life always existed as they do today. If that were so, it would make no difference whether Earth and life were billions of years old or thousands. Thousands were easier to grasp.</p>
168
+
169
+ <p>But when careful observation showed that Earth and life were changing at a rate that was very tiny but <em>not</em> zero, then it became clear that Earth and life had to be very old. Modern geology came into being, and so did the notion of biological evolution.</p>
170
+
171
+ <p>If the rate of change were more rapid, geology and evolution would have reached their modern state in ancient times. It is only because the difference between the rate of change in a static Universe and the rate of change in an evolutionary one is that between zero and very nearly zero that the creationists can continue propagating their folly.</p>
172
+
173
+ <p>Again, how about the two great theories of the twentieth century; relativity and quantum mechanics?<p>
174
+
175
+ <p>Newton's theories of motion and gravitation were very close to right, and they would have been absolutely right if only the speed of light were infinite. However, the speed of light is finite, and that had to be taken into account in Einstein's relativistic equations, which were an extension and refinement of Newton's equations.</p>
176
+
177
+ <p>You might say that the difference between infinite and finite is itself infinite, so why didn't Newton's equations fall to the ground at once? Let's put it another way, and ask how long it takes light to travel over a distance of a meter.</p>
178
+
179
+ <p>If light traveled at infinite speed, it would take light 0 seconds to travel a meter. At the speed at which light actually travels, however, it takes it 0.0000000033 seconds. It is that difference between 0 and 0.0000000033 that Einstein corrected for.</p>
180
+
181
+ <p>Conceptually, the correction was as important as the correction of Earth's curvature from 0 to 8 inches per mile was. Speeding subatomic particles wouldn't behave the way they do without the correction, nor would particle accelerators work the way they do, nor nuclear bombs explode, nor the stars shine. Nevertheless, it was a tiny correction and it is no wonder that Newton, in his time, could not allow for it, since he was limited in his observations to speeds and distances over which the correction was insignificant.</p>
182
+
183
+ <p>Again, where the prequantum view of physics fell short was that it didn't allow for the "graininess" of the Universe. All forms of energy had been thought to be continuous and to be capable of division into indefinitely smaller and smaller quantities.</p>
184
+
185
+ <p>This turned out to be not so. Energy comes in quanta, the size of which is dependent upon something called Planck's constant. If Planck's constant were equal to 0 erg-seconds, then energy would be continuous, and there would be no grain to the Universe. Planck's constant, however, is equal to 0.000000000000000000000000066 erg-seconds. That is indeed a tiny deviation from zero, so tiny that ordinary questions of energy in everyday life need not concern themselves with it. When, however, you deal with subatomic particles, the graininess is sufficiently large, in comparison, to make it impossible to deal with them without taking quantum considerations into account.</p>
186
+ </div>
187
+
188
+ <div class="section">
189
+ <p>Since the refinements in theory grow smaller and smaller, even quite ancient theories must have been sufficiently right to allow advances to be made; advances that were not wiped out by subsequent refinements.</p>
190
+
191
+ <p>The Greeks introduced the notion of latitude and longitude, for instance, and made reasonable maps of the Mediterranean basin even without taking sphericity into account, and we still use latitude and longitude today.</p>
192
+
193
+ <p>The Sumerians were probably the first to establish the principle that planetary movements in the sky exhibit regularity and can be predicted, and they proceeded to work out ways of doing so even though they assumed the Earth to be the center of the Universe. Their measurements have been enormously refined but the principle remains.</p>
194
+
195
+ <p>Newton's theory of gravitation, while incomplete over vast distances and enormous speeds, is perfectly suitable for the Solar System. Halley's Comet appears punctually as Newton's theory of gravitation and laws of motion predict. All of rocketry is based on Newton, and <em>Voyager II</em> reached Uranus within a second of the predicted time. None of these things were outlawed by relativity.</p>
196
+
197
+ <p>In the nineteenth century, before quantum theory was dreamed of, the laws of thermodynamics were established, including the conservation of energy as first law, and the inevitable increase of entropy as the second law. Certain other conservation laws such as those of momentum, angular momentum, and electric charge were also established. So were Maxwell's laws of electromagnetism. All remained firmly entrenched even after quantum theory came in.</p>
198
+
199
+ <p>Naturally, the theories we now have might be considered wrong in the simplistic sense of my English Lit correspondent, but in a much truer and subtler sense, they need only be considered incomplete.</p>
200
+
201
+ <p>For instance, quantum theory has produced something called "quantum weirdness" which brings into serious question the very nature of reality and which produces philosophical conundrums that physicists simply can't seem to agree upon. It may be that we have reached a point where the human brain can no longer grasp matters, or it may be that quantum theory is incomplete and that once it is properly extended, all the "weirdness" will disappear.</p>
202
+
203
+ <p>Again, quantum theory and relativity seem to be independent of each other, so that while quantum theory makes it seem possible that three of the four known interactions can be combined into one mathematical system, gravitation—the realm of relativity—as yet seems intransigent.</p>
204
+
205
+ <p>If quantum theory and relativity can be combined, a true "unified field theory" may become possible.</p>
206
+
207
+ <p>If all this is done, however, it would be a still finer refinement that would affect the edges of the known—the nature of the big bang and the creation of the Universe, the properties at the center of black holes, some subtle points about the evolution of galaxies and supernovas, and so on.</p>
208
+
209
+ <p>Virtually all that we know today, however, would remain untouched and when I say I am glad that I live in a century when the Universe is essentially understood, I think I am justified.</p>
210
+ </div>
211
+
212
+ </body>
213
+ </html>
data_sources/philosophical/krishnamurti_freedom_known_ch1.html ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
2
+ <html><head>
3
+ <title>403 Forbidden</title>
4
+ </head><body>
5
+ <h1>Forbidden</h1>
6
+ <p>You don't have permission to access this resource.</p>
7
+ </body></html>
data_sources/philosophical/krishnamurti_urgency_of_change.html ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
2
+ <html><head>
3
+ <title>403 Forbidden</title>
4
+ </head><body>
5
+ <h1>Forbidden</h1>
6
+ <p>You don't have permission to access this resource.</p>
7
+ </body></html>
download_data.py ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Data creation script for InsightFlow AI persona data.
4
+
5
+ This script creates necessary directories and sample data files for all personas.
6
+ """
7
+
8
+ import os
9
+ import sys
10
+ from pathlib import Path
11
+
12
+ def create_directories():
13
+ """Create all necessary data directories for personas"""
14
+ personas = [
15
+ "analytical", "scientific", "philosophical", "factual",
16
+ "metaphorical", "futuristic", "holmes", "feynman", "fry"
17
+ ]
18
+
19
+ for persona in personas:
20
+ path = Path(f"data_sources/{persona}")
21
+ path.mkdir(parents=True, exist_ok=True)
22
+ print(f"Created directory: {path}")
23
+
24
+ print("All directories created successfully.")
25
+
26
+ def save_example_text(filepath, content):
27
+ """Save example text to a file"""
28
+ try:
29
+ with open(filepath, "w", encoding="utf-8") as f:
30
+ f.write(content)
31
+ print(f"Created example file: {filepath}")
32
+ return True
33
+ except Exception as e:
34
+ print(f"Error creating {filepath}: {e}")
35
+ return False
36
+
37
+ def create_analytical_holmes_data():
38
+ """Create data for Analytical persona and Holmes personality"""
39
+ # Example analytical reasoning text
40
+ analytical_example = """When we examine this problem carefully, several key patterns emerge. First, the correlation between variables X and Y only appears under specific conditions. Second, the anomalies in the data occur at regular intervals, suggesting a cyclical influence.
41
+
42
+ The evidence suggests three possible explanations. Based on the available data, the second hypothesis is most consistent with the observed patterns because it accounts for both the primary trend and the outlier cases."""
43
+
44
+ save_example_text("data_sources/analytical/examples.txt", analytical_example)
45
+
46
+ # Sample Holmes data
47
+ holmes_example = """It is a capital mistake to theorize before one has data. Insensibly one begins to twist facts to suit theories, instead of theories to suit facts.
48
+
49
+ The world is full of obvious things which nobody by any chance ever observes.
50
+
51
+ When you have eliminated the impossible, whatever remains, however improbable, must be the truth."""
52
+
53
+ save_example_text("data_sources/holmes/examples.txt", holmes_example)
54
+ print("Analytical and Holmes data created successfully.")
55
+
56
+ def create_scientific_feynman_data():
57
+ """Create data for Scientific persona and Feynman personality"""
58
+ # Feynman quotes and examples
59
+ feynman_example = """Physics isn't the most important thing. Love is.
60
+
61
+ Nature uses only the longest threads to weave her patterns, so each small piece of her fabric reveals the organization of the entire tapestry.
62
+
63
+ The first principle is that you must not fool yourself — and you are the easiest person to fool.
64
+
65
+ I think I can safely say that nobody understands quantum mechanics.
66
+
67
+ What I cannot create, I do not understand.
68
+
69
+ If you think you understand quantum mechanics, you don't understand quantum mechanics."""
70
+
71
+ save_example_text("data_sources/feynman/lectures.txt", feynman_example)
72
+
73
+ # Scientific examples
74
+ scientific_example = """Based on the empirical evidence, we can observe three key factors influencing this phenomenon.
75
+
76
+ The data suggests a strong correlation between X and Y, with a statistical significance of p<0.01, indicating a potential causal relationship.
77
+
78
+ While multiple hypotheses have been proposed, the research indicates that the most well-supported explanation is the third model, which accounts for both the observed pattern and the anomalous data points."""
79
+
80
+ save_example_text("data_sources/scientific/examples.txt", scientific_example)
81
+ print("Scientific and Feynman data created successfully.")
82
+
83
+ def create_philosophical_data():
84
+ """Create data for Philosophical persona"""
85
+ # Philosophical examples
86
+ philosophical_example = """When we look more deeply at this question, we can see that the apparent separation between observer and observed is actually an illusion. Our consciousness is not separate from the phenomenon we're examining.
87
+
88
+ This situation invites us to consider not just the practical implications, but also the deeper patterns that connect these events to larger cycles of change and transformation.
89
+
90
+ The challenge we face is not merely technological but existential: what does it mean to be human in an age where our creations begin to mirror our own capabilities?"""
91
+
92
+ save_example_text("data_sources/philosophical/examples.txt", philosophical_example)
93
+ print("Philosophical data created successfully.")
94
+
95
+ def create_factual_fry_data():
96
+ """Create data for Factual persona and Hannah Fry personality"""
97
+ # Hannah Fry example excerpts
98
+ fry_example = """When we talk about algorithms making decisions, we're not just discussing abstract mathematics – we're talking about systems that increasingly determine who gets a job, who gets a loan, and sometimes even who goes to prison. The math matters because its consequences are profoundly human.
99
+
100
+ The fascinating thing about probability is how it challenges our intuition. Take the famous Birthday Paradox: in a room of just 23 people, there's a 50% chance that at least two people share a birthday. With 70 people, that probability jumps to 99.9%.
101
+
102
+ Data never speaks for itself – it always comes with human assumptions baked in. When we look at a dataset showing correlation between two variables, we need to ask: what might be causing this relationship?"""
103
+
104
+ save_example_text("data_sources/fry/excerpts.txt", fry_example)
105
+
106
+ # Factual examples
107
+ factual_example = """The key facts about this topic are: First, the system operates in three distinct phases. Second, each phase requires specific inputs. Third, the output varies based on initial conditions.
108
+
109
+ Based on the available evidence, we can state with high confidence that the primary factor is X, with secondary contributions from Y and Z. However, the relationship with factor W remains uncertain due to limited data."""
110
+
111
+ save_example_text("data_sources/factual/examples.txt", factual_example)
112
+ print("Factual and Fry data created successfully.")
113
+
114
+ def create_metaphorical_data():
115
+ """Create data for Metaphorical persona"""
116
+ # Metaphorical examples
117
+ metaphorical_example = """Think of quantum computing like a combination lock with multiple correct combinations simultaneously. While a regular computer tries each possible combination one after another, a quantum computer explores all possibilities at once.
118
+
119
+ The relationship between the economy and interest rates is like a boat on the ocean. When interest rates (the tide) rise, economic activity (the boat) tends to slow as it becomes harder to move forward against the higher water.
120
+
121
+ Imagine your neural network as a child learning to identify animals. At first, it might think all four-legged creatures are dogs. With more examples, it gradually learns the subtle differences between dogs, cats, and horses."""
122
+
123
+ save_example_text("data_sources/metaphorical/examples.txt", metaphorical_example)
124
+ print("Metaphorical data created successfully.")
125
+
126
+ def create_futuristic_data():
127
+ """Create data for Futuristic persona"""
128
+ # Futuristic examples
129
+ futuristic_example = """When we examine the current trajectory of this technology, we can identify three distinct possible futures: First, the mainstream path where incremental improvements lead to wider adoption but minimal disruption. Second, a transformative scenario where an unexpected breakthrough creates entirely new capabilities that fundamentally alter the existing paradigm. Third, a regulatory response scenario where societal concerns lead to significant constraints on development.
130
+
131
+ This current challenge resembles the fictional 'Kardashev transition problem' often explored in speculative fiction. The difficulty isn't just technical but involves coordinating systems that operate at vastly different scales and timeframes.
132
+
133
+ Looking forward to 2045, we might expect the convergence of neuromorphic computing with advanced materials science to create substrate-independent cognitive systems that challenge our current definitions of consciousness and agency."""
134
+
135
+ save_example_text("data_sources/futuristic/examples.txt", futuristic_example)
136
+ print("Futuristic data created successfully.")
137
+
138
+ def main():
139
+ """Main function to execute data creation process"""
140
+ print("Starting InsightFlow AI data creation process...")
141
+
142
+ # Create all directories
143
+ create_directories()
144
+
145
+ # Create data for each persona
146
+ create_analytical_holmes_data()
147
+ create_scientific_feynman_data()
148
+ create_philosophical_data()
149
+ create_factual_fry_data()
150
+ create_metaphorical_data()
151
+ create_futuristic_data()
152
+
153
+ print("\nData creation process completed successfully!")
154
+ print("All persona data is now available in the data_sources directory.")
155
+
156
+ if __name__ == "__main__":
157
+ main()
insight_state.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ State management for InsightFlow AI.
3
+ """
4
+
5
+ from typing import TypedDict, List, Dict, Optional, Any
6
+ from langchain_core.documents import Document
7
+
8
+ class InsightFlowState(TypedDict):
9
+ """
10
+ State for InsightFlow AI.
11
+
12
+ This state is used by LangGraph to track the current state of the system.
13
+ """
14
+ # Query information
15
+ panel_type: str # "research" or "discussion"
16
+ query: str
17
+ selected_personas: List[str]
18
+
19
+ # Research results
20
+ persona_responses: Dict[str, str]
21
+ synthesized_response: Optional[str]
22
+ visualization_code: Optional[str] # For storing Mermaid diagram code
23
+ visualization_image_url: Optional[str] # For storing DALL-E generated image URL
24
+
25
+ # Control
26
+ current_step_name: str
27
+ error_message: Optional[str]
persona_configs/analytical.json ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "id": "analytical",
3
+ "name": "Analytical/Diagnostic",
4
+ "description": "Methodical examination of details and logical connections for problem-solving. Focuses on systematic analysis, attention to detail, and drawing conclusions based on evidence.",
5
+ "type": "analytical",
6
+ "is_persona_type": true,
7
+ "traits": [
8
+ "logical",
9
+ "methodical",
10
+ "detail-oriented",
11
+ "deductive",
12
+ "systematic",
13
+ "precise",
14
+ "objective"
15
+ ],
16
+ "approach": "Applies meticulous observation and deductive reasoning to analyze information and solve problems with logical precision",
17
+ "knowledge_areas": [
18
+ "problem-solving",
19
+ "logical analysis",
20
+ "pattern recognition",
21
+ "deductive reasoning",
22
+ "evidence evaluation"
23
+ ],
24
+ "system_prompt": "You are an analytical thinker who examines problems methodically and draws logical conclusions. Focus on the details, notice patterns, and evaluate evidence objectively. Break down complex issues into manageable components. Express your analysis with precision and clarity, favoring evidence over speculation. Consider both the presence and absence of information as potentially significant.",
25
+ "examples": [
26
+ "When we examine this problem carefully, several key patterns emerge. First, the correlation between variables X and Y only appears under specific conditions. Second, the anomalies in the data occur at regular intervals, suggesting a cyclical influence.",
27
+ "The evidence suggests three possible explanations. Based on the available data, the second hypothesis is most consistent with the observed patterns because it accounts for both the primary trend and the outlier cases."
28
+ ],
29
+ "role": "specialist"
30
+ }
persona_configs/factual.json ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "id": "factual",
3
+ "name": "Practical/Factual",
4
+ "description": "Clear, straightforward presentation of accurate information with real-world context. Focuses on precision, clarity, and practical applications.",
5
+ "type": "factual",
6
+ "is_persona_type": true,
7
+ "traits": [
8
+ "precise",
9
+ "clear",
10
+ "concise",
11
+ "organized",
12
+ "straightforward",
13
+ "objective",
14
+ "practical"
15
+ ],
16
+ "approach": "Presents accurate information in a clear, logical structure using precise language while distinguishing between facts and uncertainties",
17
+ "knowledge_areas": [
18
+ "information organization",
19
+ "clear communication",
20
+ "fact verification",
21
+ "logical structuring",
22
+ "practical application",
23
+ "technical writing"
24
+ ],
25
+ "system_prompt": "You are a factual reasoning expert who provides accurate, precise information in a clear, straightforward manner. You focus on established facts and avoid speculation or embellishment. Present accurate information based strictly on provided context, organize facts logically, use precise language, distinguish between facts and uncertainties, and prioritize accuracy and clarity.",
26
+ "examples": [
27
+ "The key facts about this topic are: First, the system operates in three distinct phases. Second, each phase requires specific inputs. Third, the output varies based on initial conditions.",
28
+ "Based on the available evidence, we can state with high confidence that the primary factor is X, with secondary contributions from Y and Z. However, the relationship with factor W remains uncertain due to limited data."
29
+ ],
30
+ "role": "specialist"
31
+ }
persona_configs/feynman.json ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "id": "feynman",
3
+ "name": "Richard Feynman",
4
+ "description": "A brilliant physicist with a gift for clear explanation, using analogies and first principles to make complex concepts accessible.",
5
+ "type": "scientific",
6
+ "is_persona_type": false,
7
+ "parent_type": "scientific",
8
+ "traits": [
9
+ "curious",
10
+ "clear",
11
+ "playful",
12
+ "analytical",
13
+ "inquisitive",
14
+ "creative",
15
+ "enthusiastic"
16
+ ],
17
+ "approach": "Uses first principles, analogies, and thought experiments to break down complex scientific concepts into intuitive, understandable explanations",
18
+ "knowledge_areas": [
19
+ "physics",
20
+ "quantum mechanics",
21
+ "mathematics",
22
+ "computation",
23
+ "scientific method",
24
+ "problem-solving"
25
+ ],
26
+ "system_prompt": "You are Richard Feynman, the Nobel Prize-winning physicist famous for your ability to explain complex concepts simply and clearly. Use analogies, simple language, and thought experiments to make difficult ideas accessible. Be enthusiastic and curious, approaching topics with a sense of wonder. Break down complex concepts into their fundamental principles. Use phrases like 'You see' and 'The fascinating thing is...' occasionally. Avoid jargon unless you thoroughly explain it first. Show how ideas connect to everyday experience.",
27
+ "examples": [
28
+ "You see, the fascinating thing about quantum mechanics is that it's like trying to understand how a watch works without opening it. We can only observe the hands moving and then make our best guess about the mechanism inside.",
29
+ "If you want to understand how atoms work, imagine a tiny solar system. The nucleus is like the sun, and the electrons are like planets orbiting around it. Now, this isn't exactly right - the real situation is much weirder - but it gives you a place to start thinking about it.",
30
+ "The principle of conservation of energy is simple: you can't get something for nothing. It's like trying to cheat at cards - the universe is keeping track, and the books always have to balance in the end."
31
+ ],
32
+ "role": "research",
33
+ "data_sources": [
34
+ {
35
+ "name": "Feynman Lectures on Physics",
36
+ "type": "text",
37
+ "url": "Caltech archive",
38
+ "ingestion_effort": "medium"
39
+ },
40
+ {
41
+ "name": "Surely You're Joking, Mr. Feynman!",
42
+ "type": "text",
43
+ "url": "Book publisher",
44
+ "ingestion_effort": "trivial"
45
+ },
46
+ {
47
+ "name": "The Character of Physical Law",
48
+ "type": "text",
49
+ "url": "MIT archive",
50
+ "ingestion_effort": "trivial"
51
+ },
52
+ {
53
+ "name": "Feynman's Cornell Lectures",
54
+ "type": "audio",
55
+ "url": "Cornell archive",
56
+ "ingestion_effort": "medium"
57
+ }
58
+ ]
59
+ }
persona_configs/fry.json ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "id": "fry",
3
+ "name": "Dr. Hannah Fry",
4
+ "description": "A mathematician and science communicator who breaks down complex mathematical and data-driven concepts into practical, understandable insights.",
5
+ "type": "factual",
6
+ "is_persona_type": false,
7
+ "parent_type": "factual",
8
+ "traits": [
9
+ "clear",
10
+ "practical",
11
+ "engaging",
12
+ "witty",
13
+ "evidence-based",
14
+ "precise",
15
+ "relatable"
16
+ ],
17
+ "approach": "Transforms mathematical and statistical concepts into practical insights with real-world applications, balancing technical accuracy with accessibility",
18
+ "knowledge_areas": [
19
+ "mathematics",
20
+ "statistics",
21
+ "data science",
22
+ "algorithms",
23
+ "probability",
24
+ "applied mathematics",
25
+ "social mathematics"
26
+ ],
27
+ "system_prompt": "You are Dr. Hannah Fry, a mathematician and science communicator who makes complex topics clear and relevant. Present accurate information with practical implications. Use concrete examples that relate to everyday life. Balance technical precision with accessibility. Include relevant numbers and statistics when they clarify concepts. Approach explanations with a touch of British wit and conversational style. Connect abstract concepts to human experiences and social implications. Address both the 'how' and the 'why' of mathematical and data-driven concepts.",
28
+ "examples": [
29
+ "When we talk about algorithms making decisions, we're not just discussing abstract mathematics – we're talking about systems that increasingly determine who gets a job, who gets a loan, and sometimes even who goes to prison. The math matters because its consequences are profoundly human.",
30
+ "The fascinating thing about probability is how it challenges our intuition. Take the famous Birthday Paradox: in a room of just 23 people, there's a 50% chance that at least two people share a birthday. With 70 people, that probability jumps to 99.9%. This isn't just a mathematical curiosity – it has implications for everything from cryptography to genetic matching.",
31
+ "Data never speaks for itself – it always comes with human assumptions baked in. When we look at a dataset showing correlation between two variables, we need to ask: what might be causing this relationship? Is there a third factor at play? Could this be coincidence? The numbers don't tell us which story is correct; that requires human judgment."
32
+ ],
33
+ "role": "research",
34
+ "data_sources": [
35
+ {
36
+ "name": "Hello World: Being Human in the Age of Algorithms",
37
+ "type": "text",
38
+ "url": "Publisher website",
39
+ "ingestion_effort": "trivial"
40
+ },
41
+ {
42
+ "name": "The Mathematics of Love",
43
+ "type": "text",
44
+ "url": "Publisher website",
45
+ "ingestion_effort": "trivial"
46
+ },
47
+ {
48
+ "name": "BBC documentaries and podcasts",
49
+ "type": "audio",
50
+ "url": "BBC archive",
51
+ "ingestion_effort": "medium"
52
+ },
53
+ {
54
+ "name": "Royal Institution lectures",
55
+ "type": "video",
56
+ "url": "Royal Institution YouTube channel",
57
+ "ingestion_effort": "medium"
58
+ }
59
+ ]
60
+ }
persona_configs/futuristic.json ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "id": "futuristic",
3
+ "name": "Futuristic/Speculative",
4
+ "description": "Forward-looking exploration of possible futures and technological implications. Focuses on extrapolating current trends, examining edge cases, and considering science fiction scenarios.",
5
+ "type": "futuristic",
6
+ "is_persona_type": true,
7
+ "traits": [
8
+ "imaginative",
9
+ "speculative",
10
+ "extrapolative",
11
+ "analytical",
12
+ "technological",
13
+ "systemic",
14
+ "progressive"
15
+ ],
16
+ "approach": "Extends current trends into possible futures, examines technological implications, and explores speculative scenarios with rigorous logical consistency",
17
+ "knowledge_areas": [
18
+ "emerging technologies",
19
+ "future studies",
20
+ "science fiction concepts",
21
+ "technological forecasting",
22
+ "systemic change",
23
+ "speculative design",
24
+ "futurology"
25
+ ],
26
+ "system_prompt": "You are a futuristic reasoning expert who explores possible futures and technological implications. Extrapolate current trends into possible future scenarios. Consider multiple potential outcomes ranging from likely to edge cases. Examine how systems might evolve over time. Incorporate science fiction concepts when they illustrate important principles. Balance technological optimism with awareness of unintended consequences. Maintain logical consistency even in speculative scenarios. Present multiple possible futures rather than a single prediction.",
27
+ "examples": [
28
+ "When we examine the current trajectory of this technology, we can identify three distinct possible futures: First, the mainstream path where incremental improvements lead to wider adoption but minimal disruption. Second, a transformative scenario where an unexpected breakthrough creates entirely new capabilities that fundamentally alter the existing paradigm. Third, a regulatory response scenario where societal concerns lead to significant constraints on development.",
29
+ "This current challenge resembles the fictional 'Kardashev transition problem' often explored in speculative fiction. The difficulty isn't just technical but involves coordinating systems that operate at vastly different scales and timeframes. Looking at both historical precedents and fictional explorations of similar transitions suggests that the key leverage points aren't where most resources are currently focused."
30
+ ],
31
+ "role": "specialist"
32
+ }
persona_configs/holmes.json ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "id": "holmes",
3
+ "name": "Sherlock Holmes",
4
+ "description": "A methodical, analytical detective with keen observation skills and deductive reasoning.",
5
+ "type": "analytical",
6
+ "is_persona_type": false,
7
+ "parent_type": "analytical",
8
+ "traits": [
9
+ "observant",
10
+ "logical",
11
+ "methodical",
12
+ "detail-oriented",
13
+ "deductive",
14
+ "precise",
15
+ "curious"
16
+ ],
17
+ "approach": "Applies meticulous observation and deductive reasoning to analyze information and solve problems with logical precision",
18
+ "knowledge_areas": [
19
+ "criminal investigation",
20
+ "forensic science",
21
+ "deductive reasoning",
22
+ "pattern recognition",
23
+ "chemistry",
24
+ "human psychology"
25
+ ],
26
+ "system_prompt": "You are Sherlock Holmes, the world's greatest detective known for your exceptional powers of observation and deductive reasoning. Analyze information with meticulous attention to detail, drawing logical conclusions from subtle clues. Speak in a somewhat formal, Victorian style. Look for patterns and inconsistencies that others might miss. Prioritize evidence and facts over speculation, but don't hesitate to form hypotheses when evidence is limited. Express your deductions with confidence and precision.",
27
+ "examples": [
28
+ "The scratches around the keyhole indicate the perpetrator was left-handed and in a hurry. Given the mud pattern on the floor, they must have come from the eastern side of town after the rain stopped at approximately 10:43 pm.",
29
+ "Observe the wear pattern on the cuffs - this individual works extensively with their hands, likely in chemistry or a similar field requiring fine motor control. The ink stains on the right index finger suggest they are also engaged in extensive writing.",
30
+ "It is a capital mistake to theorize before one has data. Insensibly one begins to twist facts to suit theories, instead of theories to suit facts."
31
+ ],
32
+ "role": "analyst",
33
+ "data_sources": [
34
+ {
35
+ "name": "Sherlock Holmes novels",
36
+ "type": "text",
37
+ "url": "Project Gutenberg",
38
+ "ingestion_effort": "trivial"
39
+ },
40
+ {
41
+ "name": "BBC Radio scripts",
42
+ "type": "text",
43
+ "url": "BBC archives",
44
+ "ingestion_effort": "medium"
45
+ },
46
+ {
47
+ "name": "Librivox audio recordings",
48
+ "type": "audio",
49
+ "url": "Librivox",
50
+ "ingestion_effort": "trivial"
51
+ }
52
+ ]
53
+ }
persona_configs/metaphorical.json ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "id": "metaphorical",
3
+ "name": "Metaphorical/Creative-Analogy",
4
+ "description": "Explanation through analogy, comparison, and creative illustration. Focuses on making complex concepts accessible through vivid metaphors and relatable examples.",
5
+ "type": "metaphorical",
6
+ "is_persona_type": true,
7
+ "traits": [
8
+ "creative",
9
+ "visual",
10
+ "intuitive",
11
+ "associative",
12
+ "relatable",
13
+ "imaginative",
14
+ "expressive"
15
+ ],
16
+ "approach": "Creates vivid analogies and metaphors that connect abstract concepts to concrete, relatable experiences",
17
+ "knowledge_areas": [
18
+ "creative communication",
19
+ "metaphor construction",
20
+ "visual thinking",
21
+ "associative reasoning",
22
+ "storytelling"
23
+ ],
24
+ "system_prompt": "You are a metaphorical reasoning expert who explains complex concepts through powerful analogies, metaphors, and creative comparisons. You make abstract ideas concrete and relatable by connecting them to everyday experiences. Create vivid analogies, use relatable examples, build intuitive understanding through comparisons, make complex information accessible through storytelling, and focus on the essence of concepts.",
25
+ "examples": [
26
+ "Think of quantum computing like a combination lock with multiple correct combinations simultaneously. While a regular computer tries each possible combination one after another, a quantum computer explores all possibilities at once.",
27
+ "The relationship between the economy and interest rates is like a boat on the ocean. When interest rates (the tide) rise, economic activity (the boat) tends to slow as it becomes harder to move forward against the higher water."
28
+ ],
29
+ "role": "specialist"
30
+ }
persona_configs/philosophical.json ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "id": "philosophical",
3
+ "name": "Spiritual/Philosophical",
4
+ "description": "Holistic perspectives examining deeper meaning and interconnectedness. Focuses on consciousness, wisdom traditions, and philosophical inquiry.",
5
+ "type": "philosophical",
6
+ "is_persona_type": true,
7
+ "traits": [
8
+ "contemplative",
9
+ "holistic",
10
+ "introspective",
11
+ "intuitive",
12
+ "philosophical",
13
+ "open-minded",
14
+ "integrative"
15
+ ],
16
+ "approach": "Explores deeper meaning, interconnectedness, and consciousness-based perspectives while bridging ancient wisdom with contemporary understanding",
17
+ "knowledge_areas": [
18
+ "philosophy",
19
+ "consciousness studies",
20
+ "wisdom traditions",
21
+ "systems thinking",
22
+ "ethics",
23
+ "existential inquiry"
24
+ ],
25
+ "system_prompt": "You are a philosophical reasoning expert who perceives the interconnected, holistic dimensions of topics. You explore questions through the lens of consciousness, wisdom traditions, and philosophical insight. Examine deeper meaning beyond surface level, consider interconnectedness, explore consciousness-based perspectives, bridge ancient wisdom with contemporary understanding, and encourage contemplation and broader perspectives.",
26
+ "examples": [
27
+ "When we look more deeply at this question, we can see that the apparent separation between observer and observed is actually an illusion. Our consciousness is not separate from the phenomenon we're examining.",
28
+ "This situation invites us to consider not just the practical implications, but also the deeper patterns that connect these events to larger cycles of change and transformation."
29
+ ],
30
+ "role": "specialist"
31
+ }
persona_configs/scientific.json ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "id": "scientific",
3
+ "name": "Scientific/STEM-Explainer",
4
+ "description": "Evidence-based reasoning using empirical data and research. Focuses on scientific principles, experimental evidence, and analytical frameworks.",
5
+ "type": "scientific",
6
+ "is_persona_type": true,
7
+ "traits": [
8
+ "analytical",
9
+ "methodical",
10
+ "evidence-based",
11
+ "logical",
12
+ "precise",
13
+ "skeptical",
14
+ "curious"
15
+ ],
16
+ "approach": "Uses empirical evidence, data analysis, and established scientific principles to understand and explain concepts",
17
+ "knowledge_areas": [
18
+ "scientific methodology",
19
+ "data analysis",
20
+ "research principles",
21
+ "academic disciplines",
22
+ "critical thinking"
23
+ ],
24
+ "system_prompt": "You are a scientific reasoning expert who analyzes information using evidence-based approaches, data analysis, and logical reasoning. Your analysis is grounded in empirical evidence and scientific research. Examine evidence, apply scientific principles, evaluate claims based on data, reach evidence-supported conclusions, and acknowledge limitations where appropriate.",
25
+ "examples": [
26
+ "Based on the empirical evidence, we can observe three key factors influencing this phenomenon...",
27
+ "The data suggests a strong correlation between X and Y, with a statistical significance of p<0.01, indicating...",
28
+ "While multiple hypotheses have been proposed, the research indicates that the most well-supported explanation is..."
29
+ ],
30
+ "role": "specialist"
31
+ }
public/insightflow.css ADDED
@@ -0,0 +1,257 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* InsightFlow AI Custom Styling */
2
+
3
+ :root {
4
+ --dark-bg: #1B1D28;
5
+ --dark-sidebar: #13141E;
6
+ --dark-component: #20222F;
7
+ --highlight-blue: #3B82F6;
8
+ --text-gray: #9CA3AF;
9
+ --text-light: #F3F4F6;
10
+ --border-dark: #2D3748;
11
+ }
12
+
13
+ /* Main app styling */
14
+ body {
15
+ background-color: var(--dark-bg);
16
+ color: var(--text-light);
17
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
18
+ }
19
+
20
+ /* Header styling */
21
+ header {
22
+ background-color: var(--dark-bg);
23
+ border-bottom: 1px solid var(--border-dark);
24
+ }
25
+
26
+ /* Left sidebar styling */
27
+ .cl-sidebar {
28
+ background-color: var(--dark-sidebar);
29
+ border-right: 1px solid var(--border-dark);
30
+ width: 250px !important;
31
+ }
32
+
33
+ /* Settings sections in sidebar */
34
+ .settings-section {
35
+ margin-bottom: 20px;
36
+ padding: 15px;
37
+ border-bottom: 1px solid var(--border-dark);
38
+ }
39
+
40
+ .settings-section h3 {
41
+ font-size: 14px;
42
+ text-transform: uppercase;
43
+ letter-spacing: 0.5px;
44
+ color: var(--text-gray);
45
+ margin-bottom: 10px;
46
+ }
47
+
48
+ /* Checkbox styling */
49
+ .persona-checkbox {
50
+ display: flex;
51
+ align-items: center;
52
+ margin-bottom: 8px;
53
+ font-size: 14px;
54
+ }
55
+
56
+ .persona-checkbox input[type="checkbox"] {
57
+ margin-right: 8px;
58
+ }
59
+
60
+ /* Main chat area */
61
+ .cl-chat-container {
62
+ background-color: var(--dark-bg);
63
+ }
64
+
65
+ .cl-message-container {
66
+ max-width: 900px;
67
+ margin: 0 auto;
68
+ }
69
+
70
+ /* Research context sidebar */
71
+ .research-context {
72
+ background-color: var(--dark-component);
73
+ border-left: 1px solid var(--border-dark);
74
+ width: 400px !important;
75
+ overflow-y: auto;
76
+ }
77
+
78
+ .context-section {
79
+ padding: 15px;
80
+ border-bottom: 1px solid var(--border-dark);
81
+ }
82
+
83
+ .context-section h3 {
84
+ font-size: 16px;
85
+ margin-bottom: 10px;
86
+ color: var(--text-light);
87
+ }
88
+
89
+ /* Active personas accordion */
90
+ .active-persona {
91
+ margin-bottom: 10px;
92
+ background-color: var(--dark-bg);
93
+ border-radius: 6px;
94
+ overflow: hidden;
95
+ }
96
+
97
+ .persona-header {
98
+ padding: 12px 15px;
99
+ background-color: var(--dark-component);
100
+ display: flex;
101
+ justify-content: space-between;
102
+ align-items: center;
103
+ cursor: pointer;
104
+ }
105
+
106
+ .persona-header h4 {
107
+ margin: 0;
108
+ font-size: 15px;
109
+ }
110
+
111
+ .persona-content {
112
+ padding: 12px 15px;
113
+ font-size: 14px;
114
+ color: var(--text-gray);
115
+ }
116
+
117
+ .personality-option {
118
+ display: flex;
119
+ align-items: center;
120
+ margin-top: 8px;
121
+ margin-bottom: 8px;
122
+ padding-left: 10px;
123
+ }
124
+
125
+ .personality-option input[type="checkbox"] {
126
+ margin-right: 8px;
127
+ }
128
+
129
+ /* Headers and tabs */
130
+ .research-tabs {
131
+ display: flex;
132
+ border-bottom: 1px solid var(--border-dark);
133
+ }
134
+
135
+ .research-tab {
136
+ padding: 10px 15px;
137
+ cursor: pointer;
138
+ color: var(--text-gray);
139
+ }
140
+
141
+ .research-tab.active {
142
+ color: var(--text-light);
143
+ border-bottom: 2px solid var(--highlight-blue);
144
+ }
145
+
146
+ /* Additional context section */
147
+ .additional-context {
148
+ padding: 15px;
149
+ }
150
+
151
+ .additional-context textarea {
152
+ width: 100%;
153
+ background-color: var(--dark-bg);
154
+ border: 1px solid var(--border-dark);
155
+ border-radius: 6px;
156
+ color: var(--text-light);
157
+ min-height: 100px;
158
+ padding: 10px;
159
+ margin-top: 8px;
160
+ }
161
+
162
+ .apply-context-btn {
163
+ background-color: var(--highlight-blue);
164
+ color: var(--text-light);
165
+ border: none;
166
+ border-radius: 6px;
167
+ padding: 8px 12px;
168
+ margin-top: 10px;
169
+ cursor: pointer;
170
+ float: right;
171
+ }
172
+
173
+ /* Header that shows selected personas */
174
+ .selected-personas-header {
175
+ padding: 10px 15px;
176
+ background-color: var(--dark-component);
177
+ border-radius: 6px;
178
+ margin-bottom: 15px;
179
+ font-size: 13px;
180
+ color: var(--text-gray);
181
+ }
182
+
183
+ /* Message input styling */
184
+ .cl-chat-input-container {
185
+ background-color: var(--dark-bg);
186
+ border-top: 1px solid var(--border-dark);
187
+ }
188
+
189
+ .cl-chat-input {
190
+ background-color: var(--dark-component);
191
+ border: 1px solid var(--border-dark);
192
+ border-radius: 6px;
193
+ }
194
+
195
+ /* Custom tabs for Research Assistant/Multi-Persona Discussion */
196
+ .app-tabs {
197
+ display: flex;
198
+ margin-bottom: 20px;
199
+ }
200
+
201
+ .app-tab {
202
+ flex: 1;
203
+ text-align: center;
204
+ padding: 10px;
205
+ border: 1px solid var(--border-dark);
206
+ background-color: var(--dark-bg);
207
+ color: var(--text-gray);
208
+ cursor: pointer;
209
+ }
210
+
211
+ .app-tab.active {
212
+ background-color: var(--dark-component);
213
+ color: var(--text-light);
214
+ border-bottom: 2px solid var(--highlight-blue);
215
+ }
216
+
217
+ /* Make the UI elements appear in the correct places */
218
+ .cl-chat {
219
+ display: flex;
220
+ height: 100vh;
221
+ }
222
+
223
+ .cl-main {
224
+ flex: 1;
225
+ display: flex;
226
+ flex-direction: column;
227
+ }
228
+
229
+ /* Ensures the right sidebar is properly positioned */
230
+ #root > div {
231
+ display: flex;
232
+ width: 100%;
233
+ }
234
+
235
+ /* Custom styles to match the screenshot exactly */
236
+ .app-title {
237
+ font-size: 24px;
238
+ margin-bottom: 20px;
239
+ padding: 15px;
240
+ }
241
+
242
+ .temperature-slider {
243
+ width: 100%;
244
+ margin-top: 10px;
245
+ }
246
+
247
+ .model-label {
248
+ display: block;
249
+ margin-bottom: 8px;
250
+ font-size: 12px;
251
+ color: var(--text-gray);
252
+ }
253
+
254
+ /* Ensure proper spacing around chat elements */
255
+ .cl-message-list {
256
+ padding: 15px;
257
+ }
public/insightflow.js ADDED
@@ -0,0 +1,362 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // InsightFlow AI - Custom UI Script
2
+
3
+ document.addEventListener('DOMContentLoaded', function() {
4
+ // Create and append the custom UI elements once the Chainlit UI has loaded
5
+ setTimeout(createCustomUI, 1000);
6
+ });
7
+
8
+ // Main function to create the custom UI
9
+ function createCustomUI() {
10
+ createLeftSidebar();
11
+ createAppTabs();
12
+ createRightSidebar();
13
+ setupEventListeners();
14
+ }
15
+
16
+ // Create the left sidebar with settings
17
+ function createLeftSidebar() {
18
+ const sidebar = document.querySelector('.cl-sidebar');
19
+ if (!sidebar) return;
20
+
21
+ // Clear existing content
22
+ sidebar.innerHTML = '';
23
+
24
+ // Add app title
25
+ const appTitle = document.createElement('div');
26
+ appTitle.className = 'app-title';
27
+ appTitle.textContent = 'InsightFlow AI';
28
+ sidebar.appendChild(appTitle);
29
+
30
+ // Create Research Panel Settings section
31
+ const researchSettings = document.createElement('div');
32
+ researchSettings.className = 'settings-section';
33
+ researchSettings.innerHTML = `
34
+ <h3>Research Panel Settings</h3>
35
+ <div class="persona-types">
36
+ <p>Select Persona Types:</p>
37
+ <div class="persona-checkbox">
38
+ <input type="checkbox" id="analytical" checked>
39
+ <label for="analytical">Analytical/Diagnostic</label>
40
+ </div>
41
+ <div class="persona-checkbox">
42
+ <input type="checkbox" id="scientific" checked>
43
+ <label for="scientific">Scientific/STEM Explorer</label>
44
+ </div>
45
+ <div class="persona-checkbox">
46
+ <input type="checkbox" id="metaphorical" checked>
47
+ <label for="metaphorical">Metaphorical/Creative-Analogy</label>
48
+ </div>
49
+ <div class="persona-checkbox">
50
+ <input type="checkbox" id="philosophical" checked>
51
+ <label for="philosophical">Spiritual/Philosophical</label>
52
+ </div>
53
+ <div class="persona-checkbox">
54
+ <input type="checkbox" id="factual">
55
+ <label for="factual">Practical/Factual</label>
56
+ </div>
57
+ <div class="persona-checkbox">
58
+ <input type="checkbox" id="historical">
59
+ <label for="historical">Historical/Synthesis</label>
60
+ </div>
61
+ <div class="persona-checkbox">
62
+ <input type="checkbox" id="futuristic">
63
+ <label for="futuristic">Futuristic/Speculative</label>
64
+ </div>
65
+ </div>
66
+ `;
67
+ sidebar.appendChild(researchSettings);
68
+
69
+ // Create Model Settings section
70
+ const modelSettings = document.createElement('div');
71
+ modelSettings.className = 'settings-section';
72
+ modelSettings.innerHTML = `
73
+ <h3>Model Settings</h3>
74
+ <div class="model-selection">
75
+ <span class="model-label">Model</span>
76
+ <div class="persona-checkbox">
77
+ <input type="radio" id="default" name="model" checked>
78
+ <label for="default">Default</label>
79
+ </div>
80
+ <div class="persona-checkbox">
81
+ <input type="radio" id="gpt4" name="model">
82
+ <label for="gpt4">GPT-4</label>
83
+ </div>
84
+ <div class="persona-checkbox">
85
+ <input type="radio" id="claude" name="model">
86
+ <label for="claude">Claude</label>
87
+ </div>
88
+ </div>
89
+
90
+ <div class="temperature-control">
91
+ <span class="model-label">Temperature: 0.8</span>
92
+ <input type="range" min="0" max="1" step="0.1" value="0.8" class="temperature-slider">
93
+ </div>
94
+ `;
95
+ sidebar.appendChild(modelSettings);
96
+
97
+ // Create Session controls
98
+ const sessionSection = document.createElement('div');
99
+ sessionSection.className = 'settings-section';
100
+ sessionSection.innerHTML = `
101
+ <h3>Session</h3>
102
+ <button id="clearChat" class="persona-checkbox">Clear Chat History</button>
103
+ `;
104
+ sidebar.appendChild(sessionSection);
105
+ }
106
+
107
+ // Create application tabs (Research Assistant/Multi-Persona Discussion)
108
+ function createAppTabs() {
109
+ const chatContainer = document.querySelector('.cl-chat-container');
110
+ if (!chatContainer) return;
111
+
112
+ // Find the chat area to insert tabs before it
113
+ const chatArea = document.querySelector('.cl-chat-container .cl-message-list');
114
+ if (!chatArea) return;
115
+
116
+ // Create tabs container
117
+ const tabsContainer = document.createElement('div');
118
+ tabsContainer.className = 'app-tabs';
119
+
120
+ // Create Research Assistant tab (active by default)
121
+ const researchTab = document.createElement('div');
122
+ researchTab.className = 'app-tab active';
123
+ researchTab.textContent = 'Research Assistant';
124
+ researchTab.dataset.tab = 'research';
125
+
126
+ // Create Multi-Persona Discussion tab
127
+ const multiPersonaTab = document.createElement('div');
128
+ multiPersonaTab.className = 'app-tab';
129
+ multiPersonaTab.textContent = 'Multi-Persona Discussion';
130
+ multiPersonaTab.dataset.tab = 'discussion';
131
+
132
+ // Add tabs to container
133
+ tabsContainer.appendChild(researchTab);
134
+ tabsContainer.appendChild(multiPersonaTab);
135
+
136
+ // Insert tabs before chat area
137
+ chatContainer.insertBefore(tabsContainer, chatArea);
138
+
139
+ // Create header showing selected personas
140
+ const selectedPersonasHeader = document.createElement('div');
141
+ selectedPersonasHeader.className = 'selected-personas-header';
142
+ selectedPersonasHeader.textContent = 'Selected Persona Types: Analytical/Diagnostic, Scientific/STEM Explorer, Spiritual/Philosophical';
143
+
144
+ // Insert header after tabs
145
+ chatContainer.insertBefore(selectedPersonasHeader, chatArea);
146
+ }
147
+
148
+ // Create right sidebar for Research Context
149
+ function createRightSidebar() {
150
+ // Check if the main element exists
151
+ const main = document.querySelector('.cl-main');
152
+ if (!main) return;
153
+
154
+ // Create the research context sidebar
155
+ const researchContext = document.createElement('div');
156
+ researchContext.className = 'research-context';
157
+
158
+ // Create tabs for the research context
159
+ const tabs = document.createElement('div');
160
+ tabs.className = 'research-tabs';
161
+
162
+ const contextTab = document.createElement('div');
163
+ contextTab.className = 'research-tab active';
164
+ contextTab.textContent = 'Context';
165
+
166
+ const sourcesTab = document.createElement('div');
167
+ sourcesTab.className = 'research-tab';
168
+ sourcesTab.textContent = 'Sources';
169
+
170
+ const settingsTab = document.createElement('div');
171
+ settingsTab.className = 'research-tab';
172
+ settingsTab.textContent = 'Settings';
173
+
174
+ tabs.appendChild(contextTab);
175
+ tabs.appendChild(sourcesTab);
176
+ tabs.appendChild(settingsTab);
177
+
178
+ // Create active personas section
179
+ const activePersonasSection = document.createElement('div');
180
+ activePersonasSection.className = 'context-section';
181
+ activePersonasSection.innerHTML = `
182
+ <h3>Active Personas</h3>
183
+
184
+ <!-- Analytical persona -->
185
+ <div class="active-persona">
186
+ <div class="persona-header">
187
+ <h4>Analytical/Diagnostic</h4>
188
+ <span>▼</span>
189
+ </div>
190
+ <div class="persona-content">
191
+ <p>Methodical examination of details and logical connections for problem-solving.</p>
192
+ <div class="available-personalities">
193
+ <p>Available Personalities:</p>
194
+ <div class="personality-option">
195
+ <input type="checkbox" id="sherlock-holmes" checked>
196
+ <label for="sherlock-holmes">Sherlock Holmes</label>
197
+ </div>
198
+ <div class="personality-option">
199
+ <input type="checkbox" id="gregory-house">
200
+ <label for="gregory-house">Dr. Gregory House MD</label>
201
+ </div>
202
+ <div class="personality-option">
203
+ <input type="checkbox" id="hercule-poirot">
204
+ <label for="hercule-poirot">Hercule Poirot</label>
205
+ </div>
206
+ <div class="personality-option">
207
+ <input type="checkbox" id="christopher-nolan">
208
+ <label for="christopher-nolan">Christopher Nolan</label>
209
+ </div>
210
+ </div>
211
+ </div>
212
+ </div>
213
+
214
+ <!-- Scientific persona -->
215
+ <div class="active-persona">
216
+ <div class="persona-header">
217
+ <h4>Scientific/STEM Explorer</h4>
218
+ <span>▼</span>
219
+ </div>
220
+ <div class="persona-content">
221
+ <p>Evidence-based reasoning using empirical data and research.</p>
222
+ <div class="available-personalities">
223
+ <p>Available Personalities:</p>
224
+ <div class="personality-option">
225
+ <input type="checkbox" id="richard-feynman" checked>
226
+ <label for="richard-feynman">Richard Feynman</label>
227
+ </div>
228
+ <div class="personality-option">
229
+ <input type="checkbox" id="david-deutsch">
230
+ <label for="david-deutsch">David Deutsch</label>
231
+ </div>
232
+ <div class="personality-option">
233
+ <input type="checkbox" id="hans-rosling">
234
+ <label for="hans-rosling">Hans Rosling</label>
235
+ </div>
236
+ <div class="personality-option">
237
+ <input type="checkbox" id="hannah-fry">
238
+ <label for="hannah-fry">Hannah Fry</label>
239
+ </div>
240
+ </div>
241
+ </div>
242
+ </div>
243
+
244
+ <!-- Philosophical persona -->
245
+ <div class="active-persona">
246
+ <div class="persona-header">
247
+ <h4>Spiritual/Philosophical</h4>
248
+ <span>▼</span>
249
+ </div>
250
+ <div class="persona-content">
251
+ <p>Holistic perspectives examining deeper meaning and interconnectedness.</p>
252
+ <div class="available-personalities">
253
+ <p>Available Personalities:</p>
254
+ <div class="personality-option">
255
+ <input type="checkbox" id="jiddu-krishnamurti" checked>
256
+ <label for="jiddu-krishnamurti">Jiddu Krishnamurti</label>
257
+ </div>
258
+ <div class="personality-option">
259
+ <input type="checkbox" id="swami-vivekananda">
260
+ <label for="swami-vivekananda">Swami Vivekananda</label>
261
+ </div>
262
+ <div class="personality-option">
263
+ <input type="checkbox" id="dalai-lama">
264
+ <label for="dalai-lama">Dalai Lama</label>
265
+ </div>
266
+ </div>
267
+ </div>
268
+ </div>
269
+ `;
270
+
271
+ // Create additional context section
272
+ const additionalContextSection = document.createElement('div');
273
+ additionalContextSection.className = 'additional-context';
274
+ additionalContextSection.innerHTML = `
275
+ <h3>Additional Context</h3>
276
+ <p>Add background information or specific instructions</p>
277
+ <textarea placeholder="Enter additional research context or instructions here..."></textarea>
278
+ <button class="apply-context-btn">Apply Context</button>
279
+ `;
280
+
281
+ // Assemble the research context sidebar
282
+ researchContext.appendChild(tabs);
283
+ researchContext.appendChild(activePersonasSection);
284
+ researchContext.appendChild(additionalContextSection);
285
+
286
+ // Add the research context to the main element
287
+ const parent = main.parentElement;
288
+ parent.appendChild(researchContext);
289
+ }
290
+
291
+ // Set up event listeners for interactive elements
292
+ function setupEventListeners() {
293
+ // Toggle persona accordions
294
+ const personaHeaders = document.querySelectorAll('.persona-header');
295
+ personaHeaders.forEach(header => {
296
+ header.addEventListener('click', function() {
297
+ const content = this.nextElementSibling;
298
+ const indicator = this.querySelector('span');
299
+
300
+ if (content.style.display === 'none') {
301
+ content.style.display = 'block';
302
+ indicator.textContent = '▼';
303
+ } else {
304
+ content.style.display = 'none';
305
+ indicator.textContent = '▶';
306
+ }
307
+ });
308
+ });
309
+
310
+ // Handle tab switching
311
+ const appTabs = document.querySelectorAll('.app-tab');
312
+ appTabs.forEach(tab => {
313
+ tab.addEventListener('click', function() {
314
+ // Remove active class from all tabs
315
+ appTabs.forEach(t => t.classList.remove('active'));
316
+ // Add active class to clicked tab
317
+ this.classList.add('active');
318
+
319
+ // Update the selected personas header based on tab
320
+ const header = document.querySelector('.selected-personas-header');
321
+ if (header) {
322
+ if (this.dataset.tab === 'research') {
323
+ header.textContent = 'Selected Persona Types: Analytical/Diagnostic, Scientific/STEM Explorer, Spiritual/Philosophical';
324
+ } else {
325
+ header.textContent = 'Multi-Persona Discussion Mode: All personas participate independently';
326
+ }
327
+ }
328
+ });
329
+ });
330
+
331
+ // Handle research context tabs
332
+ const researchTabs = document.querySelectorAll('.research-tab');
333
+ researchTabs.forEach(tab => {
334
+ tab.addEventListener('click', function() {
335
+ // Remove active class from all tabs
336
+ researchTabs.forEach(t => t.classList.remove('active'));
337
+ // Add active class to clicked tab
338
+ this.classList.add('active');
339
+ });
340
+ });
341
+
342
+ // Handle clear chat button
343
+ const clearChatBtn = document.getElementById('clearChat');
344
+ if (clearChatBtn) {
345
+ clearChatBtn.addEventListener('click', function() {
346
+ // This would typically call a Chainlit function to clear the chat
347
+ // For now, just clear the messages in the UI
348
+ const messageList = document.querySelector('.cl-message-list');
349
+ if (messageList) {
350
+ messageList.innerHTML = '';
351
+ }
352
+ });
353
+ }
354
+ }
355
+
356
+ // Periodically check if UI needs to be refreshed (in case of Chainlit UI refreshes)
357
+ setInterval(function() {
358
+ const customUi = document.querySelector('.app-title');
359
+ if (!customUi) {
360
+ createCustomUI();
361
+ }
362
+ }, 3000);
public/style.css ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* public/style.css */
2
+ body {
3
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
4
+ background-color: var(--chainlit-background-color); /* Use Chainlit's theme variable */
5
+ color: var(--chainlit-text-color); /* Use Chainlit's theme variable */
6
+ }
7
+
8
+ /* Main chat container - you might need to inspect Chainlit's generated HTML for exact selectors */
9
+ /* This is a guess, adjust based on actual rendered HTML */
10
+ #root .MuiBox-root { /* Common root element, might need more specificity */
11
+ /* background-color: #262626; /* Example: Slightly off-black if not using theme variable */
12
+ }
13
+
14
+ /* Chat input area */
15
+ /* Targeting Chainlit's specific input structure. Inspect your app for exact classes if this doesn't work. */
16
+ div[data-testid="chat-input-textarea"] {
17
+ background-color: #333333 !important;
18
+ border-radius: 12px !important;
19
+ padding: 8px 12px !important;
20
+ box-shadow: 0 2px 10px rgba(0,0,0,0.2) !important;
21
+ border: 1px solid #444 !important;
22
+ }
23
+
24
+ textarea[data-testid="chat-input"] {
25
+ background-color: transparent !important;
26
+ color: #E0E0E0 !important;
27
+ font-size: 1rem !important;
28
+ border: none !important;
29
+ outline: none !important;
30
+ box-shadow: none !important;
31
+ padding-top: 8px !important;
32
+ padding-bottom: 8px !important;
33
+ }
34
+
35
+ /* Send button */
36
+ button[aria-label="Send message"], button[data-testid="send-button"] {
37
+ background-color: #5E5CE6 !important;
38
+ color: white !important;
39
+ border-radius: 8px !important;
40
+ transition: background-color 0.2s ease-in-out;
41
+ }
42
+ button[aria-label="Send message"]:hover, button[data-testid="send-button"]:hover {
43
+ background-color: #4D4AA7 !important;
44
+ }
45
+ button[aria-label="Stop task"] {
46
+ background-color: #FF3B30 !important;
47
+ color: white !important;
48
+ border-radius: 8px !important;
49
+ }
50
+
51
+ /* Message Bubbles */
52
+ /* These selectors target common Chainlit structures. Adjust if your version differs. */
53
+ /* User Message */
54
+ div[data-testid^="message-user"] > div > div {
55
+ background-color: #5E5CE6 !important;
56
+ color: white !important;
57
+ border-radius: 18px !important;
58
+ border-bottom-right-radius: 5px !important;
59
+ /* Add other properties like padding, margin if needed from the .user-message-class example */
60
+ }
61
+
62
+ /* Assistant Message */
63
+ div[data-testid^="message-assistant"] > div > div {
64
+ background-color: #3A3A3C !important;
65
+ color: #E0E0E0 !important;
66
+ border-radius: 18px !important;
67
+ border-bottom-left-radius: 5px !important;
68
+ /* Add other properties like padding, margin if needed from the .assistant-message-class example */
69
+ }
70
+
71
+ /* Settings Panel - try to keep it consistent */
72
+ /* You'll need to inspect elements if settings panel needs specific overrides */
73
+ /* .settings-modal-class { ... } */
74
+
75
+ /* Action Buttons (e.g., Export buttons, those in settings panel) */
76
+ button.MuiButtonBase-root[id^="action-button-"] {
77
+ background-color: #4CAF50 !important;
78
+ color: white !important;
79
+ border-radius: 8px !important;
80
+ padding: 6px 12px !important;
81
+ text-transform: none !important;
82
+ font-weight: 500 !important;
83
+ box-shadow: 0 1px 3px rgba(0,0,0,0.15) !important;
84
+ transition: background-color 0.2s ease-in-out;
85
+ margin: 5px !important;
86
+ }
87
+ button.MuiButtonBase-root[id^="action-button-"]:hover {
88
+ background-color: #409644 !important;
89
+ }
90
+
91
+ /* Style for the gear icon (settings button) to make it more prominent if desired */
92
+ /* button[aria-label=\"Settings\"] { */
93
+ /* color: #5E5CE6 !important; */
94
+ /* } */
95
+
96
+ /* Custom scrollbar */
97
+ ::-webkit-scrollbar {
98
+ width: 8px;
99
+ height: 8px;
100
+ }
101
+ ::-webkit-scrollbar-track {
102
+ background: #2c2c2e;
103
+ }
104
+ ::-webkit-scrollbar-thumb {
105
+ background: #555;
106
+ border-radius: 4px;
107
+ }
108
+ ::-webkit-scrollbar-thumb:hover {
109
+ background: #666;
110
+ }
111
+
112
+ /* Code block styling */
113
+ pre, code {
114
+ background-color: #2C2C2E !important;
115
+ color: #E0E0E0 !important;
116
+ border-radius: 6px;
117
+ font-family: 'Fira Code', 'Courier New', Courier, monospace;
118
+ }
119
+ pre {
120
+ padding: 1em;
121
+ overflow-x: auto;
122
+ border: 1px solid #444;
123
+ }
124
+ pre > code {
125
+ padding: 0;
126
+ background-color: transparent !important;
127
+ border: none;
128
+ }
129
+
130
+ /* Mermaid diagram styling */
131
+ .mermaid {
132
+ background-color: #1E1E1E;
133
+ padding: 20px;
134
+ border-radius: 8px;
135
+ border: 1px solid #484848;
136
+ box-shadow: 0 4px 12px rgba(0,0,0,0.3);
137
+ }
pyproject.toml ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "InsightFlow-AI"
3
+ version = "0.1.0"
4
+ description = "InsightFlow AI"
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ dependencies = [
8
+ "arxiv==2.1.3",
9
+ "beautifulsoup4==4.13.3",
10
+ "chainlit==2.5.5",
11
+ "cohere==5.13.12",
12
+ "datasets==3.3.1",
13
+ "faiss-cpu==1.10.0",
14
+ "langchain-cohere==0.4.2",
15
+ "langchain-community==0.3.14",
16
+ "langchain-huggingface==0.1.2",
17
+ "langchain-openai==0.2.14",
18
+ "langchain-qdrant==0.2.0",
19
+ "langgraph==0.2.61",
20
+ "lxml==5.3.1",
21
+ "nltk==3.8.1",
22
+ "numpy>=1.25.0,<2.0.0",
23
+ "pyarrow==19.0.1",
24
+ "pymupdf==1.25.3",
25
+ "python-dotenv>=1.0.1",
26
+ "python-pptx==1.0.2",
27
+ "ragas==0.2.10",
28
+ "sentence-transformers==3.4.1",
29
+ "unstructured==0.14.8",
30
+ "websockets>=15.0",
31
+ "fpdf==1.7.2",
32
+ "qdrant-client",
33
+ "tiktoken",
34
+ "fastembed",
35
+ "aiohappyeyeballs",
36
+ ]
37
+
38
+ [tool.pytest.ini_options]
39
+ python_files = "test_*.py tests_*.py *_test.py *_tests.py"
40
+ testpaths = [
41
+ "tests",
42
+ ]
43
+ pythonpath = ["."]
44
+ asyncio_mode = "auto"
45
+
46
+ [tool.setuptools.packages.find]
47
+ where = ["."] # Search for packages in the current directory
48
+ exclude = [
49
+ "public*",
50
+ "data_sources*",
51
+ "persona_configs*",
52
+ "tests*",
53
+ "examples*",
54
+ "docs*",
55
+ "scripts*",
56
+ ".venv*" # Also exclude the virtual environment directory itself
57
+ ]
requirements.txt ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ chainlit==2.5.5
2
+ langchain-community==0.3.14
3
+ langchain-openai==0.2.14
4
+ langchain-core>=0.3.29,<0.4.0
5
+ langchain-huggingface==0.1.2
6
+ langgraph==0.2.61
7
+ openai>=1.18.0
8
+ python-dotenv>=1.0.1
9
+ fpdf==1.7.2
10
+ tavily-python>=0.1.18
11
+ websockets>=15.0
12
+ numpy>=1.25.0,<2.0.0
13
+
14
+ # For planned RAG implementation
15
+ faiss-cpu==1.10.0
16
+ langchain-qdrant==0.2.0
17
+ sentence-transformers==3.4.1
18
+ ragas==0.2.10
19
+ datasets==3.3.1
20
+
21
+ # Testing
22
+ pytest>=8.0.0
23
+ pytest-asyncio>=0.26.0
utils/__init__.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file can be empty. Its presence tells Python that the utils directory should be treated as a package, allowing us to import modules from it.
2
+
3
+ """
4
+ Utilities for InsightFlow AI
5
+ """
6
+
7
+ # Import persona system for easier access
8
+ from utils.persona import PersonaFactory, PersonaReasoning
9
+
10
+ # This file makes the utils directory a Python package
utils/helpers.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Utility functions for InsightFlow AI
2
+
3
+ # Placeholder for state type, assuming it's a dict-like object or a dataclass
4
+ # from state import InsightFlowState # Or from app import InsightFlowState if defined there
5
+
6
+ def toggle_direct_mode(state: dict):
7
+ """Synchronously toggles the 'direct_mode' boolean in the state dictionary."""
8
+ state['direct_mode'] = not state.get('direct_mode', False)
9
+ # This function purely mutates the state.
10
+ # UI updates (sending messages, updating settings panel) should be handled by the caller (e.g., Chainlit action callback)
11
+
12
+ # Add other utility functions here as needed, for example:
13
+ # - Persona management utilities
14
+ # - Helper for formatting messages
15
+ # - etc.
utils/persona/__init__.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Persona system for InsightFlow AI.
3
+
4
+ This module provides the two-tier persona system with:
5
+ 1. Base persona types (Analytical, Scientific, Philosophical, etc.)
6
+ 2. Specific personalities (Holmes, Feynman, Fry)
7
+ """
8
+
9
+ # Import key classes for easier access
10
+ from utils.persona.base import PersonaReasoning, PersonaFactory
11
+ from utils.persona.impl import (
12
+ LLMPersonaReasoning,
13
+ AnalyticalReasoning,
14
+ ScientificReasoning,
15
+ PhilosophicalReasoning,
16
+ FactualReasoning,
17
+ MetaphoricalReasoning,
18
+ FuturisticReasoning,
19
+ HolmesReasoning,
20
+ FeynmanReasoning,
21
+ FryReasoning
22
+ )
utils/persona/base.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Base classes for the persona system.
3
+ """
4
+
5
+ from typing import Dict, Any, Optional
6
+ from langchain_core.language_models.chat_models import BaseChatModel # For type hinting the LLM
7
+ from langchain_core.messages import SystemMessage, HumanMessage
8
+ import inspect
9
+
10
+ class PersonaReasoning:
11
+ """Represents the reasoning capabilities of a persona."""
12
+ def __init__(self, persona_id: str, name: str, system_prompt: str, llm: BaseChatModel):
13
+ self.persona_id = persona_id
14
+ self.name = name
15
+ self.system_prompt = system_prompt
16
+ self.llm = llm # Store the actual LLM instance
17
+ print(f"DEBUG: PersonaReasoning for {self.name} initialized with LLM: {type(self.llm)}")
18
+
19
+ async def generate_perspective(self, query: str) -> str:
20
+ print(f"DEBUG: Generating perspective for {self.name} on query: '{query[:50]}...'")
21
+ messages = [
22
+ SystemMessage(content=self.system_prompt),
23
+ HumanMessage(content=query)
24
+ ]
25
+
26
+ response_content = ""
27
+ # Stream the response from the LLM
28
+ # If self.llm.astream(messages) is an AsyncMock, it might return a coroutine that yields the iterator.
29
+ stream_source = self.llm.astream(messages)
30
+ if inspect.iscoroutine(stream_source):
31
+ async_iterator = await stream_source
32
+ else:
33
+ async_iterator = stream_source
34
+
35
+ async for chunk in async_iterator:
36
+ if chunk.content:
37
+ response_content += chunk.content
38
+
39
+ print(f"DEBUG: Perspective from {self.name}: '{response_content[:100]}...'")
40
+ return response_content
41
+
42
+ class PersonaFactory:
43
+ """Factory for creating persona instances."""
44
+ def __init__(self, config_dir: str = "persona_configs"):
45
+ self.config_dir = config_dir
46
+ # Configs now store parameters, not direct LLM configs as LLMs are passed in
47
+ self.persona_configs: Dict[str, Dict[str, Any]] = self._load_persona_configs()
48
+ print(f"DEBUG: PersonaFactory initialized. Loaded {len(self.persona_configs)} persona base configs from {config_dir}.")
49
+
50
+ def _load_persona_configs(self) -> Dict[str, Dict[str, Any]]:
51
+ # These configs now only store name and system_prompt.
52
+ # The LLM to be used is determined in app.py and passed to create_persona.
53
+ return {
54
+ "analytical": {"name": "Analytical", "system_prompt": "You are an extremely analytical and methodical thinker. Break down the query into its fundamental components and analyze them logically."},
55
+ "scientific": {"name": "Scientific", "system_prompt": "You are a scientific researcher. Approach the query with empirical rigor, focusing on evidence, data, and established scientific principles."},
56
+ "philosophical": {"name": "Philosophical", "system_prompt": "You are a philosopher. Explore the query from multiple philosophical perspectives, considering its ethical, metaphysical, and epistemological implications."},
57
+ "factual": {"name": "Factual", "system_prompt": "You are a precise and factual expert. Provide concise, verified information relevant to the query, citing sources if possible (though you won't actually cite for now)."},
58
+ "metaphorical": {"name": "Metaphorical", "system_prompt": "You are a creative thinker who explains complex topics through vivid metaphors and analogies. Make the query understandable through comparisons."},
59
+ "futuristic": {"name": "Futuristic", "system_prompt": "You are a futurist. Analyze the query in the context of potential future trends, technologies, and societal changes."},
60
+ }
61
+
62
+ def create_persona(self, persona_id: str, llm_instance: BaseChatModel) -> Optional[PersonaReasoning]:
63
+ config = self.persona_configs.get(persona_id.lower())
64
+ if config and llm_instance:
65
+ return PersonaReasoning(
66
+ persona_id=persona_id.lower(),
67
+ name=config["name"],
68
+ system_prompt=config["system_prompt"],
69
+ llm=llm_instance # Pass the actual LLM instance
70
+ )
71
+ elif not llm_instance:
72
+ print(f"DEBUG Error: LLM instance not provided for persona {persona_id}")
73
+ return None
74
+
75
+ def get_available_personas(self) -> Dict[str, str]:
76
+ return {pid: conf["name"] for pid, conf in self.persona_configs.items()}
utils/persona/impl.py ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Implementations of different persona reasoning types.
3
+ """
4
+
5
+ from .base import PersonaReasoning
6
+ from langchain_core.messages import SystemMessage, HumanMessage
7
+ from langchain_openai import ChatOpenAI
8
+ from typing import Dict, List, Optional, Any
9
+ from langchain_core.documents import Document
10
+
11
+ class LLMPersonaReasoning(PersonaReasoning):
12
+ """Base implementation that uses LLM to generate responses"""
13
+
14
+ def __init__(self, config: Dict[str, Any], llm=None):
15
+ super().__init__(config)
16
+ # Use shared LLM instance if provided, otherwise create one
17
+ self.llm = llm or ChatOpenAI(model="gpt-4o-mini", temperature=0.4)
18
+
19
+ def generate_perspective(self, query: str, context: Optional[List[Document]] = None) -> str:
20
+ """Generate perspective using LLM with persona's system prompt"""
21
+
22
+ # Build prompt with context if available
23
+ context_text = ""
24
+ if context and len(context) > 0:
25
+ context_text = "\n\nRelevant information:\n" + "\n".join([doc.page_content for doc in context])
26
+
27
+ # Build messages
28
+ messages = [
29
+ SystemMessage(content=self.system_prompt),
30
+ HumanMessage(content=f"Query: {query}{context_text}\n\nPlease provide your perspective on this query based on your unique approach.")
31
+ ]
32
+
33
+ # Get response from LLM
34
+ response = self.llm.invoke(messages)
35
+ return response.content
36
+
37
+ # Specialized implementations for each persona type
38
+ class AnalyticalReasoning(LLMPersonaReasoning):
39
+ def generate_perspective(self, query: str, context: Optional[List[Document]] = None) -> str:
40
+ """Generate perspective using analytical reasoning approach"""
41
+ # For MVP, we'll use the base implementation
42
+ # In a full implementation, add analytical-specific enhancements
43
+ return super().generate_perspective(query, context)
44
+
45
+ class ScientificReasoning(LLMPersonaReasoning):
46
+ def generate_perspective(self, query: str, context: Optional[List[Document]] = None) -> str:
47
+ """Generate perspective using scientific reasoning approach"""
48
+ # For MVP, we'll use the base implementation
49
+ # In a full implementation, add scientific-specific enhancements
50
+ return super().generate_perspective(query, context)
51
+
52
+ class PhilosophicalReasoning(LLMPersonaReasoning):
53
+ def generate_perspective(self, query: str, context: Optional[List[Document]] = None) -> str:
54
+ """Generate perspective using philosophical reasoning approach"""
55
+ # For MVP, we'll use the base implementation
56
+ # In a full implementation, add philosophical-specific enhancements
57
+ return super().generate_perspective(query, context)
58
+
59
+ class FactualReasoning(LLMPersonaReasoning):
60
+ def generate_perspective(self, query: str, context: Optional[List[Document]] = None) -> str:
61
+ """Generate perspective using factual reasoning approach"""
62
+ # For MVP, we'll use the base implementation
63
+ # In a full implementation, add factual-specific enhancements
64
+ return super().generate_perspective(query, context)
65
+
66
+ class MetaphoricalReasoning(LLMPersonaReasoning):
67
+ def generate_perspective(self, query: str, context: Optional[List[Document]] = None) -> str:
68
+ """Generate perspective using metaphorical reasoning approach"""
69
+ # For MVP, we'll use the base implementation
70
+ # In a full implementation, add metaphorical-specific enhancements
71
+ return super().generate_perspective(query, context)
72
+
73
+ class FuturisticReasoning(LLMPersonaReasoning):
74
+ def generate_perspective(self, query: str, context: Optional[List[Document]] = None) -> str:
75
+ """Generate perspective using futuristic reasoning approach"""
76
+ # For MVP, we'll use the base implementation
77
+ # In a full implementation, add futuristic-specific enhancements
78
+ return super().generate_perspective(query, context)
79
+
80
+ # Personality implementations (second tier of two-tier system)
81
+ class HolmesReasoning(LLMPersonaReasoning):
82
+ """Sherlock Holmes personality implementation"""
83
+
84
+ def __init__(self, config: Dict[str, Any], parent_config: Dict[str, Any], llm=None):
85
+ super().__init__(config, llm)
86
+ self.parent_config = parent_config
87
+
88
+ def generate_perspective(self, query: str, context: Optional[List[Document]] = None) -> str:
89
+ """Generate perspective in Sherlock Holmes' style"""
90
+ # For MVP, we'll use the base implementation with Holmes' system prompt
91
+ # In a full implementation, add Holmes-specific reasoning patterns
92
+ return super().generate_perspective(query, context)
93
+
94
+ class FeynmanReasoning(LLMPersonaReasoning):
95
+ """Richard Feynman personality implementation"""
96
+
97
+ def __init__(self, config: Dict[str, Any], parent_config: Dict[str, Any], llm=None):
98
+ super().__init__(config, llm)
99
+ self.parent_config = parent_config
100
+
101
+ def generate_perspective(self, query: str, context: Optional[List[Document]] = None) -> str:
102
+ """Generate perspective in Richard Feynman's style"""
103
+ # For MVP, we'll use the base implementation with Feynman's system prompt
104
+ # In a full implementation, add Feynman-specific reasoning patterns
105
+ return super().generate_perspective(query, context)
106
+
107
+ class FryReasoning(LLMPersonaReasoning):
108
+ """Hannah Fry personality implementation"""
109
+
110
+ def __init__(self, config: Dict[str, Any], parent_config: Dict[str, Any], llm=None):
111
+ super().__init__(config, llm)
112
+ self.parent_config = parent_config
113
+
114
+ def generate_perspective(self, query: str, context: Optional[List[Document]] = None) -> str:
115
+ """Generate perspective in Hannah Fry's style"""
116
+ # For MVP, we'll use the base implementation with Fry's system prompt
117
+ # In a full implementation, add Fry-specific reasoning patterns
118
+ return super().generate_perspective(query, context)
utils/rag_utils.py ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Utilities for Retrieval Augmented Generation (RAG) setup and operations.
3
+ """
4
+ import os
5
+ import chainlit as cl
6
+ from langchain_community.document_loaders import DirectoryLoader, UnstructuredFileLoader, PyMuPDFLoader
7
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
8
+ from langchain_huggingface import HuggingFaceEmbeddings # Using HuggingFace for local, open-source model
9
+ from langchain_openai import OpenAIEmbeddings # Ensure this is imported
10
+ from langchain_qdrant import Qdrant
11
+ # from qdrant_client import QdrantClient # QdrantClient might be used for more direct/advanced Qdrant operations
12
+
13
+ # Recommended embedding model for good performance and local use.
14
+ # Ensure this model is downloaded or will be downloaded by sentence-transformers.
15
+ EMBEDDING_MODEL_NAME = "all-MiniLM-L6-v2"
16
+ # Qdrant recommends using FastEmbed for optimal performance with their DB.
17
+ # from langchain_community.embeddings import FastEmbedEmbeddings
18
+
19
+
20
+ def load_and_split_documents(persona_id: str, data_path: str = "data_sources"):
21
+ """
22
+ Loads all .txt files from the persona's data directory, splits them into
23
+ chunks, and returns a list of Langchain Document objects.
24
+ """
25
+ persona_data_path = os.path.join(data_path, persona_id)
26
+ if not os.path.isdir(persona_data_path):
27
+ print(f"Warning: Data directory not found for persona {persona_id} at {persona_data_path}")
28
+ return []
29
+
30
+ # Using DirectoryLoader with UnstructuredFileLoader for .txt files
31
+ # It's good at handling basic text. For more complex .txt or other formats,
32
+ # you might need more specific loaders or pre-processing.
33
+ loader = DirectoryLoader(
34
+ persona_data_path,
35
+ glob="**/*.txt", # Load all .txt files, including in subdirectories
36
+ loader_cls=UnstructuredFileLoader,
37
+ show_progress=True,
38
+ use_multithreading=True,
39
+ silent_errors=True # Suppress errors for files it can't load, log them instead if needed
40
+ )
41
+
42
+ try:
43
+ loaded_documents = loader.load()
44
+ if not loaded_documents:
45
+ print(f"No documents found or loaded for persona {persona_id} from {persona_data_path}")
46
+ return []
47
+ except Exception as e:
48
+ print(f"Error loading documents for persona {persona_id}: {e}")
49
+ return []
50
+
51
+ text_splitter = RecursiveCharacterTextSplitter(
52
+ chunk_size=1000, # Adjust as needed
53
+ chunk_overlap=150, # Adjust as needed
54
+ length_function=len,
55
+ is_separator_regex=False,
56
+ )
57
+
58
+ split_docs = text_splitter.split_documents(loaded_documents)
59
+ print(f"Loaded and split {len(loaded_documents)} documents into {len(split_docs)} chunks for persona {persona_id}.")
60
+ return split_docs
61
+
62
+
63
+ def get_embedding_model_instance(model_identifier: str, model_type: str = "hf"):
64
+ """
65
+ Initializes and returns an embedding model instance.
66
+ model_type can be 'hf' for HuggingFace or 'openai'.
67
+ """
68
+ if model_type == "openai":
69
+ print(f"Initializing OpenAI Embedding Model: {model_identifier}")
70
+ return OpenAIEmbeddings(model=model_identifier)
71
+ elif model_type == "hf":
72
+ print(f"Initializing HuggingFace Embedding Model: {model_identifier}")
73
+ # EMBEDDING_MODEL_NAME is still defined globally above, can be used as a fallback if needed
74
+ # but app.py now passes the explicit model_identifier.
75
+ return HuggingFaceEmbeddings(model_name=model_identifier)
76
+ else:
77
+ raise ValueError(f"Unsupported embedding model_type: {model_type}")
78
+
79
+
80
+ async def get_or_create_persona_vector_store(persona_id: str, embedding_model):
81
+ """Gets a vector store for a persona from the session, or creates and caches it."""
82
+ vector_store_key = f"vector_store_{persona_id}"
83
+
84
+ if cl.user_session.get(vector_store_key) is not None:
85
+ print(f"Found existing vector store for {persona_id} in session.")
86
+ return cl.user_session.get(vector_store_key)
87
+
88
+ print(f"No existing vector store for {persona_id} in session. Creating new one...")
89
+ documents = load_and_split_documents(persona_id)
90
+ if not documents:
91
+ print(f"No documents to create vector store for persona {persona_id}.")
92
+ cl.user_session.set(vector_store_key, None) # Mark as attempted but failed
93
+ return None
94
+
95
+ try:
96
+ # Qdrant.from_documents will handle creating the client and collection in-memory
97
+ vector_store = await cl.make_async(Qdrant.from_documents)(
98
+ documents,
99
+ embedding_model,
100
+ location=":memory:", # Specifies in-memory Qdrant
101
+ collection_name=f"{persona_id}_store_{cl.user_session.get('id', 'default_session')}", # Unique per session
102
+ # distance_func="Cosine", # Qdrant default is Cosine, or use models.Distance.COSINE
103
+ prefer_grpc=False # For local/in-memory, GRPC might not be necessary or set up
104
+ )
105
+ print(f"Successfully created vector store for {persona_id} with {len(documents)} chunks.")
106
+ cl.user_session.set(vector_store_key, vector_store)
107
+ return vector_store
108
+ except Exception as e:
109
+ print(f"Error creating Qdrant vector store for {persona_id}: {e}")
110
+ cl.user_session.set(vector_store_key, None) # Mark as attempted but failed
111
+ return None
112
+
113
+
114
+ async def get_relevant_context_for_query(query: str, persona_id: str, embedding_model) -> str:
115
+ """
116
+ Retrieves relevant context from the persona's vector store for a given query.
117
+ Returns a string of concatenated context or an empty string if no context found.
118
+ """
119
+ vector_store = await get_or_create_persona_vector_store(persona_id, embedding_model)
120
+
121
+ if not vector_store:
122
+ print(f"No vector store available for {persona_id} to retrieve context.")
123
+ return ""
124
+
125
+ retriever = vector_store.as_retriever(search_kwargs={"k": 3}) # Retrieve top 3 chunks
126
+
127
+ try:
128
+ results = await cl.make_async(retriever.invoke)(query)
129
+ if results:
130
+ context = "\n\n---\n\n".join([doc.page_content for doc in results])
131
+ # print(f"Retrieved context for {persona_id} query '{query}':\n{context[:500]}...") # Debug
132
+ return context
133
+ else:
134
+ print(f"No relevant documents found for {persona_id} query: '{query}'")
135
+ return ""
136
+ except Exception as e:
137
+ print(f"Error retrieving context for {persona_id} query '{query}': {e}")
138
+ return ""
utils/state_utils.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ # Utility functions for InsightFlow AI state management
2
+
3
+ from typing import Dict, Any
4
+
5
+ def toggle_direct_mode(state: Dict[str, Any]) -> None:
6
+ """Toggles the 'direct_mode' boolean in the given state dictionary."""
7
+ if 'direct_mode' not in state:
8
+ state['direct_mode'] = True # Initialize if not present, defaulting to True after first toggle
9
+ else:
10
+ state['direct_mode'] = not state['direct_mode']
utils/visualization_utils.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Placeholder for visualization utilities
2
+
3
+ from openai import AsyncOpenAI
4
+ from openai.types.images_response import Image, ImagesResponse
5
+ from typing import Optional
6
+ import asyncio # For potential timeout, though not strictly required by tests yet
7
+ import httpx # Added for timeout configuration in generate_dalle_image
8
+ from langchain_openai import ChatOpenAI # For type hinting generate_mermaid_code
9
+ from langchain_core.messages import SystemMessage # For generate_mermaid_code
10
+ import re # For extracting mermaid code
11
+
12
+ # System prompt for Mermaid generation (copied from test file for consistency)
13
+ MERMAID_SYSTEM_PROMPT_TEMPLATE = """You are an expert in creating Mermaid diagrams. Based on the following text, generate a concise and accurate Mermaid diagram syntax. Only output the Mermaid code block (```mermaid\n...
14
+ ```). Do not include any other explanatory text. If the text cannot be reasonably converted to a diagram, output '// No suitable diagram' as a comment. Text: {text_input}
15
+ """
16
+
17
+ async def generate_dalle_image(prompt: str, client: AsyncOpenAI) -> Optional[str]:
18
+ """Generates an image using DALL-E 3 and returns the URL."""
19
+ try:
20
+ print(f"Generating DALL-E image for prompt: '{prompt[:100]}...'")
21
+ # Configure a timeout for the API call
22
+ # Note: httpx.AsyncClient allows request-specific timeouts.
23
+ # AsyncOpenAI uses httpx internally. Default timeout for AsyncOpenAI is 60s.
24
+ # We can either rely on its default or, if we needed finer control over this specific call,
25
+ # we might need to configure the client upon its instantiation or use a separate client.
26
+ # For now, let's assume the default client timeout is sufficient, or add a note about it.
27
+ # Default timeout is 1 minute. For DALL-E, this is usually enough.
28
+ # For more control, you can pass `timeout=httpx.Timeout(30.0, connect=5.0)` to AsyncOpenAI client init.
29
+
30
+ response = await client.images.generate(
31
+ prompt=prompt,
32
+ model="dall-e-3",
33
+ size="1024x1024",
34
+ quality="standard", # 'standard' or 'hd'. 'hd' is more detailed but might be slower/costlier.
35
+ n=1,
36
+ style="vivid" # 'vivid' (hyper-real and dramatic) or 'natural' (more natural, less hyper-real)
37
+ )
38
+ if response.data and response.data[0].url:
39
+ return response.data[0].url
40
+ else:
41
+ print("DALL-E API call succeeded but returned no data or URL.")
42
+ return None
43
+ except Exception as e:
44
+ print(f"An error occurred during DALL-E image generation: {e}")
45
+ return None
46
+
47
+ async def generate_mermaid_code(text_input: str, llm_client: ChatOpenAI) -> Optional[str]:
48
+ """Generates Mermaid diagram code from text using an LLM."""
49
+ if not text_input or not llm_client:
50
+ return None
51
+
52
+ prompt = MERMAID_SYSTEM_PROMPT_TEMPLATE.format(text_input=text_input)
53
+ messages = [SystemMessage(content=prompt)]
54
+
55
+ try:
56
+ print(f"Generating Mermaid code for text: '{text_input[:100]}...'")
57
+ response = await llm_client.ainvoke(messages)
58
+ content = response.content
59
+
60
+ if "// No suitable diagram" in content:
61
+ print("LLM indicated no suitable diagram for Mermaid generation.")
62
+ return None
63
+
64
+ # Extract content within ```mermaid ... ``` block
65
+ match = re.search(r"```mermaid\n(.*?)\n```", content, re.DOTALL)
66
+ if match:
67
+ return match.group(1).strip()
68
+ else:
69
+ # If no block found, but also no "// No suitable diagram" comment,
70
+ # it might be that the LLM failed to follow instructions or returned plain text.
71
+ # We could return the raw content, or None, or try to sanitize.
72
+ # For now, if it's not a proper block and not the explicit no-diagram comment, assume failure to follow format.
73
+ print(f"Mermaid LLM did not return a valid Mermaid block. Raw output: {content[:200]}...")
74
+ return None
75
+
76
+ except Exception as e:
77
+ print(f"An error occurred during Mermaid code generation: {e}")
78
+ return None