Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
707398d
feat: parameterize chunk size in DocsiteProcessor
IZO-Ong Jul 25, 2026
a4379e9
fix: revert unauthorized nltk import and _accumulate_chunks changes t…
IZO-Ong Jul 25, 2026
6f8600a
feat: rewrite DocsiteIndexer for Postgres batch lifecycle
IZO-Ong Jul 25, 2026
a0964d5
feat: rewire main() to the Postgres batch lifecycle
IZO-Ong Jul 25, 2026
6cfc4c4
feat: rewrite DocsiteSearch for Postgres hybrid search, preserve lega…
IZO-Ong Jul 25, 2026
41d7e10
fix: order keyword CTE by rank before limiting in hybrid search
IZO-Ong Jul 25, 2026
340a2ec
feat: backend-flagged docsite search with Postgres shadow-mode compar…
IZO-Ong Jul 25, 2026
3919172
fix: correct shadow-mode docstrings and test import placement
IZO-Ong Jul 25, 2026
0bb3ade
feat: add offline golden-query recall/latency eval script
IZO-Ong Jul 25, 2026
1d780db
fix: remove orphaned threshold arg and revert unrequested noqa suppre…
IZO-Ong Jul 25, 2026
34d47cd
fix: Restore back threshold to 0.8
IZO-Ong Jul 28, 2026
1050006
fix: apply the 0.7 relevance gate to both search_documentation backends
IZO-Ong Jul 28, 2026
b28b5fc
refactor: remove shadow-mode Postgres comparison from job_chat
IZO-Ong Jul 28, 2026
ba0769b
fix: cast hybrid RRF score to float8 so results stay JSON-serializable
IZO-Ong Jul 28, 2026
73e8442
fix: use semantic strategy on Postgres so the relevance gate survives…
IZO-Ong Jul 28, 2026
5fd8ed8
feat: add per-request backend selection to search_docsite
IZO-Ong Jul 28, 2026
cbf3ae9
feat: restore Pinecone write path behind embed_docsite target param
IZO-Ong Jul 28, 2026
ed07783
fix: add type annotations to embed_docsite's new helper functions
IZO-Ong Jul 28, 2026
131830e
feat: add versioned migration runner for the docsite schema
IZO-Ong Jul 28, 2026
ab6b223
feat: report backend agreement in the offline docsite eval
IZO-Ong Jul 28, 2026
378aeab
docs: correct stale LegacyPineconeDocsiteSearch docstring
IZO-Ong Jul 28, 2026
1643730
refactor: revert unrelated churn in docsite_processor
IZO-Ong Jul 28, 2026
8a99866
refactor: revert import and formatting churn in docsite consumers
IZO-Ong Jul 28, 2026
0a021a2
refactor: drop type annotations from internal helpers
IZO-Ong Jul 28, 2026
f2f540f
refactor: extract shared docsite backend resolver
IZO-Ong Jul 28, 2026
62b77db
docs: focus docsite comments on invariants
IZO-Ong Jul 28, 2026
b8d753b
fix: run docsite migrations from the indexer, not every connection
IZO-Ong Jul 29, 2026
27ab6ac
Remove openai key test
IZO-Ong Jul 29, 2026
c2476c0
fix: return a clear 503 when the docsite schema is not initialised
IZO-Ong Jul 29, 2026
5d56511
fix: bind docsite embeddings as pgvector Vector
IZO-Ong Jul 29, 2026
0725262
fix: commit before toggling autocommit, and mark failed batches
IZO-Ong Jul 29, 2026
b5946bd
test: add Postgres docsite roundtrip integration suite
IZO-Ong Jul 29, 2026
947e585
fix: don't let a pruning failure invalidate the batch just promoted
IZO-Ong Jul 29, 2026
a303af8
fix: change integration test to use 127.0.0.1 instead of localhost to…
IZO-Ong Jul 29, 2026
74889bf
fix: update env example to support docsite backend and postgres test …
IZO-Ong Jul 29, 2026
483de4e
fix: run_eval compares postgres semantic, and loads dotenv
IZO-Ong Jul 29, 2026
590b808
Add results from running golden queries
IZO-Ong Jul 30, 2026
9fb9f11
fix: import ordering and streamline comments
IZO-Ong Jul 30, 2026
6cbaec1
lint: fix B008 and B904 linting issues
IZO-Ong Jul 30, 2026
ad6e2e8
lint: fix B905, RUF059, edited docs in helpers
IZO-Ong Jul 30, 2026
4bf19d4
fix: lint F401 and docsite name accepting in pinecone
IZO-Ong Jul 30, 2026
3b78106
feat: add results for each query for run_eval.py
IZO-Ong Jul 30, 2026
b994b6f
refactor: use a timestamped migration filename
IZO-Ong Aug 7, 2026
1b401d8
test: expand golden query set to 30 categorised queries
IZO-Ong Aug 7, 2026
4ae1fb1
feat: report eval recall per query category
IZO-Ong Aug 7, 2026
2fed53a
feat: resolve docsite eval batches by chunk size
IZO-Ong Aug 7, 2026
f9e30d0
feat: compare semantic and hybrid across chunk sizes in the eval
IZO-Ong Aug 7, 2026
10263d7
docs: update comments in roundtrip test
IZO-Ong Aug 11, 2026
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: 7 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,17 @@
ANTHROPIC_API_KEY=sk-YOUR-API-KEY-HERE

OPENAI_API_KEY=sk-YOUR-API-KEY-HERE
PINECONE_KEY=YOUR-API-KEY-HERE
PINECONE_API_KEY=YOUR-API-KEY-HERE
POSTGRES_URL=postgresql://localhost:5432/apollo_dev
SENTRY_DSN=YOUR-API-KEY-HERE
GITHUB_TOKEN=KEY

# Which backend serves docsite search reads: 'pinecone' (default) or 'postgres'.
DOCSITE_SEARCH_BACKEND=pinecone

# Database for the Postgres docsite integration suite
POSTGRES_TEST_URL=postgresql://postgres:postgres@127.0.0.1:5433/postgres

# Langfuse observability
LANGFUSE_SECRET_KEY=sk-lf-...
LANGFUSE_PUBLIC_KEY=pk-lf-...
Expand Down
134 changes: 133 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ psycopg2-binary = "^2.9.10"
langfuse = "^4.14.1"
opentelemetry-instrumentation-anthropic = "^0.62.1"
opentelemetry-instrumentation-threading = "0.65b0"
pgvector = "^0.5.0"
pandas = "^2.2"

[tool.poetry.group.dev]
optional = false
Expand All @@ -50,6 +52,7 @@ testpaths = [
"services/job_chat/tests",
"services/latest_adaptors/tests",
"services/search_docsite/tests",
"services/embed_docsite/tests",
"services/tools",
]

Expand Down
64 changes: 64 additions & 0 deletions services/db_migrations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""Versioned schema migrations for the Python-owned docs database (POSTGRES_URL).

Applies .sql files in lexical order, records applied filenames so re-runs are a
no-op, and takes an advisory lock so concurrent starters queue.

The tracking table (_migrations_docs) and lock key are distinct from the
TypeScript runner's in platform/src/db/migrate.ts, because APOLLO_CLIENTS_DB_URL
falls back to POSTGRES_URL locally and both runners can target one database.
"""

from pathlib import Path

from util import create_logger

logger = create_logger("db_migrations")

MIGRATIONS_DIR = Path(__file__).parent / "migrations"

# Distinct from the TypeScript runner's 8314_2025 so the two never block each other.
MIGRATION_LOCK_KEY = 8314_2026

CREATE_TRACKING_TABLE_SQL = """
CREATE TABLE IF NOT EXISTS _migrations_docs (
filename TEXT PRIMARY KEY,
applied_at TIMESTAMPTZ NOT NULL DEFAULT now()
)
"""


def _migration_files():
"""Every .sql file in the migrations directory, in lexical order."""
if not MIGRATIONS_DIR.is_dir():
return []
return sorted(MIGRATIONS_DIR.glob("*.sql"))


def run_migrations(conn):
"""Apply any migrations not yet recorded. Returns the count applied this run.

