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
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,13 @@ service is unavailable, automatic reranking is silently skipped and retrieval
order is preserved. An unavailable remote/internal service is reported while
search still fails open.

The default local model, `local/minilm-l6-v2`, uses ONNX Runtime, tokenizers,
and NumPy directly; ChromaDB is not required. It retains the existing
384-dimensional MiniLM vectors padded to 512 dimensions and the cache at
`~/.cache/chroma/onnx_models/all-MiniLM-L6-v2/onnx`. A cold cache downloads the
~80 MB model archive and verifies its SHA-256 before extraction. Prepopulate
this cache for offline use; existing MiniLM databases do not need reindexing.

For a source checkout, open **[http://localhost:8062](http://localhost:8062)**
to interactively browse and try out the API.
<p align="center">
Expand Down
22 changes: 12 additions & 10 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,18 @@ dependencies = [
"pyyaml>=6.0",
# Server deps
"fastapi>=0.111.1",
# Explicit security floors for FastAPI's parser and Braintrust's Git helper.
"starlette>=1.3.1",
"gitpython>=3.1.58",
"uvicorn>=0.34.0",
"openai>=2.8.0",
"anthropic>=0.72.0",
"litellm>=1.80.11",
"litellm>=1.98.0,<2",
"braintrust>=0.12.0",
"cryptography>=45.0.0",
"python-jose>=3.3.0",
"cryptography>=50.0.1",
"passlib>=1.7.4",
"tenacity>=9.0.0",
"bcrypt>=4.2.1",
"duckduckgo-search>=7.0.1",
"xlsxwriter>=3.2.2",
"hdbscan>=0.8.40",
"redis>=6.2.0",
Expand All @@ -37,22 +38,23 @@ dependencies = [
"colorlog>=6.10.1",
"httpx>=0.28.1",
"pydantic[email]>=2.13.0",
"nltk>=3.9.3",
"json-repair>=0.30.0",
"json-repair>=0.60.1",
"gepa>=0.1.1,<0.2",
# CLI
"typer>=0.15.0",
"rich>=13.0.0",
"chromadb>=1.5.8",
# Cross-encoder reranker + local embedding providers — chromadb pulls
# ``sentence-transformers`` transitively, but we depend on it directly
# so the CrossEncoder/SentenceTransformer surface is guaranteed.
# Direct MiniLM ONNX inference; no vector database dependency.
"onnxruntime>=1.23.2",
"tokenizers>=0.22.0",
"numpy>=1.26.0",
# Cross-encoder reranker and Nomic local embeddings.
"sentence-transformers>=3.0",
"einops>=0.8.0",
# Native KNN search via the sqlite-vec loadable extension. A pure-Python
# fallback exists (see sqlite_storage/_base.py) for the rare platform whose
# SQLite build can't load extensions, but ship the fast path by default.
"sqlite-vec>=0.1.6",
"urllib3>=2.7.0",
]

[project.optional-dependencies]
Expand Down
2 changes: 1 addition & 1 deletion reflexio/cli/commands/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def _ensure_llm_configured(env_path: Path) -> None:
(conditionally) prompt for an embedding key, re-load the .env so
the new values land in ``os.environ``, and return.
- Missing only an embedding key (e.g. user set ANTHROPIC_API_KEY
manually) AND chromadb is unavailable → prompt only for an
manually) AND ONNX dependencies are unavailable → prompt only for an
embedding provider.
- No LLM key, non-interactive stdin (CI, nohup, container) → print a
clean pointer to the .env file and raise ``typer.Exit(1)`` so the
Expand Down
26 changes: 13 additions & 13 deletions reflexio/cli/commands/setup_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ def _prompt_llm_provider(env_path: Path) -> tuple[str, str, str]:
def _build_embedding_choices() -> list[tuple[str, str | None, str]]:
"""Build the interactive embedding-provider menu at call time.

The local option is included only when ``chromadb`` is importable; built
The local option is included only when the ONNX dependencies are importable; built
dynamically so the menu always reflects the current Python environment
rather than a snapshot frozen at module load.

Expand All @@ -159,11 +159,11 @@ def _build_embedding_choices() -> list[tuple[str, str | None, str]]:
provider (no API key needed).
"""
from reflexio.server.llm.providers.local_embedding_provider import (
is_chromadb_importable,
are_local_embedding_dependencies_available,
)

choices: list[tuple[str, str | None, str]] = []
if is_chromadb_importable():
if are_local_embedding_dependencies_available():
choices.append(
(
"local",
Expand Down Expand Up @@ -217,7 +217,7 @@ def _prompt_embedding_provider(env_path: Path, llm_provider_key: str) -> str | N
return None

# Non-interactive: pick the first available option without prompting. When
# chromadb is importable that's local (no key required); otherwise it's
# ONNX dependencies are importable that's local (no key required); otherwise it's
# OpenAI or Gemini, which still won't have an API key but at least the
# caller knows the wizard didn't block.
if _is_non_interactive():
Expand Down Expand Up @@ -307,7 +307,7 @@ def _choose_embedding_provider(env_path: Path, *, embedding_flag: str) -> str |
| | | picks the embedder. |
+-----------------+----------------+-------------------------------------+
| ``"auto"`` | interactive | Show the menu (default = local |
| | | when chromadb is importable). Write |
| | | when ONNX deps are installed). Write |
| | | the choice to org config; for |
| | | OpenAI / Gemini also collect the |
| | | API key inline. |
Expand All @@ -328,18 +328,18 @@ def _choose_embedding_provider(env_path: Path, *, embedding_flag: str) -> str |
"""
if embedding_flag in _EMBEDDING_MODEL_NAMES:
# Explicit non-default flag wins over interactive / auto-detection.
# ``--embedding=local`` requires chromadb at runtime, so refuse to
# ``--embedding=local`` requires ONNX dependencies at runtime, so refuse to
# persist a broken override the same way the interactive flow
# hides the option in that situation.
if embedding_flag == "local":
from reflexio.server.llm.providers.local_embedding_provider import (
is_chromadb_importable,
are_local_embedding_dependencies_available,
)

if not is_chromadb_importable():
if not are_local_embedding_dependencies_available():
typer.echo(
"Error: --embedding=local requires chromadb. "
"Install it with `pip install chromadb` or pick "
"Error: --embedding=local requires ONNX dependencies. "
"Install them with `pip install onnxruntime tokenizers numpy` or pick "
"openai/gemini/auto."
)
raise typer.Exit(1)
Expand All @@ -353,7 +353,7 @@ def _choose_embedding_provider(env_path: Path, *, embedding_flag: str) -> str |

choices = _build_embedding_choices()
if not choices:
# No providers available (chromadb not importable AND no cloud
# No providers available (ONNX dependencies not importable AND no cloud
# embedders in scope). Defer to runtime auto-detection, which will
# raise a clear error if nothing matches.
return None
Expand Down Expand Up @@ -862,7 +862,7 @@ def openclaw(
storage_label = _prompt_storage(env_path)

# Step 2.5: Upfront embedding-provider step. Local is the default when
# chromadb is importable; the choice persists to org config so it
# ONNX dependencies are importable; the choice persists to org config so it
# survives later cloud-key changes. Skipped for remote storage modes
# for the reason above.
is_remote = storage_label in {"Managed Reflexio", "Self-hosted Reflexio"}
Expand Down Expand Up @@ -985,7 +985,7 @@ def init(
display_name, model, _ = _prompt_llm_provider(env_path)

# Step 2.5: Upfront embedding-provider step. Local is the default when
# chromadb is importable; the choice is persisted to org config so it
# ONNX dependencies are importable; the choice is persisted to org config so it
# survives later cloud-key changes. Skipped for both Managed and
# Self-hosted modes — the remote server owns its own model config and
# a local override would just shadow whatever the operator set there.
Expand Down
2 changes: 0 additions & 2 deletions reflexio/integrations/openclaw/plugin/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"reflexio-ai",
# Used by reflexio's local embedding provider (ONNXMiniLM_L6_V2).
"chromadb>=0.5",
"einops>=0.8.0",
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ case "$CMD" in
echo "$svc_pid" > "$PID_FILE"

# Give uvicorn up to ~10s to answer /health. The very first boot
# after a fresh checkout may be slower (LiteLLM import, chromadb
# after a fresh checkout may be slower (LiteLLM import, ONNX Runtime
# warmup). We always return ok; the backend catches up in background
# if it needs to.
for _ in 1 2 3 4 5 6 7 8 9 10; do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ if ! grep -q '^OPENCLAW_SMART_USE_LOCAL_CLI=' "$REFLEXIO_ENV"; then
echo "[openclaw-smart] appended OPENCLAW_SMART_USE_LOCAL_CLI=1 to $REFLEXIO_ENV" >&2
fi
if ! grep -q '^OPENCLAW_SMART_USE_LOCAL_EMBEDDING=' "$REFLEXIO_ENV"; then
printf '# Use the in-process ONNX embedder (chromadb) — no API key for semantic search\nOPENCLAW_SMART_USE_LOCAL_EMBEDDING=1\n' >> "$REFLEXIO_ENV"
printf '# Use the in-process ONNX embedder (ONNX Runtime) — no API key for semantic search\nOPENCLAW_SMART_USE_LOCAL_EMBEDDING=1\n' >> "$REFLEXIO_ENV"
echo "[openclaw-smart] appended OPENCLAW_SMART_USE_LOCAL_EMBEDDING=1 to $REFLEXIO_ENV" >&2
fi

Expand Down
Loading
Loading