Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ jobs:
run: pip install -e '.[ui,mcp,opensearch,serve,dev]' numpy

- name: Check the optional deps are importable
# Four test modules call pytest.importorskip("mcp") / ("streamlit"). If
# the install above ever breaks, those ~60 tests would skip and the job
# would still go green — so fail here instead.
run: python -c "import mcp, streamlit, streamlit_agraph, opensearchpy, numpy"
# Several test modules call pytest.importorskip(...). If the install
# above ever breaks, those tests would skip and the job would still go
# green — so fail here instead.
run: python -c "import mcp, starlette, jinja2, opensearchpy, numpy"

- name: Run tests
# -rs lists skip reasons, so a suite that stops running is visible in
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ A brain is built from four primitives:
## Quickstart

```bash
pip install -e '.[all]' # core + UI (Streamlit) + MCP server
pip install -e '.[all]' # core + explorer UI + MCP server

# Try the bundled example (support brain: products, issues, segments, comments)
open-index index --brain examples/support-brain
Expand Down Expand Up @@ -47,7 +47,7 @@ Prefer containers, or need a brain several agents share? →
| `open-index ingest <connector>` | Run a connector now to pull entities from an MCP server. |
| `open-index run [--force] [--loop N]` | Run every connector whose `schedule` is due (wire into cron/CI). |
| `open-index search <query> [-t doc_type]` | Search from the terminal. |
| `open-index ui` | Launch the Streamlit explorer (Explore / **Map** / Analytics / Jobs). |
| `open-index ui` | Launch the explorer (How to use / Schema / Explore / **Map** / Analytics / Jobs). |
| `open-index mcp [--read-only]` | Run the MCP context layer over stdio. **Read+write by default**; `--read-only` opts out of writes. |
| `open-index serve [--port --token --read-only]` | Serve the MCP context layer over **HTTP** for remote agents (bearer-token auth). |
| `open-index serve --brains <root>` | Serve **every** brain under a directory from one process, each at `/<name>/mcp`. |
Expand Down Expand Up @@ -374,7 +374,7 @@ fastest way to get an answer and the best place to sanity-check a bigger change.
```bash
git clone https://github.com/DrDroidLab/open-index
cd open-index
pip install -e '.[all]' # core + UI (Streamlit) + MCP server
pip install -e '.[all]' # core + explorer UI + MCP server
pytest # run the test suite
```

Expand Down
2 changes: 0 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,6 @@ services:
<<: *brain-env
OPEN_INDEX_SEARCH_BACKEND: ${OPEN_INDEX_SEARCH_BACKEND:-sqlite}
OPEN_INDEX_OPENSEARCH_HOSTS: ${OPEN_INDEX_OPENSEARCH_HOSTS:-http://opensearch:9200}
STREAMLIT_SERVER_HEADLESS: "true"
STREAMLIT_SERVER_ADDRESS: 0.0.0.0
ports:
- "${UI_PORT:-8501}:8501"

Expand Down
4 changes: 2 additions & 2 deletions docs/quickstart.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ description: Install Open Index, run the bundled example brain, and open the exp
## Install

```bash
pip install -e '.[all]' # core + UI (Streamlit) + MCP server
pip install -e '.[all]' # core + explorer UI + MCP server
```

The `[all]` extra pulls in the Streamlit explorer and the MCP server. For a
The `[all]` extra pulls in the explorer and the MCP server. For a
narrower install, pick the extras you need — `[ui]`, `[mcp]`, `[serve]`,
`[semantic]`, `[opensearch]`.

Expand Down
2 changes: 1 addition & 1 deletion docs/reference/cli.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ changing dimensions.
| Command | What it does |
|---|---|
| `open-index search <query> [-t doc_type]` | Search from the terminal. |
| `open-index ui` | Launch the Streamlit explorer (Explore / **Map** / Analytics / Jobs). |
| `open-index ui` | Launch the explorer (How to use / Schema / Explore / **Map** / Analytics / Jobs). |

## Serving over MCP

Expand Down
33 changes: 18 additions & 15 deletions open_index/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
open-index index (re)load entities/ into the search index
open-index ingest <connector> run a connector to pull entities from MCP
open-index search <query> search from the terminal
open-index ui launch the Streamlit map explorer
open-index ui launch the explorer in a browser
open-index mcp run the MCP server (stdio)

`--brain <dir>` selects the brain directory (default: current directory).
Expand All @@ -15,7 +15,6 @@
from __future__ import annotations

import json
import sys
from pathlib import Path
from typing import Optional

Expand Down Expand Up @@ -357,25 +356,29 @@ def list_connectors(brain: str = BrainOpt):
@app.command()
def ui(
brain: str = BrainOpt,
port: int = typer.Option(8501, help="Streamlit port."),
host: str = typer.Option("0.0.0.0", help="Address to bind."),
port: int = typer.Option(8501, help="Port to serve the explorer on."),
):
"""Launch the Streamlit map explorer."""
"""Launch the explorer: schema, search, and the relationship map."""
import os
import subprocess

app_path = Path(__file__).parent / "ui" / "app.py"
env = dict(os.environ, OPEN_INDEX_DIR=str(Path(brain).resolve()))
cmd = [
sys.executable, "-m", "streamlit", "run", str(app_path),
"--server.port", str(port),
]

os.environ.setdefault("OPEN_INDEX_DIR", str(Path(brain).resolve()))

try:
subprocess.run(cmd, env=env, check=True)
except FileNotFoundError:
typer.secho("Streamlit not installed: pip install 'open-index[ui]'",
from open_index.ui.web import serve as serve_ui
except ImportError:
typer.secho("The explorer needs its extras: pip install 'open-index[ui]'",
fg=typer.colors.RED, err=True)
raise typer.Exit(1)

root = os.environ.get("OPEN_INDEX_BRAINS_ROOT")
where = f"every brain under {root}" if root else os.environ["OPEN_INDEX_DIR"]
typer.echo(f"open-index explorer · {where}")
# The bind address is not necessarily reachable, so print a URL that is.
shown = "127.0.0.1" if host in ("0.0.0.0", "::") else host
typer.echo(f" http://{shown}:{port}")
serve_ui(host=host, port=port)


@app.command()
def mcp(
Expand Down
7 changes: 4 additions & 3 deletions open_index/storage/sqlite_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,10 @@ class SQLiteBackend:
def __init__(self, db_path: str | Path, config=None):
self.db_path = Path(db_path)
self.db_path.parent.mkdir(parents=True, exist_ok=True)
# check_same_thread=False: the Streamlit UI caches one Brain (and thus one
# connection) but reruns it across worker threads. Access is serialized
# through _lock so the shared connection stays safe.
# check_same_thread=False: the explorer caches one Brain (and so one
# connection) while Starlette runs its sync endpoints on a threadpool, so
# the connection is reached from several threads. Access is serialized
# through _lock, which is what actually keeps it safe.
self._conn = sqlite3.connect(str(self.db_path), check_same_thread=False)
self._conn.row_factory = sqlite3.Row
self._conn.execute("PRAGMA foreign_keys = ON")
Expand Down
Loading
Loading