Everything happens in one transaction: the advisory lock is held for its
duration, so a racing process waits and then sees the migrations already
recorded rather than colliding on CREATE TABLE.
"""
files = _migration_files()

with conn.cursor() as cur:
cur.execute("SELECT pg_advisory_xact_lock(%s)", (MIGRATION_LOCK_KEY,))
cur.execute(CREATE_TRACKING_TABLE_SQL)

cur.execute("SELECT filename FROM _migrations_docs")
already_applied = {row[0] for row in cur.fetchall()}

pending = [f for f in files if f.name not in already_applied]
for path in pending:
logger.info(f"Applying migration {path.name}")
cur.execute(path.read_text(encoding="utf-8"))
cur.execute("INSERT INTO _migrations_docs (filename) VALUES (%s)", (path.name,))

conn.commit()

if pending:
logger.info(f"Applied {len(pending)} migration(s)")

return len(pending)
32 changes: 26 additions & 6 deletions services/embed_docsite/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
## Embed Docsite (RAG)

This service embeds the OpenFn Documentation to a vector database. It downloads, chunks, processes metadata, embeds and uploads the documentation to a vector database (Pinecone).
This service embeds the OpenFn Documentation to a vector database. It downloads,
chunks, processes metadata, embeds and uploads the documentation to a vector
database (Pinecone).

## Usage - Embedding OpenFn Documentation

The vector database used here is Pinecone. To obtain the env variables follow these steps:
The vector database used here is Pinecone. To obtain the env variables follow
these steps:

1. Create an account on [Pinecone] and set up a free cluster.
2. Obtain the URL and token for the cluster and add them to the `.env` file.
Expand All @@ -15,26 +18,43 @@ The vector database used here is Pinecone. To obtain the env variables follow th
```bash
openfn apollo embed_docsite tmp/payload.json
```

To run directly from this repo (note that the server must be started):

```bash
bun py embed_docsite tmp/payload.json -O
```

## Implementation
The service uses the DocsiteProcessor to download the documentation and chunk it into smaller parts. The DocsiteIndexer formats metadata, creates a new collection, embeds the chunked texts (OpenAI) and uploads them into the vector database (Pinecone).

The service uses the DocsiteProcessor to download the documentation and chunk it
into smaller parts. The DocsiteIndexer formats metadata, creates a new
collection, embeds the chunked texts (OpenAI) and uploads them into the vector
database (Pinecone).

The chunked texts can be viewed in `tmp/split_sections`.

## Payload Reference

The write target is independent of the read backend (`DOCSITE_SEARCH_BACKEND`),
so a Postgres batch can be built while Pinecone still serves search traffic.

The input payload is a JSON object. All parameters are optional:

```js
{
"target": "pinecone", // 'pinecone' | 'postgres'. Defaults to pinecone. Chooses the write destination.
"docs_to_upload": ["adaptor_docs", "general_docs", "adaptor_functions"], // Select from 3 types of documentation to upload
"collection_name": "docsite-20250225", // Name of the collection in the vector database (defaults to the current date)
"index_name": "docsite", // Name of the index in the vector database (an index contains collections; defaults to docsite)
"docs_to_ignore": ["job-examples.md", "release-notes.md"], // Titles of documents that should not be indexed
"max_total_collections" : 3 // The max number of collections to keep in the vector database. This will delete older collections by date.
"chunk_target_length": 1000, // Target chunk size in characters
"chunk_min_length": 700, // Minimum chunk size before merging with the next split

// Pinecone target only:
"collection_name": "docsite-20250225", // Namespace (defaults to the current timestamp)
"index_name": "docsite", // Name of the index in the vector database (an index contains collections; defaults to docsite)
"max_total_collections": 3, // The max number of collections to keep in the vector database. This will delete older collections by date.

// Postgres target only:
"keep_batches": 2 // Number of recent complete batches to retain when pruning
}
```
Empty file.
Loading