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
25 changes: 17 additions & 8 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
# King Context API Keys
# King Context: API Keys
# Copy this file to .env at your project root

# Required Firecrawl API key for documentation scraping
# Required for default scraper. Firecrawl API key for documentation scraping.
# Get yours at https://firecrawl.dev
FIRECRAWL_API_KEY=

# Optional — OpenRouter API key for OpenRouter LLM stages or Ollama fallback
# Scraper provider selection (king-scrape)
# Default: firecrawl (requires FIRECRAWL_API_KEY).
# Other options: crawl4ai (local, beta; bundled by 'npx @king-context/cli init', activate with 'crawl4ai-setup')
# SCRAPE_PROVIDER=firecrawl
#
# Stage-specific overrides (precedence: stage var > SCRAPE_PROVIDER > default 'firecrawl'):
# SCRAPE_DISCOVER_PROVIDER=crawl4ai
# SCRAPE_FETCH_PROVIDER=firecrawl

# Optional. OpenRouter API key for OpenRouter LLM stages or Ollama fallback.
# Get yours at https://openrouter.ai
OPENROUTER_API_KEY=

Expand Down Expand Up @@ -38,21 +47,21 @@ CONCURRENCY_OLLAMA=2
ENABLE_FALLBACK=false
FALLBACK_MODEL=google/gemini-3-flash-preview

# Required Exa API key for king-research topic-driven web search
# Powers semantic search and content retrieval for the research pipeline
# Required for king-research. Exa API key for topic-driven web search.
# Powers semantic search and content retrieval for the research pipeline.
# Get yours at https://dashboard.exa.ai/api-keys
EXA_API_KEY=

# OptionalJina API key for reranking and embeddings in king-research
# The anonymous tier works for light use; set a key to raise rate limits
# Optional. Jina API key for reranking and embeddings in king-research.
# The anonymous tier works for light use; set a key to raise rate limits.
# Get yours at https://jina.ai/api
JINA_API_KEY=

# Legacy optional alias for research query generation.
# Prefer RESEARCH_MODEL above for new configs.
OPENROUTER_MODEL_RESEARCH=

# OptionalEffort-ladder overrides for king-research
# Optional. Effort-ladder overrides for king-research.
# Uncomment any line to override the built-in defaults shown below
#RESEARCH_BASIC_QUERIES=3
#RESEARCH_MEDIUM_QUERIES=5
Expand Down
46 changes: 46 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Tests

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
test:
name: Pytest (firecrawl path)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: pip
- name: Install with firecrawl extra
run: |
python -m pip install --upgrade pip
pip install -e ".[firecrawl,dev]"
- name: Run pytest
run: pytest -q

smoke-crawl4ai:
name: Crawl4AI smoke test
runs-on: ubuntu-latest
needs: [test]
# Bloqueante por design (ADR-0011): este smoke detecta API churn entre minor
# versions de crawl4ai (>=0.8.5,<0.9). Se quebrar, queremos saber antes do
# release, nao depois. Nao mover para continue-on-error.
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: pip
- name: Install with crawl4ai extra
run: |
python -m pip install --upgrade pip
pip install -e ".[crawl4ai,dev]"
crawl4ai-setup
- name: Run smoke script
run: python scripts/smoke-crawl4ai.py
timeout-minutes: 5
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ related:
- ADR-0006
- ADR-0007
- ADR-0008
- ADR-0009
keywords:
- cli-first
- agent-retrieval
Expand All @@ -40,6 +41,7 @@ tags:




# ADR-0001: Adopt CLI-first architecture for agent retrieval

## Context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ supersedes: []
superseded_by: []
related:
- ADR-0001
- ADR-0009
keywords:
- llm-provider
- openrouter
Expand All @@ -26,6 +27,7 @@ tags:
---



# ADR-0003: Pluggable LLM provider abstraction for CLI tools

## Context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ related:
- ADR-0002
- ADR-0007
- ADR-0008
- ADR-0011
keywords:
- multi-os
- windows
Expand All @@ -33,6 +34,7 @@ tags:




# Treat multi-OS compatibility as a baseline

## Context
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
---
id: ADR-0009
title: Pluggable scraper provider abstraction for king-scrape
status: accepted
date: 2026-05-06
areas:
- cli
- scraper
- providers
- plugin-system
supersedes: []
superseded_by: []
related:
- ADR-0001
- ADR-0003
- ADR-0010
- ADR-0011
keywords:
- scraper-provider
- registry
- stage-aware
- soft-import
- python-extras
- entry-points
tags:
- architecture
- scraper
- providers
---





# ADR-0009: Pluggable scraper provider abstraction for king-scrape

## Context

O king-scrape (CLI de indexação de documentação) chama Firecrawl SaaS diretamente nas etapas discover (src/king_context/scraper/discover.py) e fetch (src/king_context/scraper/fetch.py). Essa dependência fechada contradiz o posicionamento local-first do produto declarado em ADR-0001 e cria fricção pra adoção (FIRECRAWL_API_KEY obrigatória antes de indexar a primeira documentação). ADR-0003 já estabeleceu o padrão pra LLMs (cloud default + local opt-in via abstração de provider via OpenRouter e Ollama). Falta o equivalente pra scraping.

