Make --config a global CLI parameter, add it as a param to HaikuRAGApp
This commit is contained in:
parent
bf09807b56
commit
3c557d5bfe
8 changed files with 167 additions and 126 deletions
|
|
@ -1,6 +1,13 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- **Configuration**: All CLI commands now properly support `--config` parameter for specifying custom configuration files
|
||||||
|
- Configuration loading consolidated across CLI, app, and client with consistent resolution order
|
||||||
|
- `HaikuRAGApp`, MCP server, and A2A server now accept `config` parameter for programmatic configuration
|
||||||
|
- Updated CLI documentation to clarify global vs per-command options
|
||||||
|
|
||||||
## [0.14.1] - 2025-11-06
|
## [0.14.1] - 2025-11-06
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
||||||
10
docs/cli.md
10
docs/cli.md
|
|
@ -3,14 +3,20 @@
|
||||||
The `haiku-rag` CLI provides complete document management functionality.
|
The `haiku-rag` CLI provides complete document management functionality.
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
All commands support:
|
Global options (must be specified before the command):
|
||||||
|
|
||||||
|
- `--config` - Specify custom configuration file
|
||||||
|
- `--version` / `-v` - Show version and exit
|
||||||
|
|
||||||
|
Per-command options:
|
||||||
|
|
||||||
- `--db` - Specify custom database path
|
- `--db` - Specify custom database path
|
||||||
- `-h` - Show help for specific command
|
- `-h` - Show help for specific command
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```bash
|
```bash
|
||||||
haiku-rag list --db /path/to/custom.db
|
haiku-rag --config /path/to/config.yaml list
|
||||||
|
haiku-rag --config /path/to/config.yaml list --db /path/to/custom.db
|
||||||
haiku-rag add -h
|
haiku-rag add -h
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ from pathlib import Path
|
||||||
import logfire
|
import logfire
|
||||||
from pydantic_ai import Agent, RunContext
|
from pydantic_ai import Agent, RunContext
|
||||||
|
|
||||||
from haiku.rag.config import Config
|
from haiku.rag.config import AppConfig, Config
|
||||||
from haiku.rag.graph_common import get_model
|
from haiku.rag.graph_common import get_model
|
||||||
|
|
||||||
from .context import load_message_history, save_message_history
|
from .context import load_message_history, save_message_history
|
||||||
|
|
@ -42,6 +42,7 @@ __all__ = [
|
||||||
|
|
||||||
def create_a2a_app(
|
def create_a2a_app(
|
||||||
db_path: Path,
|
db_path: Path,
|
||||||
|
config: AppConfig = Config,
|
||||||
security_schemes: dict | None = None,
|
security_schemes: dict | None = None,
|
||||||
security: list[dict[str, list[str]]] | None = None,
|
security: list[dict[str, list[str]]] | None = None,
|
||||||
):
|
):
|
||||||
|
|
@ -49,6 +50,7 @@ def create_a2a_app(
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
db_path: Path to the LanceDB database
|
db_path: Path to the LanceDB database
|
||||||
|
config: App configuration
|
||||||
security_schemes: Optional security scheme definitions for the AgentCard
|
security_schemes: Optional security scheme definitions for the AgentCard
|
||||||
security: Optional security requirements for the AgentCard
|
security: Optional security requirements for the AgentCard
|
||||||
|
|
||||||
|
|
@ -57,12 +59,12 @@ def create_a2a_app(
|
||||||
"""
|
"""
|
||||||
base_storage = InMemoryStorage()
|
base_storage = InMemoryStorage()
|
||||||
storage = LRUMemoryStorage(
|
storage = LRUMemoryStorage(
|
||||||
storage=base_storage, max_contexts=Config.a2a.max_contexts
|
storage=base_storage, max_contexts=config.a2a.max_contexts
|
||||||
)
|
)
|
||||||
broker = InMemoryBroker()
|
broker = InMemoryBroker()
|
||||||
|
|
||||||
# Create the agent with native search tool
|
# Create the agent with native search tool
|
||||||
model = get_model(Config.qa.provider, Config.qa.model)
|
model = get_model(config.qa.provider, config.qa.model)
|
||||||
agent = Agent(
|
agent = Agent(
|
||||||
model=model,
|
model=model,
|
||||||
deps_type=AgentDependencies,
|
deps_type=AgentDependencies,
|
||||||
|
|
@ -115,12 +117,13 @@ def create_a2a_app(
|
||||||
broker=broker,
|
broker=broker,
|
||||||
db_path=db_path,
|
db_path=db_path,
|
||||||
agent=agent, # type: ignore
|
agent=agent, # type: ignore
|
||||||
|
config=config,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create FastA2A app with custom worker lifecycle
|
# Create FastA2A app with custom worker lifecycle
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def lifespan(app):
|
async def lifespan(app):
|
||||||
logger.info(f"Started A2A server (max contexts: {Config.a2a.max_contexts})")
|
logger.info(f"Started A2A server (max contexts: {config.a2a.max_contexts})")
|
||||||
async with app.task_manager:
|
async with app.task_manager:
|
||||||
async with worker.run():
|
async with worker.run():
|
||||||
yield
|
yield
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ from haiku.rag.a2a.context import load_message_history, save_message_history
|
||||||
from haiku.rag.a2a.models import AgentDependencies
|
from haiku.rag.a2a.models import AgentDependencies
|
||||||
from haiku.rag.a2a.skills import extract_question_from_task
|
from haiku.rag.a2a.skills import extract_question_from_task
|
||||||
from haiku.rag.client import HaikuRAG
|
from haiku.rag.client import HaikuRAG
|
||||||
|
from haiku.rag.config import AppConfig, Config
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from fasta2a import Worker # type: ignore
|
from fasta2a import Worker # type: ignore
|
||||||
|
|
@ -37,10 +38,12 @@ class ConversationalWorker(Worker[list[Message]]):
|
||||||
broker,
|
broker,
|
||||||
db_path: Path,
|
db_path: Path,
|
||||||
agent: "Agent[AgentDependencies, str]",
|
agent: "Agent[AgentDependencies, str]",
|
||||||
|
config: AppConfig = Config,
|
||||||
):
|
):
|
||||||
super().__init__(storage=storage, broker=broker)
|
super().__init__(storage=storage, broker=broker)
|
||||||
self.db_path = db_path
|
self.db_path = db_path
|
||||||
self.agent = agent
|
self.agent = agent
|
||||||
|
self.config = config
|
||||||
|
|
||||||
async def run_task(self, params: TaskSendParams) -> None:
|
async def run_task(self, params: TaskSendParams) -> None:
|
||||||
task = await self.storage.load_task(params["id"])
|
task = await self.storage.load_task(params["id"])
|
||||||
|
|
@ -62,7 +65,7 @@ class ConversationalWorker(Worker[list[Message]]):
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
async with HaikuRAG(self.db_path) as client:
|
async with HaikuRAG(self.db_path, config=self.config) as client:
|
||||||
context = await self.storage.load_context(task["context_id"]) or []
|
context = await self.storage.load_context(task["context_id"]) or []
|
||||||
message_history = load_message_history(context)
|
message_history = load_message_history(context)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ from rich.markdown import Markdown
|
||||||
from rich.progress import Progress
|
from rich.progress import Progress
|
||||||
|
|
||||||
from haiku.rag.client import HaikuRAG
|
from haiku.rag.client import HaikuRAG
|
||||||
from haiku.rag.config import Config
|
from haiku.rag.config import AppConfig, Config
|
||||||
from haiku.rag.mcp import create_mcp_server
|
from haiku.rag.mcp import create_mcp_server
|
||||||
from haiku.rag.monitor import FileWatcher
|
from haiku.rag.monitor import FileWatcher
|
||||||
from haiku.rag.research.dependencies import ResearchContext
|
from haiku.rag.research.dependencies import ResearchContext
|
||||||
|
|
@ -23,8 +23,9 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class HaikuRAGApp:
|
class HaikuRAGApp:
|
||||||
def __init__(self, db_path: Path):
|
def __init__(self, db_path: Path, config: AppConfig = Config):
|
||||||
self.db_path = db_path
|
self.db_path = db_path
|
||||||
|
self.config = config
|
||||||
self.console = Console()
|
self.console = Console()
|
||||||
|
|
||||||
async def info(self):
|
async def info(self):
|
||||||
|
|
@ -136,13 +137,13 @@ class HaikuRAGApp:
|
||||||
)
|
)
|
||||||
|
|
||||||
async def list_documents(self, filter: str | None = None):
|
async def list_documents(self, filter: str | None = None):
|
||||||
async with HaikuRAG(db_path=self.db_path) as self.client:
|
async with HaikuRAG(db_path=self.db_path, config=self.config) as self.client:
|
||||||
documents = await self.client.list_documents(filter=filter)
|
documents = await self.client.list_documents(filter=filter)
|
||||||
for doc in documents:
|
for doc in documents:
|
||||||
self._rich_print_document(doc, truncate=True)
|
self._rich_print_document(doc, truncate=True)
|
||||||
|
|
||||||
async def add_document_from_text(self, text: str, metadata: dict | None = None):
|
async def add_document_from_text(self, text: str, metadata: dict | None = None):
|
||||||
async with HaikuRAG(db_path=self.db_path) as self.client:
|
async with HaikuRAG(db_path=self.db_path, config=self.config) as self.client:
|
||||||
doc = await self.client.create_document(text, metadata=metadata)
|
doc = await self.client.create_document(text, metadata=metadata)
|
||||||
self._rich_print_document(doc, truncate=True)
|
self._rich_print_document(doc, truncate=True)
|
||||||
self.console.print(
|
self.console.print(
|
||||||
|
|
@ -152,7 +153,7 @@ class HaikuRAGApp:
|
||||||
async def add_document_from_source(
|
async def add_document_from_source(
|
||||||
self, source: str, title: str | None = None, metadata: dict | None = None
|
self, source: str, title: str | None = None, metadata: dict | None = None
|
||||||
):
|
):
|
||||||
async with HaikuRAG(db_path=self.db_path) as self.client:
|
async with HaikuRAG(db_path=self.db_path, config=self.config) as self.client:
|
||||||
result = await self.client.create_document_from_source(
|
result = await self.client.create_document_from_source(
|
||||||
source, title=title, metadata=metadata
|
source, title=title, metadata=metadata
|
||||||
)
|
)
|
||||||
|
|
@ -169,7 +170,7 @@ class HaikuRAGApp:
|
||||||
)
|
)
|
||||||
|
|
||||||
async def get_document(self, doc_id: str):
|
async def get_document(self, doc_id: str):
|
||||||
async with HaikuRAG(db_path=self.db_path) as self.client:
|
async with HaikuRAG(db_path=self.db_path, config=self.config) as self.client:
|
||||||
doc = await self.client.get_document_by_id(doc_id)
|
doc = await self.client.get_document_by_id(doc_id)
|
||||||
if doc is None:
|
if doc is None:
|
||||||
self.console.print(f"[red]Document with id {doc_id} not found.[/red]")
|
self.console.print(f"[red]Document with id {doc_id} not found.[/red]")
|
||||||
|
|
@ -177,7 +178,7 @@ class HaikuRAGApp:
|
||||||
self._rich_print_document(doc, truncate=False)
|
self._rich_print_document(doc, truncate=False)
|
||||||
|
|
||||||
async def delete_document(self, doc_id: str):
|
async def delete_document(self, doc_id: str):
|
||||||
async with HaikuRAG(db_path=self.db_path) as self.client:
|
async with HaikuRAG(db_path=self.db_path, config=self.config) as self.client:
|
||||||
deleted = await self.client.delete_document(doc_id)
|
deleted = await self.client.delete_document(doc_id)
|
||||||
if deleted:
|
if deleted:
|
||||||
self.console.print(
|
self.console.print(
|
||||||
|
|
@ -189,7 +190,7 @@ class HaikuRAGApp:
|
||||||
)
|
)
|
||||||
|
|
||||||
async def search(self, query: str, limit: int = 5, filter: str | None = None):
|
async def search(self, query: str, limit: int = 5, filter: str | None = None):
|
||||||
async with HaikuRAG(db_path=self.db_path) as self.client:
|
async with HaikuRAG(db_path=self.db_path, config=self.config) as self.client:
|
||||||
results = await self.client.search(query, limit=limit, filter=filter)
|
results = await self.client.search(query, limit=limit, filter=filter)
|
||||||
if not results:
|
if not results:
|
||||||
self.console.print("[yellow]No results found.[/yellow]")
|
self.console.print("[yellow]No results found.[/yellow]")
|
||||||
|
|
@ -212,21 +213,20 @@ class HaikuRAGApp:
|
||||||
deep: Use deep QA mode (multi-step reasoning)
|
deep: Use deep QA mode (multi-step reasoning)
|
||||||
verbose: Show verbose output
|
verbose: Show verbose output
|
||||||
"""
|
"""
|
||||||
async with HaikuRAG(db_path=self.db_path) as self.client:
|
async with HaikuRAG(db_path=self.db_path, config=self.config) as self.client:
|
||||||
try:
|
try:
|
||||||
if deep:
|
if deep:
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
|
|
||||||
from haiku.rag.config import Config
|
|
||||||
from haiku.rag.qa.deep.dependencies import DeepQAContext
|
from haiku.rag.qa.deep.dependencies import DeepQAContext
|
||||||
from haiku.rag.qa.deep.graph import build_deep_qa_graph
|
from haiku.rag.qa.deep.graph import build_deep_qa_graph
|
||||||
from haiku.rag.qa.deep.state import DeepQADeps, DeepQAState
|
from haiku.rag.qa.deep.state import DeepQADeps, DeepQAState
|
||||||
|
|
||||||
graph = build_deep_qa_graph(config=Config)
|
graph = build_deep_qa_graph(config=self.config)
|
||||||
context = DeepQAContext(
|
context = DeepQAContext(
|
||||||
original_question=question, use_citations=cite
|
original_question=question, use_citations=cite
|
||||||
)
|
)
|
||||||
state = DeepQAState.from_config(context=context, config=Config)
|
state = DeepQAState.from_config(context=context, config=self.config)
|
||||||
deps = DeepQADeps(
|
deps = DeepQADeps(
|
||||||
client=self.client, console=Console() if verbose else None
|
client=self.client, console=Console() if verbose else None
|
||||||
)
|
)
|
||||||
|
|
@ -254,18 +254,16 @@ class HaikuRAGApp:
|
||||||
question: The research question
|
question: The research question
|
||||||
verbose: Show verbose output
|
verbose: Show verbose output
|
||||||
"""
|
"""
|
||||||
async with HaikuRAG(db_path=self.db_path) as client:
|
async with HaikuRAG(db_path=self.db_path, config=self.config) as client:
|
||||||
try:
|
try:
|
||||||
from haiku.rag.config import Config
|
|
||||||
|
|
||||||
if verbose:
|
if verbose:
|
||||||
self.console.print("[bold cyan]Starting research[/bold cyan]")
|
self.console.print("[bold cyan]Starting research[/bold cyan]")
|
||||||
self.console.print(f"[bold blue]Question:[/bold blue] {question}")
|
self.console.print(f"[bold blue]Question:[/bold blue] {question}")
|
||||||
self.console.print()
|
self.console.print()
|
||||||
|
|
||||||
graph = build_research_graph(config=Config)
|
graph = build_research_graph(config=self.config)
|
||||||
context = ResearchContext(original_question=question)
|
context = ResearchContext(original_question=question)
|
||||||
state = ResearchState.from_config(context=context, config=Config)
|
state = ResearchState.from_config(context=context, config=self.config)
|
||||||
deps = ResearchDeps(
|
deps = ResearchDeps(
|
||||||
client=client, console=self.console if verbose else None
|
client=client, console=self.console if verbose else None
|
||||||
)
|
)
|
||||||
|
|
@ -341,7 +339,9 @@ class HaikuRAGApp:
|
||||||
self.console.print(f"[red]Error during research: {e}[/red]")
|
self.console.print(f"[red]Error during research: {e}[/red]")
|
||||||
|
|
||||||
async def rebuild(self):
|
async def rebuild(self):
|
||||||
async with HaikuRAG(db_path=self.db_path, skip_validation=True) as client:
|
async with HaikuRAG(
|
||||||
|
db_path=self.db_path, config=self.config, skip_validation=True
|
||||||
|
) as client:
|
||||||
try:
|
try:
|
||||||
documents = await client.list_documents()
|
documents = await client.list_documents()
|
||||||
total_docs = len(documents)
|
total_docs = len(documents)
|
||||||
|
|
@ -369,7 +369,9 @@ class HaikuRAGApp:
|
||||||
async def vacuum(self):
|
async def vacuum(self):
|
||||||
"""Run database maintenance: optimize and cleanup table history."""
|
"""Run database maintenance: optimize and cleanup table history."""
|
||||||
try:
|
try:
|
||||||
async with HaikuRAG(db_path=self.db_path, skip_validation=True) as client:
|
async with HaikuRAG(
|
||||||
|
db_path=self.db_path, config=self.config, skip_validation=True
|
||||||
|
) as client:
|
||||||
await client.vacuum()
|
await client.vacuum()
|
||||||
self.console.print(
|
self.console.print(
|
||||||
"[bold green]Vacuum completed successfully.[/bold green]"
|
"[bold green]Vacuum completed successfully.[/bold green]"
|
||||||
|
|
@ -383,7 +385,7 @@ class HaikuRAGApp:
|
||||||
self.console.print()
|
self.console.print()
|
||||||
|
|
||||||
# Get all config fields dynamically
|
# Get all config fields dynamically
|
||||||
for field_name, field_value in Config.model_dump().items():
|
for field_name, field_value in self.config.model_dump().items():
|
||||||
# Format the display value
|
# Format the display value
|
||||||
if isinstance(field_value, str) and (
|
if isinstance(field_value, str) and (
|
||||||
"key" in field_name.lower()
|
"key" in field_name.lower()
|
||||||
|
|
@ -458,18 +460,18 @@ class HaikuRAGApp:
|
||||||
a2a_port: int = 8000,
|
a2a_port: int = 8000,
|
||||||
):
|
):
|
||||||
"""Start the server with selected services."""
|
"""Start the server with selected services."""
|
||||||
async with HaikuRAG(self.db_path) as client:
|
async with HaikuRAG(self.db_path, config=self.config) as client:
|
||||||
tasks = []
|
tasks = []
|
||||||
|
|
||||||
# Start file monitor if enabled
|
# Start file monitor if enabled
|
||||||
if enable_monitor:
|
if enable_monitor:
|
||||||
monitor = FileWatcher(client=client)
|
monitor = FileWatcher(client=client, config=self.config)
|
||||||
monitor_task = asyncio.create_task(monitor.observe())
|
monitor_task = asyncio.create_task(monitor.observe())
|
||||||
tasks.append(monitor_task)
|
tasks.append(monitor_task)
|
||||||
|
|
||||||
# Start MCP server if enabled
|
# Start MCP server if enabled
|
||||||
if enable_mcp:
|
if enable_mcp:
|
||||||
server = create_mcp_server(self.db_path)
|
server = create_mcp_server(self.db_path, config=self.config)
|
||||||
|
|
||||||
async def run_mcp():
|
async def run_mcp():
|
||||||
if mcp_transport == "stdio":
|
if mcp_transport == "stdio":
|
||||||
|
|
@ -496,15 +498,15 @@ class HaikuRAGApp:
|
||||||
logger.info(f"Starting A2A server on {a2a_host}:{a2a_port}")
|
logger.info(f"Starting A2A server on {a2a_host}:{a2a_port}")
|
||||||
|
|
||||||
async def run_a2a():
|
async def run_a2a():
|
||||||
app = create_a2a_app(db_path=self.db_path)
|
app = create_a2a_app(db_path=self.db_path, config=self.config)
|
||||||
config = uvicorn.Config(
|
uvicorn_config = uvicorn.Config(
|
||||||
app,
|
app,
|
||||||
host=a2a_host,
|
host=a2a_host,
|
||||||
port=a2a_port,
|
port=a2a_port,
|
||||||
log_level="warning",
|
log_level="warning",
|
||||||
access_log=False,
|
access_log=False,
|
||||||
)
|
)
|
||||||
server = uvicorn.Server(config)
|
server = uvicorn.Server(uvicorn_config)
|
||||||
await server.serve()
|
await server.serve()
|
||||||
|
|
||||||
a2a_task = asyncio.create_task(run_a2a())
|
a2a_task = asyncio.create_task(run_a2a())
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,14 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
from haiku.rag.config import Config
|
from haiku.rag.app import HaikuRAGApp
|
||||||
|
from haiku.rag.config import (
|
||||||
|
AppConfig,
|
||||||
|
find_config_file,
|
||||||
|
get_config,
|
||||||
|
load_yaml_config,
|
||||||
|
set_config,
|
||||||
|
)
|
||||||
from haiku.rag.logging import configure_cli_logging
|
from haiku.rag.logging import configure_cli_logging
|
||||||
from haiku.rag.utils import is_up_to_date
|
from haiku.rag.utils import is_up_to_date
|
||||||
|
|
||||||
|
|
@ -24,6 +31,20 @@ cli = typer.Typer(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def create_app(db: Path | None = None) -> HaikuRAGApp:
|
||||||
|
"""Create HaikuRAGApp with loaded config and resolved database path.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
db: Optional database path. If None, uses path from config.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
HaikuRAGApp instance with proper config and db path.
|
||||||
|
"""
|
||||||
|
config = get_config()
|
||||||
|
db_path = db if db else config.storage.data_dir / "haiku.rag.lancedb"
|
||||||
|
return HaikuRAGApp(db_path=db_path, config=config)
|
||||||
|
|
||||||
|
|
||||||
async def check_version():
|
async def check_version():
|
||||||
"""Check if haiku.rag is up to date and show warning if not."""
|
"""Check if haiku.rag is up to date and show warning if not."""
|
||||||
up_to_date, current_version, latest_version = await is_up_to_date()
|
up_to_date, current_version, latest_version = await is_up_to_date()
|
||||||
|
|
@ -57,14 +78,15 @@ def main(
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
"""haiku.rag CLI - Vector database RAG system"""
|
"""haiku.rag CLI - Vector database RAG system"""
|
||||||
# Store config path in environment for config loader to use
|
# Load config from --config, local folder, or default directory
|
||||||
if config:
|
config_path = find_config_file(cli_path=config)
|
||||||
import os
|
if config_path:
|
||||||
|
yaml_data = load_yaml_config(config_path)
|
||||||
os.environ["HAIKU_RAG_CONFIG_PATH"] = str(config.absolute())
|
loaded_config = AppConfig.model_validate(yaml_data)
|
||||||
|
set_config(loaded_config)
|
||||||
|
|
||||||
# Configure logging minimally for CLI context
|
# Configure logging minimally for CLI context
|
||||||
if Config.environment == "development":
|
if get_config().environment == "development":
|
||||||
# Lazy import logfire only in development
|
# Lazy import logfire only in development
|
||||||
try:
|
try:
|
||||||
import logfire # type: ignore
|
import logfire # type: ignore
|
||||||
|
|
@ -87,8 +109,8 @@ def main(
|
||||||
|
|
||||||
@cli.command("list", help="List all stored documents")
|
@cli.command("list", help="List all stored documents")
|
||||||
def list_documents(
|
def list_documents(
|
||||||
db: Path = typer.Option(
|
db: Path | None = typer.Option(
|
||||||
Config.storage.data_dir / "haiku.rag.lancedb",
|
None,
|
||||||
"--db",
|
"--db",
|
||||||
help="Path to the LanceDB database file",
|
help="Path to the LanceDB database file",
|
||||||
),
|
),
|
||||||
|
|
@ -99,9 +121,7 @@ def list_documents(
|
||||||
help="SQL WHERE clause to filter documents (e.g., \"uri LIKE '%arxiv%'\")",
|
help="SQL WHERE clause to filter documents (e.g., \"uri LIKE '%arxiv%'\")",
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
from haiku.rag.app import HaikuRAGApp
|
app = create_app(db)
|
||||||
|
|
||||||
app = HaikuRAGApp(db_path=db)
|
|
||||||
asyncio.run(app.list_documents(filter=filter))
|
asyncio.run(app.list_documents(filter=filter))
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -140,15 +160,13 @@ def add_document_text(
|
||||||
help="Metadata entries as KEY=VALUE (repeatable)",
|
help="Metadata entries as KEY=VALUE (repeatable)",
|
||||||
metavar="KEY=VALUE",
|
metavar="KEY=VALUE",
|
||||||
),
|
),
|
||||||
db: Path = typer.Option(
|
db: Path | None = typer.Option(
|
||||||
Config.storage.data_dir / "haiku.rag.lancedb",
|
None,
|
||||||
"--db",
|
"--db",
|
||||||
help="Path to the LanceDB database file",
|
help="Path to the LanceDB database file",
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
from haiku.rag.app import HaikuRAGApp
|
app = create_app(db)
|
||||||
|
|
||||||
app = HaikuRAGApp(db_path=db)
|
|
||||||
metadata = _parse_meta_options(meta)
|
metadata = _parse_meta_options(meta)
|
||||||
asyncio.run(app.add_document_from_text(text=text, metadata=metadata or None))
|
asyncio.run(app.add_document_from_text(text=text, metadata=metadata or None))
|
||||||
|
|
||||||
|
|
@ -169,15 +187,13 @@ def add_document_src(
|
||||||
help="Metadata entries as KEY=VALUE (repeatable)",
|
help="Metadata entries as KEY=VALUE (repeatable)",
|
||||||
metavar="KEY=VALUE",
|
metavar="KEY=VALUE",
|
||||||
),
|
),
|
||||||
db: Path = typer.Option(
|
db: Path | None = typer.Option(
|
||||||
Config.storage.data_dir / "haiku.rag.lancedb",
|
None,
|
||||||
"--db",
|
"--db",
|
||||||
help="Path to the LanceDB database file",
|
help="Path to the LanceDB database file",
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
from haiku.rag.app import HaikuRAGApp
|
app = create_app(db)
|
||||||
|
|
||||||
app = HaikuRAGApp(db_path=db)
|
|
||||||
metadata = _parse_meta_options(meta)
|
metadata = _parse_meta_options(meta)
|
||||||
asyncio.run(
|
asyncio.run(
|
||||||
app.add_document_from_source(
|
app.add_document_from_source(
|
||||||
|
|
@ -191,15 +207,13 @@ def get_document(
|
||||||
doc_id: str = typer.Argument(
|
doc_id: str = typer.Argument(
|
||||||
help="The ID of the document to get",
|
help="The ID of the document to get",
|
||||||
),
|
),
|
||||||
db: Path = typer.Option(
|
db: Path | None = typer.Option(
|
||||||
Config.storage.data_dir / "haiku.rag.lancedb",
|
None,
|
||||||
"--db",
|
"--db",
|
||||||
help="Path to the LanceDB database file",
|
help="Path to the LanceDB database file",
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
from haiku.rag.app import HaikuRAGApp
|
app = create_app(db)
|
||||||
|
|
||||||
app = HaikuRAGApp(db_path=db)
|
|
||||||
asyncio.run(app.get_document(doc_id=doc_id))
|
asyncio.run(app.get_document(doc_id=doc_id))
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -208,15 +222,13 @@ def delete_document(
|
||||||
doc_id: str = typer.Argument(
|
doc_id: str = typer.Argument(
|
||||||
help="The ID of the document to delete",
|
help="The ID of the document to delete",
|
||||||
),
|
),
|
||||||
db: Path = typer.Option(
|
db: Path | None = typer.Option(
|
||||||
Config.storage.data_dir / "haiku.rag.lancedb",
|
None,
|
||||||
"--db",
|
"--db",
|
||||||
help="Path to the LanceDB database file",
|
help="Path to the LanceDB database file",
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
from haiku.rag.app import HaikuRAGApp
|
app = create_app(db)
|
||||||
|
|
||||||
app = HaikuRAGApp(db_path=db)
|
|
||||||
asyncio.run(app.delete_document(doc_id=doc_id))
|
asyncio.run(app.delete_document(doc_id=doc_id))
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -241,15 +253,13 @@ def search(
|
||||||
"-f",
|
"-f",
|
||||||
help="SQL WHERE clause to filter documents (e.g., \"uri LIKE '%arxiv%'\")",
|
help="SQL WHERE clause to filter documents (e.g., \"uri LIKE '%arxiv%'\")",
|
||||||
),
|
),
|
||||||
db: Path = typer.Option(
|
db: Path | None = typer.Option(
|
||||||
Config.storage.data_dir / "haiku.rag.lancedb",
|
None,
|
||||||
"--db",
|
"--db",
|
||||||
help="Path to the LanceDB database file",
|
help="Path to the LanceDB database file",
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
from haiku.rag.app import HaikuRAGApp
|
app = create_app(db)
|
||||||
|
|
||||||
app = HaikuRAGApp(db_path=db)
|
|
||||||
asyncio.run(app.search(query=query, limit=limit, filter=filter))
|
asyncio.run(app.search(query=query, limit=limit, filter=filter))
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -258,8 +268,8 @@ def ask(
|
||||||
question: str = typer.Argument(
|
question: str = typer.Argument(
|
||||||
help="The question to ask",
|
help="The question to ask",
|
||||||
),
|
),
|
||||||
db: Path = typer.Option(
|
db: Path | None = typer.Option(
|
||||||
Config.storage.data_dir / "haiku.rag.lancedb",
|
None,
|
||||||
"--db",
|
"--db",
|
||||||
help="Path to the LanceDB database file",
|
help="Path to the LanceDB database file",
|
||||||
),
|
),
|
||||||
|
|
@ -279,9 +289,7 @@ def ask(
|
||||||
help="Show verbose progress output (only with --deep)",
|
help="Show verbose progress output (only with --deep)",
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
from haiku.rag.app import HaikuRAGApp
|
app = create_app(db)
|
||||||
|
|
||||||
app = HaikuRAGApp(db_path=db)
|
|
||||||
asyncio.run(app.ask(question=question, cite=cite, deep=deep, verbose=verbose))
|
asyncio.run(app.ask(question=question, cite=cite, deep=deep, verbose=verbose))
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -290,8 +298,8 @@ def research(
|
||||||
question: str = typer.Argument(
|
question: str = typer.Argument(
|
||||||
help="The research question to investigate",
|
help="The research question to investigate",
|
||||||
),
|
),
|
||||||
db: Path = typer.Option(
|
db: Path | None = typer.Option(
|
||||||
Config.storage.data_dir / "haiku.rag.lancedb",
|
None,
|
||||||
"--db",
|
"--db",
|
||||||
help="Path to the LanceDB database file",
|
help="Path to the LanceDB database file",
|
||||||
),
|
),
|
||||||
|
|
@ -301,17 +309,14 @@ def research(
|
||||||
help="Show verbose progress output",
|
help="Show verbose progress output",
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
from haiku.rag.app import HaikuRAGApp
|
app = create_app(db)
|
||||||
|
|
||||||
app = HaikuRAGApp(db_path=db)
|
|
||||||
asyncio.run(app.research(question=question, verbose=verbose))
|
asyncio.run(app.research(question=question, verbose=verbose))
|
||||||
|
|
||||||
|
|
||||||
@cli.command("settings", help="Display current configuration settings")
|
@cli.command("settings", help="Display current configuration settings")
|
||||||
def settings():
|
def settings():
|
||||||
from haiku.rag.app import HaikuRAGApp
|
config = get_config()
|
||||||
|
app = HaikuRAGApp(db_path=Path(), config=config)
|
||||||
app = HaikuRAGApp(db_path=Path()) # Don't need actual DB for settings
|
|
||||||
app.show_settings()
|
app.show_settings()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -368,43 +373,37 @@ def init_config(
|
||||||
help="Rebuild the database by deleting all chunks and re-indexing all documents",
|
help="Rebuild the database by deleting all chunks and re-indexing all documents",
|
||||||
)
|
)
|
||||||
def rebuild(
|
def rebuild(
|
||||||
db: Path = typer.Option(
|
db: Path | None = typer.Option(
|
||||||
Config.storage.data_dir / "haiku.rag.lancedb",
|
None,
|
||||||
"--db",
|
"--db",
|
||||||
help="Path to the LanceDB database file",
|
help="Path to the LanceDB database file",
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
from haiku.rag.app import HaikuRAGApp
|
app = create_app(db)
|
||||||
|
|
||||||
app = HaikuRAGApp(db_path=db)
|
|
||||||
asyncio.run(app.rebuild())
|
asyncio.run(app.rebuild())
|
||||||
|
|
||||||
|
|
||||||
@cli.command("vacuum", help="Optimize and clean up all tables to reduce disk usage")
|
@cli.command("vacuum", help="Optimize and clean up all tables to reduce disk usage")
|
||||||
def vacuum(
|
def vacuum(
|
||||||
db: Path = typer.Option(
|
db: Path | None = typer.Option(
|
||||||
Config.storage.data_dir / "haiku.rag.lancedb",
|
None,
|
||||||
"--db",
|
"--db",
|
||||||
help="Path to the LanceDB database file",
|
help="Path to the LanceDB database file",
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
from haiku.rag.app import HaikuRAGApp
|
app = create_app(db)
|
||||||
|
|
||||||
app = HaikuRAGApp(db_path=db)
|
|
||||||
asyncio.run(app.vacuum())
|
asyncio.run(app.vacuum())
|
||||||
|
|
||||||
|
|
||||||
@cli.command("info", help="Show read-only database info (no upgrades or writes)")
|
@cli.command("info", help="Show read-only database info (no upgrades or writes)")
|
||||||
def info(
|
def info(
|
||||||
db: Path = typer.Option(
|
db: Path | None = typer.Option(
|
||||||
Config.storage.data_dir / "haiku.rag.lancedb",
|
None,
|
||||||
"--db",
|
"--db",
|
||||||
help="Path to the LanceDB database file",
|
help="Path to the LanceDB database file",
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
from haiku.rag.app import HaikuRAGApp
|
app = create_app(db)
|
||||||
|
|
||||||
app = HaikuRAGApp(db_path=db)
|
|
||||||
asyncio.run(app.info())
|
asyncio.run(app.info())
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -425,8 +424,8 @@ def download_models_cmd():
|
||||||
help="Start haiku.rag server. Use --monitor, --mcp, and/or --a2a to enable services.",
|
help="Start haiku.rag server. Use --monitor, --mcp, and/or --a2a to enable services.",
|
||||||
)
|
)
|
||||||
def serve(
|
def serve(
|
||||||
db: Path = typer.Option(
|
db: Path | None = typer.Option(
|
||||||
Config.storage.data_dir / "haiku.rag.lancedb",
|
None,
|
||||||
"--db",
|
"--db",
|
||||||
help="Path to the LanceDB database file",
|
help="Path to the LanceDB database file",
|
||||||
),
|
),
|
||||||
|
|
@ -478,9 +477,7 @@ def serve(
|
||||||
typer.echo("Error: --stdio requires --mcp")
|
typer.echo("Error: --stdio requires --mcp")
|
||||||
raise typer.Exit(1)
|
raise typer.Exit(1)
|
||||||
|
|
||||||
from haiku.rag.app import HaikuRAGApp
|
app = create_app(db)
|
||||||
|
|
||||||
app = HaikuRAGApp(db_path=db)
|
|
||||||
|
|
||||||
transport = "stdio" if stdio else None
|
transport = "stdio" if stdio else None
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,12 +41,36 @@ __all__ = [
|
||||||
"load_yaml_config",
|
"load_yaml_config",
|
||||||
"generate_default_config",
|
"generate_default_config",
|
||||||
"load_config_from_env",
|
"load_config_from_env",
|
||||||
|
"get_config",
|
||||||
|
"set_config",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Load config from YAML file or use defaults
|
# Global config instance - initially loads from default locations
|
||||||
config_path = find_config_file(None)
|
_config: AppConfig | None = None
|
||||||
if config_path:
|
|
||||||
yaml_data = load_yaml_config(config_path)
|
|
||||||
Config = AppConfig.model_validate(yaml_data)
|
def _load_default_config() -> AppConfig:
|
||||||
else:
|
"""Load config from default locations (used at import time)."""
|
||||||
Config = AppConfig()
|
config_path = find_config_file(None)
|
||||||
|
if config_path:
|
||||||
|
yaml_data = load_yaml_config(config_path)
|
||||||
|
return AppConfig.model_validate(yaml_data)
|
||||||
|
return AppConfig()
|
||||||
|
|
||||||
|
|
||||||
|
def set_config(config: AppConfig) -> None:
|
||||||
|
"""Set the global config instance (used by CLI to override)."""
|
||||||
|
global _config
|
||||||
|
_config = config
|
||||||
|
|
||||||
|
|
||||||
|
def get_config() -> AppConfig:
|
||||||
|
"""Get the current config instance."""
|
||||||
|
global _config
|
||||||
|
if _config is None:
|
||||||
|
_config = _load_default_config()
|
||||||
|
return _config
|
||||||
|
|
||||||
|
|
||||||
|
# Legacy compatibility - Config is the default instance
|
||||||
|
Config = _load_default_config()
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ from fastmcp import FastMCP
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from haiku.rag.client import HaikuRAG
|
from haiku.rag.client import HaikuRAG
|
||||||
from haiku.rag.config import Config
|
from haiku.rag.config import AppConfig, Config
|
||||||
from haiku.rag.research.models import ResearchReport
|
from haiku.rag.research.models import ResearchReport
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -25,7 +25,7 @@ class DocumentResult(BaseModel):
|
||||||
updated_at: str
|
updated_at: str
|
||||||
|
|
||||||
|
|
||||||
def create_mcp_server(db_path: Path) -> FastMCP:
|
def create_mcp_server(db_path: Path, config: AppConfig = Config) -> FastMCP:
|
||||||
"""Create an MCP server with the specified database path."""
|
"""Create an MCP server with the specified database path."""
|
||||||
mcp = FastMCP("haiku-rag")
|
mcp = FastMCP("haiku-rag")
|
||||||
|
|
||||||
|
|
@ -37,7 +37,7 @@ def create_mcp_server(db_path: Path) -> FastMCP:
|
||||||
) -> str | None:
|
) -> str | None:
|
||||||
"""Add a document to the RAG system from a file path."""
|
"""Add a document to the RAG system from a file path."""
|
||||||
try:
|
try:
|
||||||
async with HaikuRAG(db_path) as rag:
|
async with HaikuRAG(db_path, config=config) as rag:
|
||||||
result = await rag.create_document_from_source(
|
result = await rag.create_document_from_source(
|
||||||
Path(file_path), title=title, metadata=metadata or {}
|
Path(file_path), title=title, metadata=metadata or {}
|
||||||
)
|
)
|
||||||
|
|
@ -54,7 +54,7 @@ def create_mcp_server(db_path: Path) -> FastMCP:
|
||||||
) -> str | None:
|
) -> str | None:
|
||||||
"""Add a document to the RAG system from a URL."""
|
"""Add a document to the RAG system from a URL."""
|
||||||
try:
|
try:
|
||||||
async with HaikuRAG(db_path) as rag:
|
async with HaikuRAG(db_path, config=config) as rag:
|
||||||
result = await rag.create_document_from_source(
|
result = await rag.create_document_from_source(
|
||||||
url, title=title, metadata=metadata or {}
|
url, title=title, metadata=metadata or {}
|
||||||
)
|
)
|
||||||
|
|
@ -74,7 +74,7 @@ def create_mcp_server(db_path: Path) -> FastMCP:
|
||||||
) -> str | None:
|
) -> str | None:
|
||||||
"""Add a document to the RAG system from text content."""
|
"""Add a document to the RAG system from text content."""
|
||||||
try:
|
try:
|
||||||
async with HaikuRAG(db_path) as rag:
|
async with HaikuRAG(db_path, config=config) as rag:
|
||||||
document = await rag.create_document(
|
document = await rag.create_document(
|
||||||
content, uri, title=title, metadata=metadata or {}
|
content, uri, title=title, metadata=metadata or {}
|
||||||
)
|
)
|
||||||
|
|
@ -86,7 +86,7 @@ def create_mcp_server(db_path: Path) -> FastMCP:
|
||||||
async def search_documents(query: str, limit: int = 5) -> list[SearchResult]:
|
async def search_documents(query: str, limit: int = 5) -> list[SearchResult]:
|
||||||
"""Search the RAG system for documents using hybrid search (vector similarity + full-text search)."""
|
"""Search the RAG system for documents using hybrid search (vector similarity + full-text search)."""
|
||||||
try:
|
try:
|
||||||
async with HaikuRAG(db_path) as rag:
|
async with HaikuRAG(db_path, config=config) as rag:
|
||||||
results = await rag.search(query, limit)
|
results = await rag.search(query, limit)
|
||||||
|
|
||||||
search_results = []
|
search_results = []
|
||||||
|
|
@ -110,7 +110,7 @@ def create_mcp_server(db_path: Path) -> FastMCP:
|
||||||
async def get_document(document_id: str) -> DocumentResult | None:
|
async def get_document(document_id: str) -> DocumentResult | None:
|
||||||
"""Get a document by its ID."""
|
"""Get a document by its ID."""
|
||||||
try:
|
try:
|
||||||
async with HaikuRAG(db_path) as rag:
|
async with HaikuRAG(db_path, config=config) as rag:
|
||||||
document = await rag.get_document_by_id(document_id)
|
document = await rag.get_document_by_id(document_id)
|
||||||
|
|
||||||
if document is None:
|
if document is None:
|
||||||
|
|
@ -145,7 +145,7 @@ def create_mcp_server(db_path: Path) -> FastMCP:
|
||||||
List of DocumentResult instances matching the criteria.
|
List of DocumentResult instances matching the criteria.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
async with HaikuRAG(db_path) as rag:
|
async with HaikuRAG(db_path, config=config) as rag:
|
||||||
documents = await rag.list_documents(limit, offset, filter)
|
documents = await rag.list_documents(limit, offset, filter)
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
@ -167,7 +167,7 @@ def create_mcp_server(db_path: Path) -> FastMCP:
|
||||||
async def delete_document(document_id: str) -> bool:
|
async def delete_document(document_id: str) -> bool:
|
||||||
"""Delete a document by its ID."""
|
"""Delete a document by its ID."""
|
||||||
try:
|
try:
|
||||||
async with HaikuRAG(db_path) as rag:
|
async with HaikuRAG(db_path, config=config) as rag:
|
||||||
return await rag.delete_document(document_id)
|
return await rag.delete_document(document_id)
|
||||||
except Exception:
|
except Exception:
|
||||||
return False
|
return False
|
||||||
|
|
@ -189,18 +189,17 @@ def create_mcp_server(db_path: Path) -> FastMCP:
|
||||||
The answer as a string.
|
The answer as a string.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
async with HaikuRAG(db_path) as rag:
|
async with HaikuRAG(db_path, config=config) as rag:
|
||||||
if deep:
|
if deep:
|
||||||
from haiku.rag.config import Config
|
|
||||||
from haiku.rag.qa.deep.dependencies import DeepQAContext
|
from haiku.rag.qa.deep.dependencies import DeepQAContext
|
||||||
from haiku.rag.qa.deep.graph import build_deep_qa_graph
|
from haiku.rag.qa.deep.graph import build_deep_qa_graph
|
||||||
from haiku.rag.qa.deep.state import DeepQADeps, DeepQAState
|
from haiku.rag.qa.deep.state import DeepQADeps, DeepQAState
|
||||||
|
|
||||||
graph = build_deep_qa_graph(config=Config)
|
graph = build_deep_qa_graph(config=config)
|
||||||
context = DeepQAContext(
|
context = DeepQAContext(
|
||||||
original_question=question, use_citations=cite
|
original_question=question, use_citations=cite
|
||||||
)
|
)
|
||||||
state = DeepQAState.from_config(context=context, config=Config)
|
state = DeepQAState.from_config(context=context, config=config)
|
||||||
deps = DeepQADeps(client=rag)
|
deps = DeepQADeps(client=rag)
|
||||||
|
|
||||||
result = await graph.run(state=state, deps=deps)
|
result = await graph.run(state=state, deps=deps)
|
||||||
|
|
@ -231,10 +230,10 @@ def create_mcp_server(db_path: Path) -> FastMCP:
|
||||||
from haiku.rag.research.graph import build_research_graph
|
from haiku.rag.research.graph import build_research_graph
|
||||||
from haiku.rag.research.state import ResearchDeps, ResearchState
|
from haiku.rag.research.state import ResearchDeps, ResearchState
|
||||||
|
|
||||||
async with HaikuRAG(db_path) as rag:
|
async with HaikuRAG(db_path, config=config) as rag:
|
||||||
graph = build_research_graph(config=Config)
|
graph = build_research_graph(config=config)
|
||||||
context = ResearchContext(original_question=question)
|
context = ResearchContext(original_question=question)
|
||||||
state = ResearchState.from_config(context=context, config=Config)
|
state = ResearchState.from_config(context=context, config=config)
|
||||||
deps = ResearchDeps(client=rag)
|
deps = ResearchDeps(client=rag)
|
||||||
|
|
||||||
result = await graph.run(state=state, deps=deps)
|
result = await graph.run(state=state, deps=deps)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue