Spaces:
Sleeping
Sleeping
File size: 8,173 Bytes
81598c5 17ae707 81598c5 17ae707 81598c5 17ae707 159527f 17ae707 81598c5 17ae707 81598c5 eea09fd 81598c5 17ae707 81598c5 17ae707 81598c5 eea09fd 81598c5 530fa5d 81598c5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | """
OpenMark Gradio UI β 3 tabs:
1. Chat β talk to the LangGraph agent
2. Search β instant semantic search with filters
3. Stats β knowledge base overview
"""
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
sys.stdout.reconfigure(encoding="utf-8")
import gradio as gr
from openmark import config
# Load once at startup β fail gracefully if credentials are not configured
print("Loading OpenMark...")
_embedder = None
_agent = None
_setup_error = None
try:
from openmark.embeddings.factory import get_embedder
from openmark.agent.graph import build_agent, ask
from openmark.stores import chroma as chroma_store
from openmark.stores import neo4j_store
_embedder = get_embedder()
_agent = build_agent()
print("OpenMark ready.")
except Exception as e:
_setup_error = str(e)
print(f"OpenMark setup incomplete: {e}")
# ββ Chat tab ββββββββββββββββββββββββββββββββββββββββββββββββββ
_NOT_READY = (
"## Setup required\n\n"
"This Space is a **demo shell** β it requires your own credentials to run.\n\n"
"See the [GitHub repo](https://github.com/OthmanAdi/OpenMark) for full setup instructions."
)
def chat_fn(message: str, history: list, thread_id: str):
if _agent is None:
history.append((message, _NOT_READY))
return history, ""
if not message.strip():
return history, ""
response = ask(_agent, message, thread_id=thread_id or "default")
history.append((message, response))
return history, ""
# ββ Search tab ββββββββββββββββββββββββββββββββββββββββββββββββ
def search_fn(query: str, category: str, min_score: float, n_results: int):
if _embedder is None:
return _NOT_READY
if not query.strip():
return "Enter a search query."
cat = category if category != "All" else None
ms = min_score if min_score > 0 else None
results = chroma_store.search(
query, _embedder, n=int(n_results),
category=cat, min_score=ms,
)
if not results:
return "No results found."
lines = []
for r in results:
lines.append(
f"**{r['rank']}. {r['title'] or r['url']}**\n"
f"π {r['url']}\n"
f"π {r['category']} | π {', '.join(t for t in r['tags'] if t)} | "
f"β {r['score']} | π― {r['similarity']:.3f} similarity\n"
)
return "\n---\n".join(lines)
# ββ Stats tab βββββββββββββββββββββββββββββββββββββββββββββββββ
def stats_fn():
if _embedder is None:
return _NOT_READY
chroma = chroma_store.get_stats()
neo4j = neo4j_store.get_stats()
# Category breakdown from Neo4j
cat_rows = neo4j_store.query("""
MATCH (b:Bookmark)-[:IN_CATEGORY]->(c:Category)
RETURN c.name AS category, count(b) AS count
ORDER BY count DESC
""")
cat_lines = "\n".join(f" {r['category']:<35} {r['count']:>5}" for r in cat_rows)
# Top tags
tag_rows = neo4j_store.query("""
MATCH (b:Bookmark)-[:TAGGED]->(t:Tag)
RETURN t.name AS tag, count(b) AS count
ORDER BY count DESC LIMIT 20
""")
tag_lines = ", ".join(f"{r['tag']} ({r['count']})" for r in tag_rows)
return (
f"## OpenMark Knowledge Base\n\n"
f"**ChromaDB vectors:** {chroma.get('total', 0)}\n"
f"**Neo4j bookmarks:** {neo4j.get('bookmarks', 0)}\n"
f"**Neo4j tags:** {neo4j.get('tags', 0)}\n"
f"**Neo4j categories:** {neo4j.get('categories', 0)}\n\n"
f"### By Category\n```\n{cat_lines}\n```\n\n"
f"### Top Tags\n{tag_lines}"
)
# ββ Build UI ββββββββββββββββββββββββββββββββββββββββββββββββββ
def build_ui():
with gr.Blocks(title="OpenMark") as app:
gr.Markdown("# OpenMark β Your Personal Knowledge Graph")
if _setup_error:
# No credentials β show landing page only
gr.Markdown(
"**8,000+ bookmarks, LinkedIn saves, and YouTube videos** indexed with "
"[pplx-embed](https://huggingface.co/collections/perplexity-ai/pplx-embed), "
"searchable with ChromaDB and Neo4j, queryable via a LangGraph agent.\n\n"
"Built by [Ahmad Othman Ammar Adi](https://github.com/OthmanAdi) Β· "
"[GitHub](https://github.com/OthmanAdi/OpenMark) Β· "
"[Dataset](https://huggingface.co/datasets/codingwithadi/openmark-bookmarks)\n\n"
"---\n\n"
"## Run it yourself\n\n"
"This Space requires your own data and credentials. "
"Clone the repo and follow the setup guide:\n\n"
"```bash\n"
"git clone https://github.com/OthmanAdi/OpenMark.git\n"
"cd OpenMark\n"
"pip install -r requirements.txt\n"
"cp .env.example .env # add your keys\n"
"python scripts/ingest.py\n"
"python openmark/ui/app.py\n"
"```"
)
else:
categories = ["All"] + config.CATEGORIES
with gr.Tabs():
# Tab 1: Chat
with gr.Tab("Chat"):
thread = gr.Textbox(value="default", label="Session ID", scale=1)
chatbot = gr.Chatbot(height=500)
msg_box = gr.Textbox(
placeholder="Ask anything about your saved bookmarks...",
label="Message", lines=2,
)
send_btn = gr.Button("Send", variant="primary")
send_btn.click(
chat_fn,
inputs=[msg_box, chatbot, thread],
outputs=[chatbot, msg_box],
)
msg_box.submit(
chat_fn,
inputs=[msg_box, chatbot, thread],
outputs=[chatbot, msg_box],
)
# Tab 2: Search
with gr.Tab("Search"):
with gr.Row():
q_input = gr.Textbox(placeholder="Search your knowledge base...", label="Query", scale=3)
cat_input = gr.Dropdown(categories, value="All", label="Category")
with gr.Row():
score_input = gr.Slider(0, 10, value=0, step=1, label="Min Quality Score")
n_input = gr.Slider(5, 50, value=10, step=5, label="Results")
search_btn = gr.Button("Search", variant="primary")
search_output = gr.Markdown()
search_btn.click(
search_fn,
inputs=[q_input, cat_input, score_input, n_input],
outputs=search_output,
)
q_input.submit(
search_fn,
inputs=[q_input, cat_input, score_input, n_input],
outputs=search_output,
)
# Tab 3: Stats
with gr.Tab("Stats"):
refresh_btn = gr.Button("Refresh Stats")
stats_output = gr.Markdown()
refresh_btn.click(stats_fn, outputs=stats_output)
app.load(stats_fn, outputs=stats_output)
return app
if __name__ == "__main__":
ui = build_ui()
ui.launch(server_name="0.0.0.0", server_port=7860, share=False)
|