## Decision

Criar package src/scraper_providers/ análogo ao src/llm_providers/ com Protocols separados (DiscoveryProvider, FetchProvider), registry com soft import via Python extras (pip install king-context[crawl4ai]), e stage-aware env resolution: SCRAPE_PROVIDER (global), SCRAPE_DISCOVER_PROVIDER e SCRAPE_FETCH_PROVIDER (override por stage). Suportar entry_points group king_context.scraper_providers pra plugin model futuro. Firecrawl continua como default zero-config; novos backends viram opcionais selecionáveis sem refactor de pipeline.

## Alternatives Considered

Dispatcher inline em cada stage (if provider == firecrawl: else: ...) economiza código hoje (50 linhas vs 200) mas inviabiliza plugin model via entry_points sem refactor posterior, fragmenta tratamento de soft import (ImportError precisa ser catalogado em cada stage), e perde simetria com src/llm_providers/ (custo cognitivo extra pra contributors). Provider único cobrindo discover+fetch (sem stage-aware) é mais simples conceitualmente mas inviabiliza mixing genuíno entre etapas (ex: crawl4ai discover de SPA + firecrawl fetch estável).

## Consequences

Novos backends de scraping são adicionados como módulos isolados com soft import e registro automático via entry_points. Stage-aware resolution permite mixing por etapa. Install via Python extras prepara terreno pra futuro kctx plugin install (CLI interativa) sem refactor estrutural: vira wrapper amigável em cima do mecanismo de extras + entry_points. discover.py e fetch.py passam a depender das Protocols em vez de FirecrawlApp diretamente; tests existentes precisam adaptar mocks (passar provider via param em vez de patchar SDK). Sem env var setada, comportamento default permanece idêntico ao atual (zero breaking change).

## Links

.docs/PLUGGABLE-SCRAPER-PROVIDER-ARCHITECTURE.md,src/llm_providers/
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
id: ADR-0010
title: Thin scraper provider with pipeline-owned concurrency, checkpoint, and IO
status: accepted
date: 2026-05-06
areas:
- scraper
- providers
- performance
supersedes: []
superseded_by: []
related:
- ADR-0009
keywords:
- thin-provider
- pipeline-owned
- semaphore
- checkpoint
- resume
- fetch-one
- trade-off
tags:
- architecture
- scraper
- providers
---


# ADR-0010: Thin scraper provider with pipeline-owned concurrency, checkpoint, and IO

## Context

Ao desenhar a abstração de scraper provider (ADR-0009), surgiu a escolha de onde manter concorrência, checkpoint, e gravação de .md em disco. fetch.py (src/king_context/scraper/fetch.py:53) já implementa um semaphore de concorrência (default 5, configurável via config.concurrency), resume por slug, retry com backoff, e gravação por página em .king-context/_temp/<host>/pages/<slug>.md. Esse código está em produção e testado. Cada provider candidato (Crawl4AI, Firecrawl) traz suas próprias capacidades de batching e throttling adaptativo.

## Decision

Manter as Protocols mínimas: FetchProvider expõe apenas async fetch_one(url) -> PageContent, e DiscoveryProvider expõe apenas async discover_urls(base_url) -> list[str]. Toda a lógica de semaphore, checkpoint slug-based, retry, e gravação .md continua no fetch.py (pipeline-owned). Gravação do discovered_urls.json continua no discover.py. Providers são finos: convertem 1 URL em 1 PageContent.

## Alternatives Considered

Provider gordo (cada backend implementa fetch_many com sua própria concorrência/checkpoint/IO) extrairia max performance específica de cada engine: Crawl4AI tem AdaptiveDispatcher que ajusta concurrency baseado em latência observada, Firecrawl SDK tem rate-limit handling automático. Foi descartado porque (a) duplica lógica de IO entre backends, (b) fragmenta resume semantics: trocar provider no meio de um run não retomaria do checkpoint anterior se cada um gravasse de um jeito, (c) complica testes (mock de filesystem por backend), (d) atrasa MVP. Híbrido (fetch_one mandatório + fetch_many opcional com default vindo de fetch_one paralelo) é a evolução natural se otimização virar gargalo medido.

## Consequences

Output em disco é idêntico independente do backend, contrato esperado de uma abstração de provider. Resume cross-provider funciona: URLs já baixadas com Firecrawl não são re-baixadas se o usuário trocar pra Crawl4AI no meio. Tests de provider isolam em PageContent retornado, sem mockar filesystem. Trade-off explícito: throttling adaptativo de cada backend não é usado; ambos compartilham a semaphore default de 5 concurrent. Se isso virar gargalo medido na prática, a evolução é local (não estrutural): adicionar método fetch_many opcional ao Protocol, providers podem fazer override quando vale a pena, default permanece o loop de fetch_one paralelo. Decisão consciente de ship-now > otimizar.

