Skip to content
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
212 changes: 212 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
# =============================================================================
# ContextSeek Configuration Template
# =============================================================================
# Copy this file to .env and adjust values for your environment.
#
# Optional: place a shared config at examples/configs/.env. Resolution order:
# cwd .env → repo root .env → examples/configs/.env → environment variables.
#
# Each block uses an env prefix + field name (STORAGE_ + BACKEND →
# STORAGE_BACKEND). Names are matched case-insensitively.
#
# Required for minimal use: nothing (defaults are in-memory, no models).
# Optional: file storage, LangChain embedding/LLM, retrieval tuning, evolution,
# observability, lifecycle.
# =============================================================================

# =============================================================================
# 1. Storage
# =============================================================================
# Backend: "seekdb" (embedded, persistent — default) | "memory" (ephemeral) |
# "file" (persistent) | "oceanbase" (requires OB_* below)
STORAGE_BACKEND=seekdb
# Root directory when backend is "file"
STORAGE_PATH=.contextseek/store
# URI scheme for scope resolution
# STORAGE_URI_SCHEME=contextseek://
# Optional cold tier: "file" or "memory" (leave empty to disable)
# STORAGE_COLD_BACKEND=
# STORAGE_COLD_PATH=.contextseek/cold

# =============================================================================
# 1.0 SeekDB Storage (used when STORAGE_BACKEND=seekdb)
# =============================================================================
# Embedded mode by default (local .db directory); keep STORAGE_BACKEND=seekdb.
# For seekdb server mode, set STORAGE_BACKEND=oceanbase instead.
# SEEKDB_PATH=~/.contextseek/seekdb.db
# SEEKDB_HOST= # empty = embedded; set host for remote server mode
# SEEKDB_PORT=2881
# SEEKDB_DATABASE=contextseek

# =============================================================================
# 1.1 OceanBase Storage (required when STORAGE_BACKEND=oceanbase)
# =============================================================================
# Set STORAGE_BACKEND=oceanbase and fill in the OB_* params below.
# EMBEDDING_DIMS must also be set (section 2) — OceanBase needs the vector size
# to create its HNSW index at table creation time.
#
# OB_HOST=127.0.0.1
# OB_PORT=2881
# OB_USER=root@test
# OB_PASSWORD=
# OB_DB_NAME=contextseek
# OB_TABLE_NAME=contextseek_items

# =============================================================================
# 2. Embedding
# =============================================================================
# Provider: "none" | "openai" | "dashscope" | "ollama" | "huggingface" | "langchain"
EMBEDDING_PROVIDER=none
# EMBEDDING_MODEL=text-embedding-3-small
# Optional: inferred from known providers when omitted
# EMBEDDING_DIMS=1536
# Optional base URL for compatible providers (OpenAI-compatible, Ollama, etc.)
# EMBEDDING_BASE_URL=

## Typical provider snippets (uncomment one block as needed)
# OpenAI:
# EMBEDDING_PROVIDER=openai
# EMBEDDING_MODEL=text-embedding-3-small
# OPENAI_API_KEY=sk-...
# EMBEDDING_BASE_URL=https://api.openai.com/v1
#
# DashScope:
# EMBEDDING_PROVIDER=dashscope
# EMBEDDING_MODEL=text-embedding-v3
# DASHSCOPE_API_KEY=sk-...
#
# Ollama (local):
# EMBEDDING_PROVIDER=ollama
# EMBEDDING_MODEL=nomic-embed-text
# EMBEDDING_BASE_URL=http://127.0.0.1:11434
#
# HuggingFace (local):
# EMBEDDING_PROVIDER=huggingface
# EMBEDDING_MODEL=BAAI/bge-small-zh-v1.5
#
# Advanced custom LangChain class:
# EMBEDDING_PROVIDER=langchain
# EMBEDDING_CLASS_PATH=your_package.YourEmbeddings
# EMBEDDING_DIMS=1536

# =============================================================================
# 3. LLM (rerank / extract / summarize)
# =============================================================================
# Provider: "none" | "openai" | "dashscope" | "ollama" | "langchain"
LLM_PROVIDER=none
# LLM_MODEL=gpt-4o-mini
# Optional base URL for compatible providers (OpenAI-compatible, Ollama, etc.)
# LLM_BASE_URL=

## Typical provider snippets
# OpenAI:
# LLM_PROVIDER=openai
# LLM_MODEL=gpt-4o-mini
# OPENAI_API_KEY=sk-...
# LLM_BASE_URL=https://api.openai.com/v1
#
# DashScope:
# LLM_PROVIDER=dashscope
# LLM_MODEL=qwen-plus
# DASHSCOPE_API_KEY=sk-...
#
# Ollama:
# LLM_PROVIDER=ollama
# LLM_MODEL=qwen2.5:7b
# LLM_BASE_URL=http://127.0.0.1:11434
#
# Advanced custom LangChain class:
# LLM_PROVIDER=langchain
# LLM_CLASS_PATH=your_package.YourChatModel

