|
|
|
|
|
import os |
|
import re |
|
import glob |
|
import streamlit as st |
|
import streamlit.components.v1 as components |
|
from transformers import pipeline |
|
from urllib.parse import quote |
|
from datetime import datetime |
|
import pytz |
|
|
|
|
|
st.set_page_config( |
|
page_title="AI Knowledge Tree Builder ππΏ", |
|
page_icon="π³β¨", |
|
layout="wide", |
|
initial_sidebar_state="auto", |
|
) |
|
|
|
|
|
trees = { |
|
"Biology": """ |
|
0. Biology Core Rules and Future Exceptions |
|
1. Central Dogma DNA RNA Protein |
|
- Current CRISPR RNA editing π§ͺ |
|
- Research Gene therapy siRNA π¬ |
|
- Future Programmable genetics π |
|
2. Cell Origin |
|
- Current iPSCs organoids π¦ |
|
- Research Synthetic cells π¬ |
|
- Future De novo cell creation π |
|
""", |
|
"AI Topics": """ |
|
1. Major AI Industry Players π |
|
1. Research Leaders π― |
|
- OpenAI: GPT-4 DALL-E Foundation Models π΅ |
|
- Google: PaLM Gemini LLMs π¦ |
|
- Anthropic: Claude Constitutional AI β‘ |
|
""", |
|
"Multiplayer Games": """ |
|
0. Fantasy Domain Introduction |
|
1. Setting the Scene |
|
- Current Create a high-fantasy realm ποΈ |
|
- Research Add domain-specific entities π§ββοΈ |
|
- Future AI-generated worldbuilding π |
|
""" |
|
} |
|
|
|
|
|
def sanitize_filename(text): |
|
"""Sanitize text for use in filenames.""" |
|
safe_text = re.sub(r'[^\w\s-]', ' ', text) |
|
safe_text = re.sub(r'\s+', ' ', safe_text) |
|
return safe_text.strip()[:50] |
|
|
|
def generate_timestamp_filename(query): |
|
"""Generate a timestamped filename for saving files.""" |
|
central = pytz.timezone('US/Central') |
|
current_time = datetime.now(central) |
|
time_str = current_time.strftime("%I%M%p") |
|
date_str = current_time.strftime("%m%d%Y") |
|
safe_query = sanitize_filename(query) |
|
return f"{time_str} {date_str} ({safe_query}).md" |
|
|
|
def parse_outline_to_mermaid(outline_text): |
|
"""Convert a tree outline to Mermaid syntax with clickable nodes.""" |
|
lines = outline_text.strip().split('\n') |
|
nodes = [] |
|
edges = [] |
|
clicks = [] |
|
stack = [] |
|
for line in lines: |
|
indent = len(line) - len(line.lstrip()) |
|
level = indent // 4 |
|
text = line.strip() |
|
label = re.sub(r'^[#*\->\d\.\s]+', '', text).strip() |
|
if label: |
|
node_id = f"N{len(nodes)}" |
|
nodes.append(f'{node_id}["{label}"]') |
|
clicks.append(f'click {node_id} "?q={quote(label)}" _blank') |
|
if stack: |
|
parent_level = stack[-1][0] |
|
if level > parent_level: |
|
parent_id = stack[-1][1] |
|
edges.append(f"{parent_id} --> {node_id}") |
|
stack.append((level, node_id)) |
|
else: |
|
while stack and stack[-1][0] >= level: |
|
stack.pop() |
|
if stack: |
|
parent_id = stack[-1][1] |
|
edges.append(f"{parent_id} --> {node_id}") |
|
stack.append((level, node_id)) |
|
else: |
|
stack.append((level, node_id)) |
|
return "graph TD\n" + "\n".join(nodes + edges + clicks) |
|
|
|
def generate_mermaid_html(mermaid_code: str) -> str: |
|
"""Generate HTML to center and display a Mermaid diagram.""" |
|
return f""" |
|
<html> |
|
<head> |
|
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script> |
|
<style> |
|
.centered-mermaid {{ |
|
display: flex; |
|
justify-content: center; |
|
margin: 20px auto; |
|
}} |
|
.mermaid {{ |
|
max-width: 800px; |
|
}} |
|
</style> |
|
</head> |
|
<body> |
|
<div class="mermaid centered-mermaid"> |
|
{mermaid_code} |
|
</div> |
|
<script> |
|
mermaid.initialize({{ startOnLoad: true }}); |
|
</script> |
|
</body> |
|
</html> |
|
""" |
|
|
|
def grow_tree(base_tree, new_node_name, parent_node): |
|
"""Add a new node to the tree under a specified parent.""" |
|
lines = base_tree.strip().split('\n') |
|
new_lines = [] |
|
added = False |
|
for line in lines: |
|
new_lines.append(line) |
|
if parent_node in line and not added: |
|
indent = len(line) - len(line.lstrip()) |
|
new_lines.append(f"{' ' * (indent + 4)}- {new_node_name} π±") |
|
added = True |
|
return "\n".join(new_lines) |
|
|
|
|
|
@st.cache_resource |
|
def load_generator(): |
|
"""Load a text generation model for AI lookups.""" |
|
return pipeline("text-generation", model="distilgpt2") |
|
|
|
|
|
def FileSidebar(): |
|
"""Render a sidebar for managing Markdown files.""" |
|
st.sidebar.title("π Saved Interactions") |
|
md_files = glob.glob("*.md") |
|
for file in md_files: |
|
col1, col2, col3 = st.sidebar.columns([1, 3, 1]) |
|
with col1: |
|
if st.button("π", key=f"open_{file}"): |
|
with open(file, 'r') as f: |
|
st.session_state['file_content'] = f.read() |
|
st.session_state['selected_file'] = file |
|
with col2: |
|
st.write(file) |
|
with col3: |
|
if st.button("π", key=f"delete_{file}"): |
|
os.remove(file) |
|
st.rerun() |
|
if st.sidebar.button("Create New Note"): |
|
filename = generate_timestamp_filename("New Note") |
|
with open(filename, 'w') as f: |
|
f.write("# New Note\n") |
|
st.sidebar.success(f"Created {filename}") |
|
|
|
|
|
st.title("π³ AI Knowledge Tree Builder π±") |
|
st.markdown("Grow, visualize, and explore knowledge trees with AI!") |
|
|
|
|
|
query_params = st.query_params |
|
if 'q' in query_params: |
|
query = query_params['q'] |
|
st.subheader(f"AI Lookup for: {query}") |
|
generator = load_generator() |
|
response = generator(query, max_length=50)[0]['generated_text'] |
|
st.write(f"**Response:** {response}") |
|
filename = generate_timestamp_filename(query) |
|
with open(filename, 'w') as f: |
|
f.write(f"# Query: {query}\n\n## AI Response\n{response}") |
|
st.success(f"Saved to {filename}") |
|
else: |
|
|
|
if 'current_tree' not in st.session_state: |
|
st.session_state['current_tree'] = trees["Biology"] |
|
if 'selected_tree_name' not in st.session_state: |
|
st.session_state['selected_tree_name'] = "Biology" |
|
|
|
selected_tree = st.selectbox("Select Knowledge Tree", list(trees.keys()), key="tree_select") |
|
if selected_tree != st.session_state['selected_tree_name']: |
|
st.session_state['current_tree'] = trees[selected_tree] |
|
st.session_state['selected_tree_name'] = selected_tree |
|
|
|
|
|
new_node = st.text_input("Add New Node (e.g., 'ML Pipeline')") |
|
parent_node = st.text_input("Parent Node to Attach To (e.g., 'Central Dogma')") |
|
if st.button("Grow Tree π±") and new_node and parent_node: |
|
st.session_state['current_tree'] = grow_tree(st.session_state['current_tree'], new_node, parent_node) |
|
st.success(f"Added '{new_node}' under '{parent_node}'!") |
|
|
|
|
|
st.markdown("### Knowledge Tree Visualization") |
|
mermaid_code = parse_outline_to_mermaid(st.session_state['current_tree']) |
|
mermaid_html = generate_mermaid_html(mermaid_code) |
|
components.html(mermaid_html, height=600) |
|
|
|
|
|
query = st.text_input("Enter Query for AI Lookup") |
|
if st.button("Perform AI Lookup π€") and query: |
|
generator = load_generator() |
|
response = generator(query, max_length=50)[0]['generated_text'] |
|
st.write(f"**AI Response:** {response}") |
|
filename = generate_timestamp_filename(query) |
|
with open(filename, 'w') as f: |
|
f.write(f"# Query: {query}\n\n## AI Response\n{response}") |
|
st.success(f"Saved to {filename}") |
|
|
|
|
|
if 'file_content' in st.session_state: |
|
st.markdown("### Selected File") |
|
st.markdown(st.session_state['file_content']) |
|
|
|
|
|
FileSidebar() |
|
|
|
if __name__ == "__main__": |
|
st.sidebar.markdown("Explore and grow your knowledge trees!") |