haiku.rag-slim 0.16.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of haiku.rag-slim might be problematic. Click here for more details.
- haiku/rag/__init__.py +0 -0
- haiku/rag/app.py +542 -0
- haiku/rag/chunker.py +65 -0
- haiku/rag/cli.py +466 -0
- haiku/rag/client.py +731 -0
- haiku/rag/config/__init__.py +74 -0
- haiku/rag/config/loader.py +94 -0
- haiku/rag/config/models.py +99 -0
- haiku/rag/embeddings/__init__.py +49 -0
- haiku/rag/embeddings/base.py +25 -0
- haiku/rag/embeddings/ollama.py +28 -0
- haiku/rag/embeddings/openai.py +26 -0
- haiku/rag/embeddings/vllm.py +29 -0
- haiku/rag/embeddings/voyageai.py +27 -0
- haiku/rag/graph/__init__.py +26 -0
- haiku/rag/graph/agui/__init__.py +53 -0
- haiku/rag/graph/agui/cli_renderer.py +135 -0
- haiku/rag/graph/agui/emitter.py +197 -0
- haiku/rag/graph/agui/events.py +254 -0
- haiku/rag/graph/agui/server.py +310 -0
- haiku/rag/graph/agui/state.py +34 -0
- haiku/rag/graph/agui/stream.py +86 -0
- haiku/rag/graph/common/__init__.py +5 -0
- haiku/rag/graph/common/models.py +42 -0
- haiku/rag/graph/common/nodes.py +265 -0
- haiku/rag/graph/common/prompts.py +46 -0
- haiku/rag/graph/common/utils.py +44 -0
- haiku/rag/graph/deep_qa/__init__.py +1 -0
- haiku/rag/graph/deep_qa/dependencies.py +27 -0
- haiku/rag/graph/deep_qa/graph.py +243 -0
- haiku/rag/graph/deep_qa/models.py +20 -0
- haiku/rag/graph/deep_qa/prompts.py +59 -0
- haiku/rag/graph/deep_qa/state.py +56 -0
- haiku/rag/graph/research/__init__.py +3 -0
- haiku/rag/graph/research/common.py +87 -0
- haiku/rag/graph/research/dependencies.py +151 -0
- haiku/rag/graph/research/graph.py +295 -0
- haiku/rag/graph/research/models.py +166 -0
- haiku/rag/graph/research/prompts.py +107 -0
- haiku/rag/graph/research/state.py +85 -0
- haiku/rag/logging.py +56 -0
- haiku/rag/mcp.py +245 -0
- haiku/rag/monitor.py +194 -0
- haiku/rag/qa/__init__.py +33 -0
- haiku/rag/qa/agent.py +93 -0
- haiku/rag/qa/prompts.py +60 -0
- haiku/rag/reader.py +135 -0
- haiku/rag/reranking/__init__.py +63 -0
- haiku/rag/reranking/base.py +13 -0
- haiku/rag/reranking/cohere.py +34 -0
- haiku/rag/reranking/mxbai.py +28 -0
- haiku/rag/reranking/vllm.py +44 -0
- haiku/rag/reranking/zeroentropy.py +59 -0
- haiku/rag/store/__init__.py +4 -0
- haiku/rag/store/engine.py +309 -0
- haiku/rag/store/models/__init__.py +4 -0
- haiku/rag/store/models/chunk.py +17 -0
- haiku/rag/store/models/document.py +17 -0
- haiku/rag/store/repositories/__init__.py +9 -0
- haiku/rag/store/repositories/chunk.py +442 -0
- haiku/rag/store/repositories/document.py +261 -0
- haiku/rag/store/repositories/settings.py +165 -0
- haiku/rag/store/upgrades/__init__.py +62 -0
- haiku/rag/store/upgrades/v0_10_1.py +64 -0
- haiku/rag/store/upgrades/v0_9_3.py +112 -0
- haiku/rag/utils.py +211 -0
- haiku_rag_slim-0.16.0.dist-info/METADATA +128 -0
- haiku_rag_slim-0.16.0.dist-info/RECORD +71 -0
- haiku_rag_slim-0.16.0.dist-info/WHEEL +4 -0
- haiku_rag_slim-0.16.0.dist-info/entry_points.txt +2 -0
- haiku_rag_slim-0.16.0.dist-info/licenses/LICENSE +7 -0
haiku/rag/cli.py
ADDED
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
import warnings
|
|
4
|
+
from importlib.metadata import version
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
from dotenv import load_dotenv
|
|
10
|
+
|
|
11
|
+
from haiku.rag.app import HaikuRAGApp
|
|
12
|
+
from haiku.rag.config import (
|
|
13
|
+
AppConfig,
|
|
14
|
+
find_config_file,
|
|
15
|
+
get_config,
|
|
16
|
+
load_yaml_config,
|
|
17
|
+
set_config,
|
|
18
|
+
)
|
|
19
|
+
from haiku.rag.logging import configure_cli_logging
|
|
20
|
+
from haiku.rag.utils import is_up_to_date
|
|
21
|
+
|
|
22
|
+
# Load environment variables from .env file for API keys and service URLs
|
|
23
|
+
load_dotenv()
|
|
24
|
+
|
|
25
|
+
cli = typer.Typer(
|
|
26
|
+
context_settings={"help_option_names": ["-h", "--help"]}, no_args_is_help=True
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def create_app(db: Path | None = None) -> HaikuRAGApp:
|
|
31
|
+
"""Create HaikuRAGApp with loaded config and resolved database path.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
db: Optional database path. If None, uses path from config.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
HaikuRAGApp instance with proper config and db path.
|
|
38
|
+
"""
|
|
39
|
+
config = get_config()
|
|
40
|
+
db_path = db if db else config.storage.data_dir / "haiku.rag.lancedb"
|
|
41
|
+
return HaikuRAGApp(db_path=db_path, config=config)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
async def check_version():
|
|
45
|
+
"""Check if haiku.rag is up to date and show warning if not."""
|
|
46
|
+
up_to_date, current_version, latest_version = await is_up_to_date()
|
|
47
|
+
if not up_to_date:
|
|
48
|
+
typer.echo(
|
|
49
|
+
f"Warning: haiku.rag is outdated. Current: {current_version}, Latest: {latest_version}",
|
|
50
|
+
)
|
|
51
|
+
typer.echo("Please update.")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def version_callback(value: bool):
|
|
55
|
+
if value:
|
|
56
|
+
v = version("haiku.rag-slim")
|
|
57
|
+
typer.echo(f"haiku.rag version {v}")
|
|
58
|
+
raise typer.Exit()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@cli.callback()
|
|
62
|
+
def main(
|
|
63
|
+
_version: bool = typer.Option(
|
|
64
|
+
False,
|
|
65
|
+
"-v",
|
|
66
|
+
"--version",
|
|
67
|
+
callback=version_callback,
|
|
68
|
+
help="Show version and exit",
|
|
69
|
+
),
|
|
70
|
+
config: Path | None = typer.Option(
|
|
71
|
+
None,
|
|
72
|
+
"--config",
|
|
73
|
+
help="Path to YAML configuration file",
|
|
74
|
+
),
|
|
75
|
+
):
|
|
76
|
+
"""haiku.rag CLI - Vector database RAG system"""
|
|
77
|
+
# Load config from --config, local folder, or default directory
|
|
78
|
+
config_path = find_config_file(cli_path=config)
|
|
79
|
+
if config_path:
|
|
80
|
+
yaml_data = load_yaml_config(config_path)
|
|
81
|
+
loaded_config = AppConfig.model_validate(yaml_data)
|
|
82
|
+
set_config(loaded_config)
|
|
83
|
+
|
|
84
|
+
# Configure logging minimally for CLI context
|
|
85
|
+
if get_config().environment == "development":
|
|
86
|
+
# Lazy import logfire only in development
|
|
87
|
+
try:
|
|
88
|
+
import logfire # type: ignore
|
|
89
|
+
|
|
90
|
+
logfire.configure(send_to_logfire="if-token-present")
|
|
91
|
+
logfire.instrument_pydantic_ai()
|
|
92
|
+
except Exception:
|
|
93
|
+
pass
|
|
94
|
+
else:
|
|
95
|
+
configure_cli_logging()
|
|
96
|
+
warnings.filterwarnings("ignore")
|
|
97
|
+
|
|
98
|
+
# Run version check before any command
|
|
99
|
+
try:
|
|
100
|
+
asyncio.run(check_version())
|
|
101
|
+
except Exception:
|
|
102
|
+
# Do not block CLI on version check issues
|
|
103
|
+
pass
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@cli.command("list", help="List all stored documents")
|
|
107
|
+
def list_documents(
|
|
108
|
+
db: Path | None = typer.Option(
|
|
109
|
+
None,
|
|
110
|
+
"--db",
|
|
111
|
+
help="Path to the LanceDB database file",
|
|
112
|
+
),
|
|
113
|
+
filter: str | None = typer.Option(
|
|
114
|
+
None,
|
|
115
|
+
"--filter",
|
|
116
|
+
"-f",
|
|
117
|
+
help="SQL WHERE clause to filter documents (e.g., \"uri LIKE '%arxiv%'\")",
|
|
118
|
+
),
|
|
119
|
+
):
|
|
120
|
+
app = create_app(db)
|
|
121
|
+
asyncio.run(app.list_documents(filter=filter))
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _parse_meta_options(meta: list[str] | None) -> dict[str, Any]:
|
|
125
|
+
"""Parse repeated --meta KEY=VALUE options into a dictionary.
|
|
126
|
+
|
|
127
|
+
Raises a Typer error if any entry is malformed.
|
|
128
|
+
"""
|
|
129
|
+
result: dict[str, Any] = {}
|
|
130
|
+
if not meta:
|
|
131
|
+
return result
|
|
132
|
+
for item in meta:
|
|
133
|
+
if "=" not in item:
|
|
134
|
+
raise typer.BadParameter("--meta must be in KEY=VALUE format")
|
|
135
|
+
key, value = item.split("=", 1)
|
|
136
|
+
if not key:
|
|
137
|
+
raise typer.BadParameter("--meta key cannot be empty")
|
|
138
|
+
# Best-effort JSON coercion: numbers, booleans, null, arrays/objects
|
|
139
|
+
try:
|
|
140
|
+
parsed = json.loads(value)
|
|
141
|
+
result[key] = parsed
|
|
142
|
+
except Exception:
|
|
143
|
+
# Leave as string if not valid JSON literal
|
|
144
|
+
result[key] = value
|
|
145
|
+
return result
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@cli.command("add", help="Add a document from text input")
|
|
149
|
+
def add_document_text(
|
|
150
|
+
text: str = typer.Argument(
|
|
151
|
+
help="The text content of the document to add",
|
|
152
|
+
),
|
|
153
|
+
meta: list[str] | None = typer.Option(
|
|
154
|
+
None,
|
|
155
|
+
"--meta",
|
|
156
|
+
help="Metadata entries as KEY=VALUE (repeatable)",
|
|
157
|
+
metavar="KEY=VALUE",
|
|
158
|
+
),
|
|
159
|
+
db: Path | None = typer.Option(
|
|
160
|
+
None,
|
|
161
|
+
"--db",
|
|
162
|
+
help="Path to the LanceDB database file",
|
|
163
|
+
),
|
|
164
|
+
):
|
|
165
|
+
app = create_app(db)
|
|
166
|
+
metadata = _parse_meta_options(meta)
|
|
167
|
+
asyncio.run(app.add_document_from_text(text=text, metadata=metadata or None))
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
@cli.command("add-src", help="Add a document from a file path, directory, or URL")
|
|
171
|
+
def add_document_src(
|
|
172
|
+
source: str = typer.Argument(
|
|
173
|
+
help="The file path, directory, or URL of the document(s) to add",
|
|
174
|
+
),
|
|
175
|
+
title: str | None = typer.Option(
|
|
176
|
+
None,
|
|
177
|
+
"--title",
|
|
178
|
+
help="Optional human-readable title to store with the document",
|
|
179
|
+
),
|
|
180
|
+
meta: list[str] | None = typer.Option(
|
|
181
|
+
None,
|
|
182
|
+
"--meta",
|
|
183
|
+
help="Metadata entries as KEY=VALUE (repeatable)",
|
|
184
|
+
metavar="KEY=VALUE",
|
|
185
|
+
),
|
|
186
|
+
db: Path | None = typer.Option(
|
|
187
|
+
None,
|
|
188
|
+
"--db",
|
|
189
|
+
help="Path to the LanceDB database file",
|
|
190
|
+
),
|
|
191
|
+
):
|
|
192
|
+
app = create_app(db)
|
|
193
|
+
metadata = _parse_meta_options(meta)
|
|
194
|
+
asyncio.run(
|
|
195
|
+
app.add_document_from_source(
|
|
196
|
+
source=source, title=title, metadata=metadata or None
|
|
197
|
+
)
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
@cli.command("get", help="Get and display a document by its ID")
|
|
202
|
+
def get_document(
|
|
203
|
+
doc_id: str = typer.Argument(
|
|
204
|
+
help="The ID of the document to get",
|
|
205
|
+
),
|
|
206
|
+
db: Path | None = typer.Option(
|
|
207
|
+
None,
|
|
208
|
+
"--db",
|
|
209
|
+
help="Path to the LanceDB database file",
|
|
210
|
+
),
|
|
211
|
+
):
|
|
212
|
+
app = create_app(db)
|
|
213
|
+
asyncio.run(app.get_document(doc_id=doc_id))
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@cli.command("delete", help="Delete a document by its ID")
|
|
217
|
+
def delete_document(
|
|
218
|
+
doc_id: str = typer.Argument(
|
|
219
|
+
help="The ID of the document to delete",
|
|
220
|
+
),
|
|
221
|
+
db: Path | None = typer.Option(
|
|
222
|
+
None,
|
|
223
|
+
"--db",
|
|
224
|
+
help="Path to the LanceDB database file",
|
|
225
|
+
),
|
|
226
|
+
):
|
|
227
|
+
app = create_app(db)
|
|
228
|
+
asyncio.run(app.delete_document(doc_id=doc_id))
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
# Add alias `rm` for delete
|
|
232
|
+
cli.command("rm", help="Alias for delete: remove a document by its ID")(delete_document)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
@cli.command("search", help="Search for documents by a query")
|
|
236
|
+
def search(
|
|
237
|
+
query: str = typer.Argument(
|
|
238
|
+
help="The search query to use",
|
|
239
|
+
),
|
|
240
|
+
limit: int = typer.Option(
|
|
241
|
+
5,
|
|
242
|
+
"--limit",
|
|
243
|
+
"-l",
|
|
244
|
+
help="Maximum number of results to return",
|
|
245
|
+
),
|
|
246
|
+
filter: str | None = typer.Option(
|
|
247
|
+
None,
|
|
248
|
+
"--filter",
|
|
249
|
+
"-f",
|
|
250
|
+
help="SQL WHERE clause to filter documents (e.g., \"uri LIKE '%arxiv%'\")",
|
|
251
|
+
),
|
|
252
|
+
db: Path | None = typer.Option(
|
|
253
|
+
None,
|
|
254
|
+
"--db",
|
|
255
|
+
help="Path to the LanceDB database file",
|
|
256
|
+
),
|
|
257
|
+
):
|
|
258
|
+
app = create_app(db)
|
|
259
|
+
asyncio.run(app.search(query=query, limit=limit, filter=filter))
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
@cli.command("ask", help="Ask a question using the QA agent")
|
|
263
|
+
def ask(
|
|
264
|
+
question: str = typer.Argument(
|
|
265
|
+
help="The question to ask",
|
|
266
|
+
),
|
|
267
|
+
db: Path | None = typer.Option(
|
|
268
|
+
None,
|
|
269
|
+
"--db",
|
|
270
|
+
help="Path to the LanceDB database file",
|
|
271
|
+
),
|
|
272
|
+
cite: bool = typer.Option(
|
|
273
|
+
False,
|
|
274
|
+
"--cite",
|
|
275
|
+
help="Include citations in the response",
|
|
276
|
+
),
|
|
277
|
+
deep: bool = typer.Option(
|
|
278
|
+
False,
|
|
279
|
+
"--deep",
|
|
280
|
+
help="Use deep multi-agent QA for complex questions",
|
|
281
|
+
),
|
|
282
|
+
verbose: bool = typer.Option(
|
|
283
|
+
False,
|
|
284
|
+
"--verbose",
|
|
285
|
+
help="Show verbose progress output (only with --deep)",
|
|
286
|
+
),
|
|
287
|
+
):
|
|
288
|
+
app = create_app(db)
|
|
289
|
+
asyncio.run(app.ask(question=question, cite=cite, deep=deep, verbose=verbose))
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
@cli.command("research", help="Run multi-agent research and output a concise report")
|
|
293
|
+
def research(
|
|
294
|
+
question: str = typer.Argument(
|
|
295
|
+
help="The research question to investigate",
|
|
296
|
+
),
|
|
297
|
+
db: Path | None = typer.Option(
|
|
298
|
+
None,
|
|
299
|
+
"--db",
|
|
300
|
+
help="Path to the LanceDB database file",
|
|
301
|
+
),
|
|
302
|
+
verbose: bool = typer.Option(
|
|
303
|
+
False,
|
|
304
|
+
"--verbose",
|
|
305
|
+
help="Show planning, searching previews, evaluation summary, and stop reason",
|
|
306
|
+
),
|
|
307
|
+
):
|
|
308
|
+
app = create_app(db)
|
|
309
|
+
asyncio.run(app.research(question=question, verbose=verbose))
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
@cli.command("settings", help="Display current configuration settings")
|
|
313
|
+
def settings():
|
|
314
|
+
config = get_config()
|
|
315
|
+
app = HaikuRAGApp(db_path=Path(), config=config)
|
|
316
|
+
app.show_settings()
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
@cli.command("init-config", help="Generate a YAML configuration file")
|
|
320
|
+
def init_config(
|
|
321
|
+
output: Path = typer.Argument(
|
|
322
|
+
Path("haiku.rag.yaml"),
|
|
323
|
+
help="Output path for the config file",
|
|
324
|
+
),
|
|
325
|
+
):
|
|
326
|
+
"""Generate a YAML configuration file with defaults."""
|
|
327
|
+
import yaml
|
|
328
|
+
|
|
329
|
+
from haiku.rag.config.loader import generate_default_config
|
|
330
|
+
|
|
331
|
+
if output.exists():
|
|
332
|
+
typer.echo(
|
|
333
|
+
f"Error: {output} already exists. Remove it first or choose a different path."
|
|
334
|
+
)
|
|
335
|
+
raise typer.Exit(1)
|
|
336
|
+
|
|
337
|
+
config_data = generate_default_config()
|
|
338
|
+
|
|
339
|
+
# Write YAML with comments
|
|
340
|
+
with open(output, "w") as f:
|
|
341
|
+
f.write("# haiku.rag configuration file\n")
|
|
342
|
+
f.write(
|
|
343
|
+
"# See https://ggozad.github.io/haiku.rag/configuration/ for details\n\n"
|
|
344
|
+
)
|
|
345
|
+
yaml.dump(config_data, f, default_flow_style=False, sort_keys=False)
|
|
346
|
+
|
|
347
|
+
typer.echo(f"Configuration file created: {output}")
|
|
348
|
+
typer.echo("Edit the file to customize your settings.")
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
@cli.command(
|
|
352
|
+
"rebuild",
|
|
353
|
+
help="Rebuild the database by deleting all chunks and re-indexing all documents",
|
|
354
|
+
)
|
|
355
|
+
def rebuild(
|
|
356
|
+
db: Path | None = typer.Option(
|
|
357
|
+
None,
|
|
358
|
+
"--db",
|
|
359
|
+
help="Path to the LanceDB database file",
|
|
360
|
+
),
|
|
361
|
+
):
|
|
362
|
+
app = create_app(db)
|
|
363
|
+
asyncio.run(app.rebuild())
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
@cli.command("vacuum", help="Optimize and clean up all tables to reduce disk usage")
|
|
367
|
+
def vacuum(
|
|
368
|
+
db: Path | None = typer.Option(
|
|
369
|
+
None,
|
|
370
|
+
"--db",
|
|
371
|
+
help="Path to the LanceDB database file",
|
|
372
|
+
),
|
|
373
|
+
):
|
|
374
|
+
app = create_app(db)
|
|
375
|
+
asyncio.run(app.vacuum())
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
@cli.command("info", help="Show read-only database info (no upgrades or writes)")
|
|
379
|
+
def info(
|
|
380
|
+
db: Path | None = typer.Option(
|
|
381
|
+
None,
|
|
382
|
+
"--db",
|
|
383
|
+
help="Path to the LanceDB database file",
|
|
384
|
+
),
|
|
385
|
+
):
|
|
386
|
+
app = create_app(db)
|
|
387
|
+
asyncio.run(app.info())
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
@cli.command("download-models", help="Download Docling and Ollama models per config")
|
|
391
|
+
def download_models_cmd():
|
|
392
|
+
from haiku.rag.utils import prefetch_models
|
|
393
|
+
|
|
394
|
+
try:
|
|
395
|
+
prefetch_models()
|
|
396
|
+
typer.echo("Models downloaded successfully.")
|
|
397
|
+
except Exception as e:
|
|
398
|
+
typer.echo(f"Error downloading models: {e}")
|
|
399
|
+
raise typer.Exit(1)
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
@cli.command(
|
|
403
|
+
"serve",
|
|
404
|
+
help="Start haiku.rag server. Use --monitor, --mcp, and/or --agui to enable services.",
|
|
405
|
+
)
|
|
406
|
+
def serve(
|
|
407
|
+
db: Path | None = typer.Option(
|
|
408
|
+
None,
|
|
409
|
+
"--db",
|
|
410
|
+
help="Path to the LanceDB database file",
|
|
411
|
+
),
|
|
412
|
+
monitor: bool = typer.Option(
|
|
413
|
+
False,
|
|
414
|
+
"--monitor",
|
|
415
|
+
help="Enable file monitoring",
|
|
416
|
+
),
|
|
417
|
+
mcp: bool = typer.Option(
|
|
418
|
+
False,
|
|
419
|
+
"--mcp",
|
|
420
|
+
help="Enable MCP server",
|
|
421
|
+
),
|
|
422
|
+
stdio: bool = typer.Option(
|
|
423
|
+
False,
|
|
424
|
+
"--stdio",
|
|
425
|
+
help="Run MCP server on stdio Transport (requires --mcp)",
|
|
426
|
+
),
|
|
427
|
+
mcp_port: int = typer.Option(
|
|
428
|
+
8001,
|
|
429
|
+
"--mcp-port",
|
|
430
|
+
help="Port to bind MCP server to (ignored with --stdio)",
|
|
431
|
+
),
|
|
432
|
+
agui: bool = typer.Option(
|
|
433
|
+
False,
|
|
434
|
+
"--agui",
|
|
435
|
+
help="Enable AG-UI HTTP server for graph streaming",
|
|
436
|
+
),
|
|
437
|
+
) -> None:
|
|
438
|
+
"""Start the server with selected services."""
|
|
439
|
+
# Require at least one service flag
|
|
440
|
+
if not (monitor or mcp or agui):
|
|
441
|
+
typer.echo(
|
|
442
|
+
"Error: At least one service flag (--monitor, --mcp, or --agui) must be specified"
|
|
443
|
+
)
|
|
444
|
+
raise typer.Exit(1)
|
|
445
|
+
|
|
446
|
+
if stdio and not mcp:
|
|
447
|
+
typer.echo("Error: --stdio requires --mcp")
|
|
448
|
+
raise typer.Exit(1)
|
|
449
|
+
|
|
450
|
+
app = create_app(db)
|
|
451
|
+
|
|
452
|
+
transport = "stdio" if stdio else None
|
|
453
|
+
|
|
454
|
+
asyncio.run(
|
|
455
|
+
app.serve(
|
|
456
|
+
enable_monitor=monitor,
|
|
457
|
+
enable_mcp=mcp,
|
|
458
|
+
mcp_transport=transport,
|
|
459
|
+
mcp_port=mcp_port,
|
|
460
|
+
enable_agui=agui,
|
|
461
|
+
)
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
if __name__ == "__main__":
|
|
466
|
+
cli()
|