# =============================================================================
# 4. Summarizer (drives L2 abstract / L1 overview generation in ContextSeek.add)
# =============================================================================
# Provider: "none" (disabled) | "llm" (uses LLM_*)
# SUMMARIZER_PROVIDER=none
# SUMMARIZER_L2_MAX_CHARS=100 # L2 abstract char budget
# SUMMARIZER_L1_MAX_CHARS=2000 # L1 overview char budget

# =============================================================================
# 5. Retrieval
# =============================================================================
RETRIEVAL_DEFAULT_K=20
# RETRIEVAL_DEFAULT_BUDGET=20
# RETRIEVAL_RECALL_ROUTES=["phrase","terms"]
# RETRIEVAL_RECALL_ROUTES=["phrase","terms","vector"]
# RETRIEVAL_RERANKER_MODE=heuristic # heuristic | llm
# RETRIEVAL_LLM_RERANK_TOP_N=20
# RETRIEVAL_VECTOR_WEIGHT=0.7
# RETRIEVAL_FTS_WEIGHT=0.3
# RETRIEVAL_MAX_CONTENT_CHARS=1200

# =============================================================================
# 6. Evolution (merge / extract / distill)
# =============================================================================
EVOLUTION_ENABLED=false
# EVOLUTION_SEMANTIC_MERGE_THRESHOLD=0.72
# EVOLUTION_MIN_CLUSTER_SIZE=3
# EVOLUTION_DECAY_HALF_LIFE_DAYS=7.0
# EVOLUTION_EPHEMERAL_TTL_SECONDS=3600.0
# EVOLUTION_LLM_MERGE_ENABLED=false
# EVOLUTION_LLM_CONFLICT_CHECK_ENABLED=false
# EVOLUTION_LLM_STAGE_INFER_ENABLED=false
# EVOLUTION_LLM_DISTILL_ENABLED=false
# EVOLUTION_LLM_FEEDBACK_ENABLED=false

# =============================================================================
# 6.1 Dream (optional LLM in consolidation/divergence)
# =============================================================================
# DREAM_LLM_ENABLED=false

# =============================================================================
# 7. Prompt Templates (optional overrides for LLM prompts)
# =============================================================================
# Values use Python format placeholders, e.g. {query}, {content}, {items}.
# For JSON examples in templates, escape braces as {{ and }}.
#
# PROMPT_SUMMARIZER_ABSTRACT_TEMPLATE=In at most {char_budget} chars, summarize:\n\n{content}
# PROMPT_SUMMARIZER_OVERVIEW_TEMPLATE=In at most {char_budget} chars, provide structured overview:\n\n{content}
# PROMPT_RETRIEVAL_RELEVANCE_TEMPLATE=Score 0..1 and return JSON {{\"score\": <float>}}.\nQuery: {query}\nPassage: {content}
# PROMPT_CONFLICT_JUDGE_TEMPLATE=Classify as near_duplicate/contradiction/none and return JSON {{\"label\":\"...\"}}.\nA: {new_text}\nB: {existing_text}
# PROMPT_STAGE_CLASSIFIER_TEMPLATE=Infer stage and return JSON {{\"stage\":\"...\"}}.\nsource_type: {source_type}\ndefault_stage: {default_stage}\ncontent: {content_text}
# PROMPT_FEEDBACK_TAG_TEMPLATE=Return JSON {{\"tag\":\"...\"}} for stage {stage} reason {reason}
# PROMPT_MERGE_SYNTHESIS_TEMPLATE=Synthesize one statement.\n{items}
# PROMPT_DISTILL_CANDIDATE_TEMPLATE=Return JSON {{\"distill\": true|false}}.\ncontent: {content_text}\ntags: {tags}
# PROMPT_DISTILL_RENDER_TEMPLATE=Return JSON with name/description/body.\ncontent: {content_text}\ntags: {tags}
# PROMPT_DREAM_CONSOLIDATION_TEMPLATE=Summarize shared pattern.\n{items}
# PROMPT_DREAM_DIVERGENCE_TEMPLATE=A: {a_text}\nB: {b_text}\nGenerate concise hypothesis.

# =============================================================================
# 8. Security
# =============================================================================
# SECURITY_ACL_ENABLED=true
# SECURITY_REDACT_SENSITIVE=false

# =============================================================================
# 9. Observability
# =============================================================================
OBSERVABILITY_AUDIT_ENABLED=false
# OBSERVABILITY_AUDIT_PATH=.contextseek/audit.jsonl
# OBSERVABILITY_METRICS_ENABLED=false
# OBSERVABILITY_METRICS_PATH=.contextseek/metrics.prom

# LangSmith tracing (https://smith.langchain.com)
# LANGSMITH_TRACING=false
# LANGSMITH_API_KEY=
# LANGSMITH_PROJECT=demo
# LANGSMITH_ENDPOINT=https://api.smith.langchain.com

