Parse non-strings

This commit is contained in:
Yiorgis Gozadinos 2025-09-23 16:42:40 +03:00
parent 45d0b47a02
commit 8f3055d37d
No known key found for this signature in database
3 changed files with 39 additions and 5 deletions

View file

@ -40,8 +40,9 @@ haiku-rag add-src https://example.com/article.html
# Optionally set a humanreadable title stored in the DB schema # Optionally set a humanreadable title stored in the DB schema
haiku-rag add-src /mnt/data/doc1.pdf --title "Q3 Financial Report" haiku-rag add-src /mnt/data/doc1.pdf --title "Q3 Financial Report"
# Optionally attach metadata (repeat --meta) # Optionally attach metadata (repeat --meta). Values use JSON parsing if possible:
haiku-rag add-src /mnt/data/doc1.pdf --meta source=manual --meta lang=en # numbers, booleans, null, arrays/objects; otherwise kept as strings.
haiku-rag add-src /mnt/data/doc1.pdf --meta source=manual --meta page_count=12 --meta published=true
``` ```
!!! note !!! note

View file

@ -1,7 +1,9 @@
import asyncio import asyncio
import json
import warnings import warnings
from importlib.metadata import version from importlib.metadata import version
from pathlib import Path from pathlib import Path
from typing import Any
import typer import typer
@ -137,12 +139,12 @@ def list_documents(
asyncio.run(app.list_documents()) asyncio.run(app.list_documents())
def _parse_meta_options(meta: list[str] | None) -> dict[str, str]: def _parse_meta_options(meta: list[str] | None) -> dict[str, Any]:
"""Parse repeated --meta KEY=VALUE options into a dictionary. """Parse repeated --meta KEY=VALUE options into a dictionary.
Raises a Typer error if any entry is malformed. Raises a Typer error if any entry is malformed.
""" """
result: dict[str, str] = {} result: dict[str, Any] = {}
if not meta: if not meta:
return result return result
for item in meta: for item in meta:
@ -151,7 +153,13 @@ def _parse_meta_options(meta: list[str] | None) -> dict[str, str]:
key, value = item.split("=", 1) key, value = item.split("=", 1)
if not key: if not key:
raise typer.BadParameter("--meta key cannot be empty") raise typer.BadParameter("--meta key cannot be empty")
result[key] = value # Best-effort JSON coercion: numbers, booleans, null, arrays/objects
try:
parsed = json.loads(value)
result[key] = parsed
except Exception:
# Leave as string if not valid JSON literal
result[key] = value
return result return result

View file

@ -112,6 +112,31 @@ def test_add_document_src_with_meta():
assert kwargs.get("metadata") == {"source": "manual", "lang": "en"} assert kwargs.get("metadata") == {"source": "manual", "lang": "en"}
def test_add_document_text_with_numeric_meta():
with patch("haiku.rag.app.HaikuRAGApp") as mock_app:
mock_app_instance = MagicMock()
mock_app_instance.add_document_from_text = AsyncMock()
mock_app.return_value = mock_app_instance
result = runner.invoke(
cli,
[
"add",
"some text",
"--meta",
"version=3",
"--meta",
"published=true",
],
)
assert result.exit_code == 0
mock_app_instance.add_document_from_text.assert_called_once()
_, kwargs = mock_app_instance.add_document_from_text.call_args
assert kwargs.get("text") == "some text"
assert kwargs.get("metadata") == {"version": 3, "published": True}
def test_get_document(): def test_get_document():
with patch("haiku.rag.app.HaikuRAGApp") as mock_app: with patch("haiku.rag.app.HaikuRAGApp") as mock_app:
mock_app_instance = MagicMock() mock_app_instance = MagicMock()