CLI vs API — Execution Paths¶
The same research pipeline can be reached through four entry points. They differ in which settings apply, which retrieval providers run, and how output is delivered. This page is the authoritative comparison — use it when batch CLI behavior does not match your YAML config.
Entry point map¶
flowchart TD
subgraph cli [CLI src/__main__.py]
B["Batch: python -m src \"query\""]
I["Interactive: python -m src"]
end
subgraph other [Programmatic / HTTP]
P["run_research() / run_research_with_result()"]
A["POST /research FastAPI"]
end
B --> H[run_research_helper]
I --> S[InteractiveResearchSession]
S --> W[run_research_with_result]
P --> W
A --> BP[build_pipeline]
BP --> W
H --> W2[run_research_with_result]
W --> PL[ResearchPipeline 11 stages]
W2 --> PL
| Entry | Source | Settings object | Provider set |
|---|---|---|---|
| CLI batch | run_research_helper() |
Constructor override — replaces retrieval.providers |
OpenAlex + Semantic Scholar only |
| CLI interactive | InteractiveResearchSession.run_full_query() |
Full AppSettings() merge |
All enabled: true in config |
| Programmatic | run_research() / run_research_with_result() |
Caller-supplied or default AppSettings() |
All enabled in config |
| HTTP API | build_pipeline(resolved_settings) |
get_settings() at app startup |
All enabled in config |
Batch CLI ignores provider YAML
run_research_helper() builds a fresh AppSettings with only OpenAlex and Semantic Scholar. Env vars like RA_RETRIEVAL__PROVIDERS__ARXIV__ENABLED=true have no effect on python -m src "query".
Code trace — batch shortcut¶
Batch mode is the default when a positional query is provided:
# src/__main__.py — batch path
asyncio.run(
run_research_helper(
args.query,
output_format=output_format,
export_formats=export_formats,
output_path=getattr(args, "output", None),
stream_progress=stream_progress,
)
)
Inside the helper, settings are hardcoded:
# src/retrieval/orchestrator.py — run_research_helper()
settings = AppSettings(
retrieval={
"per_provider_limit": k_each,
"providers": {
"openalex": {"enabled": True, "limit": k_each},
"semantic_scholar": {"enabled": True, "limit": k_each},
},
}
)
report, result = await run_research_with_result(user_text, settings=settings, ...)
Everything else in AppSettings (LLM provider, synthesis mode, stage toggles, ranking weights) still comes from YAML/env — only the provider dict is replaced.
Code trace — full pipeline¶
Interactive mode, the API, and programmatic calls use the merged settings:
# InteractiveResearchSession.run_full_query()
report, pipeline_result = await run_research_with_result(
query,
settings=self.settings, # AppSettings() — full merge
session=self.session,
store=self.store,
)
# FastAPI POST /research
pipeline = build_pipeline(resolved_settings)
result = await pipeline.execute(request.query)
run_research_with_result() always calls build_pipeline(resolved_settings) with whatever settings object was passed. Retrieval uses get_enabled_providers(settings) from the registry.
Side-by-side comparison¶
| Aspect | CLI batch | CLI interactive | API | Programmatic |
|---|---|---|---|---|
| Command / call | python -m src "q" |
python -m src |
POST /research |
await run_research(...) |
| Pipeline stages | All 11 (if enabled in config) | All 11 | All 11 | All 11 |
| Retrieval providers | OA + S2 only | Config-enabled | Config-enabled | Config-enabled |
per_provider_limit |
k_each (default 8) |
From config | From config | From config |
| Output delivery | stdout / --output |
stdout per query | JSON response | Return value |
| Formats | markdown, json, html, pdf | Same | markdown, json, html only | Caller renders |
Citation --export |
After report on stdout | Startup flags + follow-up export … |
export request field |
Via render_report_output |
| Session memory | Not wired (see below) | SQLite + follow-ups | Not exposed | Optional session/store args |
| Progress | stderr (unless --no-progress) |
stderr | Not streamed | Configurable |
| Setup gate | ensure_setup() always |
Same | Not automatic | Caller responsibility |
When to use which path¶
| Goal | Recommended path |
|---|---|
| Quick one-off query, default providers OK | CLI batch |
| Enable arXiv, CrossRef, or stub providers | Interactive, API, or programmatic |
| Follow-up filters without re-retrieval | CLI interactive |
| Integrate with another service | FastAPI or run_research() |
| Script JSON output | CLI batch --format json (OA+S2) or API |
| Validate provider config changes | Interactive or API — not batch CLI |
See Configuration cookbook — Enable arXiv + CrossRef.
--session flag (batch)¶
The CLI defines --session to enable SQLite memory in batch mode:
pipenv run python -m src --session "your query"
Not yet wired in main.py
As of the current codebase, args.session is parsed but not passed to run_research_helper(). Batch runs do not persist to SQLite despite the flag. Use interactive mode for session memory and follow-ups, or call run_research_with_result(..., session=..., store=...) programmatically.
Output and rendering¶
All paths converge on render_report_output() in src/reporting/output.py for format-specific rendering. Differences:
- CLI batch — prints to stdout; writes
--outputfile; appends--exportblocks after the main report - Interactive — prints each result; follow-ups re-render from cached
last_report - API — returns both
report(JSON dict) andrendered(string or nested JSON) in the response body
See Output formats for renderer details.
Configuration that applies everywhere¶
These settings apply on all paths (including batch CLI), because they are not overridden by run_research_helper():
- LLM provider and model (
RA_LLM__*) - Synthesis / query expansion mode (
RA_SYNTHESIS__*,RA_QUERY_EXPANSION__*) - Pipeline stage toggles (
RA_PIPELINE__ENABLED_STAGES__*) - Ranking and relevance weights
- Debug and progress flags (
RA_PIPELINE__DEBUG,RA_PIPELINE__STREAM_PROGRESS)
Only retrieval provider selection differs on the batch shortcut.
Programmatic example — full provider control¶
import asyncio
from src.config.settings import AppSettings
from src.retrieval.orchestrator import run_research_with_result
from src.reporting.output import render_report_output
async def main():
settings = AppSettings() # loads YAML + env
settings.retrieval.providers["arxiv"].enabled = True
report, result = await run_research_with_result(
"transformer attention mechanisms",
settings=settings,
)
print(render_report_output(report, output_format="markdown", partial=result.partial))
asyncio.run(main())
Related pages¶
- CLI reference — flags and startup behavior
- Interactive sessions — follow-up commands and SQLite
- API overview — install and response shape
- Retrieval overview — provider registry and concurrency
- Configuration cookbook — recipes per use case