# =============================================================================
# 10. Lifecycle
# =============================================================================
# LIFECYCLE_INTERVAL_SECONDS=3600
# LIFECYCLE_AUTO_COMPACT=true
# LIFECYCLE_COMPACT_MIN_ITEMS=5

# =============================================================================
# 11. GIS(need STORAGE_BACKEND=oceanbase,OceanBase >= 4.2.2 or seekdb)
# =============================================================================
# GEO_ENABLED=false
# GEO_GEO_TABLE_NAME=contextseek_geo
# GEO_SRID=4326
40 changes: 40 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: CI

on:
push:
branches: [main, master, dev, dev_*]
pull_request:

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: astral-sh/setup-uv@v5
with:
enable-cache: true

- name: Install dependencies
run: uv sync

- name: Lint
run: uv run ruff check src/ tests/

- name: Check formatting
run: uv run ruff format --check src/ tests/

test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: astral-sh/setup-uv@v5
with:
enable-cache: true

- name: Install dependencies
run: uv sync

- name: Run tests
run: uv run pytest tests/ -q
134 changes: 134 additions & 0 deletions .github/workflows/desktop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
name: desktop-build

on:
# Manual trigger from the Actions tab.
workflow_dispatch:
# Build on PRs so reviewers can download installers from the run's artifacts.
# Path filter keeps unrelated PRs from triggering the desktop build matrix.
pull_request:
paths:
- "desktop/**"
- "dashboard/**"
- "src/contextseek/**"
- "scripts/build_dashboard.sh"
- "scripts/build_python_runtime.sh"
- "scripts/package_desktop.sh"
- ".github/workflows/desktop.yml"
- "pyproject.toml"
# Release builds on version tags.
push:
tags:
- "desktop-v*"

jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
- os: macos-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 20

- uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Install Rust
uses: dtolnay/rust-toolchain@stable

- name: Linux build deps (webkit2gtk, etc.)
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev \
librsvg2-dev patchelf libssl-dev

- name: Install Python sidecar deps
run: |
python -m pip install --upgrade pip
pip install -e ".[http,openai]" pyinstaller

- name: Install Tauri CLI
run: npm install -g @tauri-apps/cli@^2

- name: Build dashboard SPA
run: bash scripts/build_dashboard.sh

- name: Bundle Python sidecar
run: bash scripts/build_python_runtime.sh

- name: Tauri build
working-directory: desktop/tauri
# `tauri` (not `cargo tauri`) — the npm @tauri-apps/cli provides the
# standalone `tauri` binary, not the `cargo-tauri` subcommand.
run: tauri build

- name: Resolve desktop version
id: app_version
shell: bash
run: |
ver="$(python -c 'import json; print(json.load(open("desktop/tauri/src-tauri/tauri.conf.json", "r", encoding="utf-8"))["version"])')"
echo "value=${ver}" >> "$GITHUB_OUTPUT"

# NOTE: code signing / notarization require secrets (Apple Developer ID,
# Windows signing cert). Wire them here once provisioned, e.g. via
# tauri-apps/tauri-action or env vars APPLE_*, WINDOWS_CERTIFICATE.

- name: Checksums
shell: bash
run: |
cd desktop/tauri/src-tauri/target/release/bundle
find . -type f \( -name "*.AppImage" -o -name "*.deb" -o -name "*.dmg" \
-o -name "*.msi" -o -name "*.exe" \) -print0 \
| while IFS= read -r -d '' f; do
sha256sum "$f" > "$f.sha256" 2>/dev/null \
|| shasum -a 256 "$f" > "$f.sha256"
done

- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: contextseek-desktop-${{ steps.app_version.outputs.value }}-${{ matrix.os }}
path: |
desktop/tauri/src-tauri/target/release/bundle/**/*.AppImage
desktop/tauri/src-tauri/target/release/bundle/**/*.deb
desktop/tauri/src-tauri/target/release/bundle/**/*.dmg
desktop/tauri/src-tauri/target/release/bundle/**/*.msi
desktop/tauri/src-tauri/target/release/bundle/**/*.exe
desktop/tauri/src-tauri/target/release/bundle/**/*.sha256
if-no-files-found: ignore

smoke-test:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Install + smoke-test desktop-server
run: |
python -m pip install --upgrade pip
pip install -e ".[http,openai]"
bash scripts/build_dashboard.sh
# Clean config so no dev .env enables an LLM that needs a key.
printf 'STORAGE_BACKEND=sqlite\n' > /tmp/ci.env
CONTEXTSEEK_CONFIG=/tmp/ci.env contextseek desktop-server \
--port 8799 --data-dir /tmp/ci-ctx &
for i in $(seq 1 30); do
curl -sf localhost:8799/health && break || sleep 1
done
curl -sf localhost:8799/health
curl -sf localhost:8799/ | grep -qi "<div id=\"root\""
curl -sf localhost:8799/settings | grep -qi "<div id=\"root\""
echo "smoke test passed"
Loading
Loading