## Links

.docs/PLUGGABLE-SCRAPER-PROVIDER-ARCHITECTURE.md,src/king_context/scraper/fetch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
id: ADR-0011
title: Crawl4AI selected as first local scraping backend
status: accepted
date: 2026-05-06
areas:
- scraper
- providers
- dependencies
supersedes: []
superseded_by: []
related:
- ADR-0004
- ADR-0009
keywords:
- crawl4ai
- local-scraping
- playwright
- spa-rendering
- trafilatura
- opt-in
tags:
- architecture
- scraper
- backend-choice
---



# ADR-0011: Crawl4AI selected as first local scraping backend

## Context

O ADR-0009 abre a porta pra ter múltiplos backends de scraping mas precisa eleger o primeiro motor local pra acompanhar o lançamento da feature. Os candidatos avaliados foram: Crawl4AI (Apache 2.0, Playwright-based, ~65k stars, ativo 2026), trafilatura (Apache 2.0, pure Python, ~5.9k stars, usado por HuggingFace e IBM Research), Scrapy + scrapy-playwright (mais boilerplate, gold standard pra crawls de milhões de páginas), e self-host de Firecrawl OSS (heavy: Redis + Node + Playwright + Docker, viola CLI-first do ADR-0001).

## Decision

Adotar Crawl4AI como primeiro motor local opt-in. Instalável via pip install king-context[crawl4ai] && crawl4ai-setup. Implementado como Crawl4AIScraperProvider em src/scraper_providers/crawl4ai_provider.py com soft import. Cobre tanto DiscoveryProvider (via deep crawl strategies) quanto FetchProvider (via AsyncWebCrawler.arun).

## Alternatives Considered

Trafilatura + httpx + crawler thin custom era a primeira recomendação durante a sessão de design, justificada pelo footprint leve (pure Python, sem browser binary, install <20MB) e excelente extração HTML→Markdown pra docs estáticas. Foi descartada porque cobertura de SPA/JS é dia-1 essencial pra ser uma alternativa de verdade ao Firecrawl, não meio-alternativa: Vercel docs, Mintlify customizado, e dashboards renderizados em React puro precisam de Playwright. Hybrid (trafilatura fast-path + Playwright fallback) seria caminho intermediário válido mas duplica trabalho que Crawl4AI já entrega numa stack só. Self-host Firecrawl OSS exige Redis + Node + Playwright + Docker e contraria o espírito CLI-first (ADR-0001). Scrapy é overkill pra docs scraping.

## Consequences

Install do modo local exige ~300MB de Chromium via crawl4ai-setup, aceitável porque o overhead só atinge quem ativamente opt-in (default Firecrawl não muda; usuário casual nunca paga esse custo). Multi-OS baseline (ADR-0004) é mantido: Crawl4AI funciona em macOS, Linux, Windows com Playwright. API churn entre minor versions de Crawl4AI exige version pin no pyproject.toml (crawl4ai>=0.8.5,<0.9) e um teste de fumaça em CI que indexe um site conhecido. Cobertura de SPA permite indexar docs modernas sem precisar de fallback pra Firecrawl. Trafilatura permanece candidata pra um futuro terceiro backend super-leve voltado pra static-only sites (roadmap v3 do ADR-0009).

## Links

.docs/PLUGGABLE-SCRAPER-PROVIDER-ARCHITECTURE.md,https://github.com/unclecode/crawl4ai,https://github.com/adbar/trafilatura
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.4.0] - 2026-05-06

### Added

- Pluggable scraper provider abstraction for `king-scrape`. Choose backend
via `SCRAPE_PROVIDER` env var or `--provider` flag. Stage-aware overrides
via `SCRAPE_DISCOVER_PROVIDER` and `SCRAPE_FETCH_PROVIDER`. Mirrors the
layout of `src/llm_providers/` (ADR-0009, ADR-0010).
- Crawl4AI local backend (beta, ADR-0011). Bundled by default in the
`[all]` extra that `npx @king-context/cli init` runs, so the package
ships in the project venv. Activation only requires running
`crawl4ai-setup` once to download the Playwright chromium.
- `.github/workflows/test.yml` with a `test` job (pytest on the firecrawl
path) and a `smoke-crawl4ai` job that runs `scripts/smoke-crawl4ai.py`
to detect API churn across `crawl4ai>=0.8.5,<0.9` minor versions.

### Changed

- `firecrawl-py` moved from core dependencies to the `[firecrawl]` extra.
`npx @king-context/cli init` continues to install everything via the
`[all]` extra, so the default flow is preserved with no breaking
change.
- `installer/lib/python.js` now installs `king-context[all]` via PEP 508
direct reference (`king-context[all] @ git+...`), so `init` and
`update` keep pulling firecrawl-py and crawl4ai after the core
dependency move.

## [0.3.2] - 2026-05-06

### Added
Expand Down
Loading
Loading