haiku.rag 0.1.0__py3-none-any.whl → 0.2.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 might be problematic. Click here for more details.

haiku/rag/app.py CHANGED
@@ -1,9 +1,13 @@
1
+ import asyncio
1
2
  from pathlib import Path
2
3
 
3
4
  from rich.console import Console
4
5
  from rich.markdown import Markdown
5
6
 
6
7
  from haiku.rag.client import HaikuRAG
8
+ from haiku.rag.config import Config
9
+ from haiku.rag.mcp import create_mcp_server
10
+ from haiku.rag.monitor import FileWatcher
7
11
  from haiku.rag.store.models.chunk import Chunk
8
12
  from haiku.rag.store.models.document import Document
9
13
 
@@ -88,20 +92,25 @@ class HaikuRAGApp:
88
92
  self.console.print(content)
89
93
  self.console.rule()
90
94
 
91
- def serve(self, transport: str | None = None):
95
+ async def serve(self, transport: str | None = None):
92
96
  """Start the MCP server."""
93
- from haiku.rag.mcp import create_mcp_server
97
+ async with HaikuRAG(self.db_path) as client:
98
+ monitor = FileWatcher(paths=Config.MONITOR_DIRECTORIES, client=client)
99
+ monitor_task = asyncio.create_task(monitor.observe())
100
+ server = create_mcp_server(self.db_path)
94
101
 
95
- server = create_mcp_server(self.db_path)
96
-
97
- if transport == "stdio":
98
- self.console.print("[green]Starting MCP server on stdio...[/green]")
99
- server.run("stdio")
100
- elif transport == "sse":
101
- self.console.print(
102
- "[green]Starting MCP server with streamable HTTP...[/green]"
103
- )
104
- server.run("sse")
105
- else:
106
- self.console.print("[green]Starting MCP server with HTTP...[/green]")
107
- server.run("streamable-http")
102
+ try:
103
+ if transport == "stdio":
104
+ await server.run_stdio_async()
105
+ elif transport == "sse":
106
+ await server.run_sse_async("sse")
107
+ else:
108
+ await server.run_http_async("streamable-http")
109
+ except KeyboardInterrupt:
110
+ pass
111
+ finally:
112
+ monitor_task.cancel()
113
+ try:
114
+ await monitor_task
115
+ except asyncio.CancelledError:
116
+ pass
haiku/rag/cli.py CHANGED
@@ -146,7 +146,7 @@ def serve(
146
146
  elif sse:
147
147
  transport = "sse"
148
148
 
149
- app.serve(transport=transport)
149
+ event_loop.run_until_complete(app.serve(transport=transport))
150
150
 
151
151
 
152
152
  if __name__ == "__main__":
haiku/rag/client.py CHANGED
@@ -88,7 +88,7 @@ class HaikuRAG:
88
88
  if not source_path.exists():
89
89
  raise ValueError(f"File does not exist: {source_path}")
90
90
 
91
- uri = str(source_path.resolve())
91
+ uri = source_path.as_uri()
92
92
  md5_hash = hashlib.md5(source_path.read_bytes()).hexdigest()
93
93
 
94
94
  # Check if document already exists
haiku/rag/config.py CHANGED
@@ -2,7 +2,7 @@ import os
2
2
  from pathlib import Path
3
3
 
4
4
  from dotenv import load_dotenv
5
- from pydantic import BaseModel
5
+ from pydantic import BaseModel, field_validator
6
6
 
7
7
  from haiku.rag.utils import get_default_data_dir
8
8
 
@@ -13,16 +13,28 @@ class AppConfig(BaseModel):
13
13
  ENV: str = "development"
14
14
 
15
15
  DEFAULT_DATA_DIR: Path = get_default_data_dir()
16
+ MONITOR_DIRECTORIES: list[Path] = []
16
17
 
17
- EMBEDDING_PROVIDER: str = "ollama"
18
- EMBEDDING_MODEL: str = "mxbai-embed-large"
19
- EMBEDDING_VECTOR_DIM: int = 1024
18
+ EMBEDDINGS_PROVIDER: str = "ollama"
19
+ EMBEDDINGS_MODEL: str = "mxbai-embed-large"
20
+ EMBEDDINGS_VECTOR_DIM: int = 1024
20
21
 
21
22
  CHUNK_SIZE: int = 256
22
23
  CHUNK_OVERLAP: int = 32
23
24
 
24
25
  OLLAMA_BASE_URL: str = "http://localhost:11434"
25
26
 
27
+ @field_validator("MONITOR_DIRECTORIES", mode="before")
28
+ @classmethod
29
+ def parse_monitor_directories(cls, v):
30
+ if isinstance(v, str):
31
+ if not v.strip():
32
+ return []
33
+ return [
34
+ Path(path.strip()).absolute() for path in v.split(",") if path.strip()
35
+ ]
36
+ return v
37
+
26
38
 
27
39
  # Expose Config object for app to import
28
40
  Config = AppConfig.model_validate(os.environ)
@@ -8,10 +8,10 @@ def get_embedder() -> EmbedderBase:
8
8
  Factory function to get the appropriate embedder based on the configuration.
9
9
  """
10
10
 
11
- if Config.EMBEDDING_PROVIDER == "ollama":
12
- return OllamaEmbedder(Config.EMBEDDING_MODEL, Config.EMBEDDING_VECTOR_DIM)
11
+ if Config.EMBEDDINGS_PROVIDER == "ollama":
12
+ return OllamaEmbedder(Config.EMBEDDINGS_MODEL, Config.EMBEDDINGS_VECTOR_DIM)
13
13
 
14
- if Config.EMBEDDING_PROVIDER == "voyageai":
14
+ if Config.EMBEDDINGS_PROVIDER == "voyageai":
15
15
  try:
16
16
  from haiku.rag.embeddings.voyageai import Embedder as VoyageAIEmbedder
17
17
  except ImportError:
@@ -20,5 +20,17 @@ def get_embedder() -> EmbedderBase:
20
20
  "Please install haiku.rag with the 'voyageai' extra:"
21
21
  "uv pip install haiku.rag --extra voyageai"
22
22
  )
23
- return VoyageAIEmbedder(Config.EMBEDDING_MODEL, Config.EMBEDDING_VECTOR_DIM)
24
- raise ValueError(f"Unsupported embedding provider: {Config.EMBEDDING_PROVIDER}")
23
+ return VoyageAIEmbedder(Config.EMBEDDINGS_MODEL, Config.EMBEDDINGS_VECTOR_DIM)
24
+
25
+ if Config.EMBEDDINGS_PROVIDER == "openai":
26
+ try:
27
+ from haiku.rag.embeddings.openai import Embedder as OpenAIEmbedder
28
+ except ImportError:
29
+ raise ImportError(
30
+ "OpenAI embedder requires the 'openai' package. "
31
+ "Please install haiku.rag with the 'openai' extra:"
32
+ "uv pip install haiku.rag --extra openai"
33
+ )
34
+ return OpenAIEmbedder(Config.EMBEDDINGS_MODEL, Config.EMBEDDINGS_VECTOR_DIM)
35
+
36
+ raise ValueError(f"Unsupported embedding provider: {Config.EMBEDDINGS_PROVIDER}")
@@ -5,7 +5,7 @@ from haiku.rag.embeddings.base import EmbedderBase
5
5
 
6
6
 
7
7
  class Embedder(EmbedderBase):
8
- _model: str = Config.EMBEDDING_MODEL
8
+ _model: str = Config.EMBEDDINGS_MODEL
9
9
  _vector_dim: int = 1024
10
10
 
11
11
  async def embed(self, text: str) -> list[float]:
@@ -0,0 +1,20 @@
1
+ try:
2
+ from openai import AsyncOpenAI
3
+
4
+ from haiku.rag.config import Config
5
+ from haiku.rag.embeddings.base import EmbedderBase
6
+
7
+ class Embedder(EmbedderBase):
8
+ _model: str = Config.EMBEDDINGS_MODEL
9
+ _vector_dim: int = 1536
10
+
11
+ async def embed(self, text: str) -> list[float]:
12
+ client = AsyncOpenAI()
13
+ response = await client.embeddings.create(
14
+ model=self._model,
15
+ input=text,
16
+ )
17
+ return response.data[0].embedding
18
+
19
+ except ImportError:
20
+ pass
@@ -5,7 +5,7 @@ try:
5
5
  from haiku.rag.embeddings.base import EmbedderBase
6
6
 
7
7
  class Embedder(EmbedderBase):
8
- _model: str = Config.EMBEDDING_MODEL
8
+ _model: str = Config.EMBEDDINGS_MODEL
9
9
  _vector_dim: int = 1024
10
10
 
11
11
  async def embed(self, text: str) -> list[float]:
haiku/rag/logging.py ADDED
@@ -0,0 +1,24 @@
1
+ import logging
2
+
3
+ from rich.console import Console
4
+ from rich.logging import RichHandler
5
+
6
+
7
+ def get_logger() -> logging.Logger:
8
+ logger = logging.getLogger("haiku.rag")
9
+
10
+ handler = RichHandler(
11
+ console=Console(stderr=True),
12
+ rich_tracebacks=True,
13
+ )
14
+ formatter = logging.Formatter("%(message)s")
15
+ handler.setFormatter(formatter)
16
+
17
+ logger.setLevel("INFO")
18
+
19
+ # Remove any existing handlers to avoid duplicates on reconfiguration
20
+ for hdlr in logger.handlers[:]:
21
+ logger.removeHandler(hdlr)
22
+
23
+ logger.addHandler(handler)
24
+ return logger
haiku/rag/monitor.py ADDED
@@ -0,0 +1,74 @@
1
+ from pathlib import Path
2
+
3
+ from watchfiles import Change, DefaultFilter, awatch
4
+
5
+ from haiku.rag.client import HaikuRAG
6
+ from haiku.rag.logging import get_logger
7
+ from haiku.rag.reader import FileReader
8
+ from haiku.rag.store.models.document import Document
9
+
10
+ logger = get_logger()
11
+
12
+
13
+ class FileFilter(DefaultFilter):
14
+ def __init__(self, *, ignore_paths: list[Path] | None = None) -> None:
15
+ self.extensions = tuple(FileReader.extensions)
16
+ super().__init__(ignore_paths=ignore_paths)
17
+
18
+ def __call__(self, change: "Change", path: str) -> bool:
19
+ return path.endswith(self.extensions) and super().__call__(change, path)
20
+
21
+
22
+ class FileWatcher:
23
+ def __init__(self, paths: list[Path], client: HaikuRAG):
24
+ self.paths = paths
25
+ self.client = client
26
+
27
+ async def observe(self):
28
+ logger.info(f"Watching files in {self.paths}")
29
+ filter = FileFilter()
30
+ await self.refresh()
31
+
32
+ async for changes in awatch(*self.paths, watch_filter=filter):
33
+ await self.handler(changes)
34
+
35
+ async def handler(self, changes: set[tuple[Change, str]]):
36
+ for change, path in changes:
37
+ if change == Change.added or change == Change.modified:
38
+ await self._upsert_document(Path(path))
39
+ elif change == Change.deleted:
40
+ await self._delete_document(Path(path))
41
+
42
+ async def refresh(self):
43
+ for path in self.paths:
44
+ for f in Path(path).rglob("**/*"):
45
+ if f.is_file() and f.suffix in FileReader.extensions:
46
+ await self._upsert_document(f)
47
+
48
+ async def _upsert_document(self, file: Path) -> Document | None:
49
+ try:
50
+ uri = file.as_uri()
51
+ existing_doc = await self.client.get_document_by_uri(uri)
52
+ print(uri)
53
+ if existing_doc:
54
+ doc = await self.client.create_document_from_source(str(file))
55
+ logger.info(f"Updated document {existing_doc.id} from {file}")
56
+ return doc
57
+ else:
58
+ doc = await self.client.create_document_from_source(str(file))
59
+ logger.info(f"Created new document {doc.id} from {file}")
60
+ return doc
61
+ except Exception as e:
62
+ logger.error(f"Failed to upsert document from {file}: {e}")
63
+ return None
64
+
65
+ async def _delete_document(self, file: Path):
66
+ try:
67
+ uri = file.as_uri()
68
+ existing_doc = await self.client.get_document_by_uri(uri)
69
+
70
+ if existing_doc and existing_doc.id:
71
+ await self.client.delete_document(existing_doc.id)
72
+ logger.info(f"Deleted document {existing_doc.id} for {file}")
73
+ except Exception as e:
74
+ logger.error(f"Failed to delete document for {file}: {e}")
@@ -1,10 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: haiku.rag
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Retrieval Augmented Generation (RAG) with SQLite
5
5
  Author-email: Yiorgis Gozadinos <ggozadinos@gmail.com>
6
6
  License: MIT
7
7
  License-File: LICENSE
8
+ Keywords: RAG,mcp,ml,sqlite,sqlite-vec
8
9
  Classifier: Development Status :: 4 - Beta
9
10
  Classifier: Environment :: Console
10
11
  Classifier: Intended Audience :: Developers
@@ -28,6 +29,8 @@ Requires-Dist: sqlite-vec>=0.1.6
28
29
  Requires-Dist: tiktoken>=0.9.0
29
30
  Requires-Dist: typer>=0.16.0
30
31
  Requires-Dist: watchfiles>=1.1.0
32
+ Provides-Extra: openai
33
+ Requires-Dist: openai>=1.0.0; extra == 'openai'
31
34
  Provides-Extra: voyageai
32
35
  Requires-Dist: voyageai>=0.3.2; extra == 'voyageai'
33
36
  Description-Content-Type: text/markdown
@@ -38,9 +41,12 @@ A SQLite-based Retrieval-Augmented Generation (RAG) system built for efficient d
38
41
 
39
42
  ## Features
40
43
  - **Local SQLite**: No need to run additional servers
41
- - **Support for various embedding providers**: You can use Ollama, VoyageAI or add your own
44
+ - **Support for various embedding providers**: You can use Ollama, VoyageAI, OpenAI or add your own
42
45
  - **Hybrid Search**: Vector search using `sqlite-vec` combined with full-text search `FTS5`, using Reciprocal Rank Fusion
43
46
  - **Multi-format Support**: Parse 40+ file formats including PDF, DOCX, HTML, Markdown, audio and more. Or add a url!
47
+ - **File monitoring** when run as a server automatically indexing your files
48
+ - **MCP server** Exposes functionality as MCP tools.
49
+ - **Python client** Call `haiku.rag` from your own python applications.
44
50
 
45
51
  ## Installation
46
52
 
@@ -52,24 +58,41 @@ By default Ollama (with the `mxbai-embed-large` model) is used for the embedding
52
58
  For other providers use:
53
59
 
54
60
  - **VoyageAI**: `uv pip install haiku.rag --extra voyageai`
61
+ - **OpenAI**: `uv pip install haiku.rag --extra openai`
55
62
 
56
63
  ## Configuration
57
64
 
65
+ You can set the directories to monitor using the `MONITOR_DIRECTORIES` environment variable (as comma separated values) :
66
+
67
+ ```bash
68
+ # Monitor single directory
69
+ export MONITOR_DIRECTORIES="/path/to/documents,/another_path/to/documents"
70
+ ```
71
+
58
72
  If you want to use an alternative embeddings provider (Ollama being the default) you will need to set the provider details through environment variables:
59
73
 
60
74
  By default:
61
75
 
62
76
  ```bash
63
- EMBEDDING_PROVIDER="ollama"
64
- EMBEDDING_MODEL="mxbai-embed-large" # or any other model
65
- EMBEDDING_VECTOR_DIM=1024
77
+ EMBEDDINGS_PROVIDER="ollama"
78
+ EMBEDDINGS_MODEL="mxbai-embed-large" # or any other model
79
+ EMBEDDINGS_VECTOR_DIM=1024
66
80
  ```
67
81
 
68
82
  For VoyageAI:
69
83
  ```bash
70
- EMBEDDING_PROVIDER="voyageai"
71
- EMBEDDING_MODEL="voyage-3.5" # or any other model
72
- EMBEDDING_VECTOR_DIM=1024
84
+ EMBEDDINGS_PROVIDER="voyageai"
85
+ EMBEDDINGS_MODEL="voyage-3.5" # or any other model
86
+ EMBEDDINGS_VECTOR_DIM=1024
87
+ VOYAGE_API_KEY="your-api-key"
88
+ ```
89
+
90
+ For OpenAI:
91
+ ```bash
92
+ EMBEDDINGS_PROVIDER="openai"
93
+ EMBEDDINGS_MODEL="text-embedding-3-small" # or text-embedding-3-large
94
+ EMBEDDINGS_VECTOR_DIM=1536
95
+ OPENAI_API_KEY="your-api-key"
73
96
  ```
74
97
 
75
98
  ## Command Line Interface
@@ -101,7 +124,7 @@ haiku-rag search "machine learning"
101
124
  # Search with custom options
102
125
  haiku-rag search "python programming" --limit 10 --k 100
103
126
 
104
- # Start MCP server (default HTTP transport)
127
+ # Start file monitoring & MCP server (default HTTP transport)
105
128
  haiku-rag serve # --stdio for stdio transport or --sse for SSE transport
106
129
  ```
107
130
 
@@ -111,7 +134,26 @@ haiku-rag command -h
111
134
  ```
112
135
  to see additional parameters for a command.
113
136
 
114
- ## MCP Server
137
+ ## File Monitoring & MCP server
138
+
139
+ You can start the server (using Streamble HTTP, stdio or SSE transports) with:
140
+
141
+ ```bash
142
+ # Start with default HTTP transport
143
+ haiku-rag serve # --stdio for stdio transport or --sse for SSE transport
144
+ ```
145
+
146
+ You need to have set the `MONITOR_DIRECTORIES` environment variable for monitoring to take place.
147
+
148
+ ### File monitoring
149
+
150
+ `haiku.rag` can watch directories for changes and automatically update the document store:
151
+
152
+ - **Startup**: Scan all monitored directories and add any new files
153
+ - **File Added/Modified**: Automatically parse and add/update the document in the database
154
+ - **File Deleted**: Remove the corresponding document from the database
155
+
156
+ ### MCP Server
115
157
 
116
158
  `haiku.rag` includes a Model Context Protocol (MCP) server that exposes RAG functionality as tools for AI assistants like Claude Desktop. The MCP server provides the following tools:
117
159
 
@@ -123,13 +165,6 @@ to see additional parameters for a command.
123
165
  - `list_documents` - List all documents with pagination
124
166
  - `delete_document` - Delete documents by ID
125
167
 
126
- You can start the server (using Streamble HTTP, stdio or SSE transports) with:
127
-
128
- ```bash
129
- # Start with default HTTP transport
130
- haiku-rag serve # --stdio for stdio transport or --sse for SSE transport
131
- ```
132
-
133
168
  ## Using `haiku.rag` from python
134
169
 
135
170
  ### Managing documents
@@ -1,16 +1,19 @@
1
1
  haiku/rag/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- haiku/rag/app.py,sha256=jJb5THgH3nbh2K8uiYsMVlkqVbSkIGEyxPMISM3epMA,4546
2
+ haiku/rag/app.py,sha256=FedUvIxPXCi7SmxUi9zJcxmoZBTQZJO00XIkoD-k87s,4915
3
3
  haiku/rag/chunker.py,sha256=lSSPWgNAe7gNZL_yNLmDtqxJix4YclOiG7gbARcEpV8,1871
4
- haiku/rag/cli.py,sha256=XOxl7H86La7fB4DvsEJxtNuSfZgOtwqQDmECSaxv4sY,4020
5
- haiku/rag/client.py,sha256=H5zE-HO8Asxo-_vEcnxFqvQixdiTFTqvNH8EkH7Xo4E,9713
6
- haiku/rag/config.py,sha256=GxpfUwsQmfzQcknIAPEET_Qu-0WFYtPkHrV3arvNdxM,596
4
+ haiku/rag/cli.py,sha256=SvDPYHHdjPu8bEF8PgE4agUo-5j3Kuq_rS9Cav6xch0,4051
5
+ haiku/rag/client.py,sha256=uWqcowc8J2Yv-liGBGSJnuQkFw4CDlf_ivOxp6E5C1M,9707
6
+ haiku/rag/config.py,sha256=b346EQ7HCFy-OU3K-SzSSoOLMuQseHFoiysYZMO1zCc,1003
7
+ haiku/rag/logging.py,sha256=zTTGpGq5tPdcd7RpCbd9EGw1IZlQDbYkrCg9t9pqRc4,580
7
8
  haiku/rag/mcp.py,sha256=tMN6fNX7ZtAER1R6DL1GkC9HZozTC4HzuQs199p7icI,4551
9
+ haiku/rag/monitor.py,sha256=aFJb5cnesEBIGyVzt8IXYrlTujiC1QSPczPuAam2yXw,2793
8
10
  haiku/rag/reader.py,sha256=S7-Z72pDvSHedvgt4-RkTOwZadG88Oed9keJ69SVITk,962
9
11
  haiku/rag/utils.py,sha256=6xVM6z2OmhzB4FEDlPbMsr_ZBBmCbMQb83nP6E2UdxY,629
10
- haiku/rag/embeddings/__init__.py,sha256=jOamqhoeFX9J-ThwvVyHGd2s8jqJzA8B6J4sxHGZ39o,1007
12
+ haiku/rag/embeddings/__init__.py,sha256=4jUPe2FyIf8BGZ7AncWSlBdNXG3URejBbnkhQf3JiD0,1505
11
13
  haiku/rag/embeddings/base.py,sha256=PTAWKTU-Q-hXIhbRK1o6pIdpaW7DFdzJXQ0Nzc6VI-w,379
12
- haiku/rag/embeddings/ollama.py,sha256=i_w7hbh-_ukysco274fLkQuFRgaFq0zIwIs8CNmRcLE,440
13
- haiku/rag/embeddings/voyageai.py,sha256=MPioqQ0duzjglqvnN_8ftVq11fvBrcpV03p9MMLwflM,533
14
+ haiku/rag/embeddings/ollama.py,sha256=hWdrTiuJwNSRYCqP0WP-z6XXA3RBGkAiknZMsPLH0qU,441
15
+ haiku/rag/embeddings/openai.py,sha256=reh8AykG2f9f5hhRDmqSsjiuCPi9SsXfe2YEZFlxXk8,550
16
+ haiku/rag/embeddings/voyageai.py,sha256=jc0JywdLJD3Ee1MUv1m8MhWCEo0enNnVcrIBtUvD-Ss,534
14
17
  haiku/rag/store/__init__.py,sha256=hq0W0DAC7ysqhWSP2M2uHX8cbG6kbr-sWHxhq6qQcY0,103
15
18
  haiku/rag/store/engine.py,sha256=BeYZRZ08zaYeeu375ysnAL3tGz4roA3GzP7WRNwznCo,2603
16
19
  haiku/rag/store/models/__init__.py,sha256=s0E72zneGlowvZrFWaNxHYjOAUjgWdLxzdYsnvNRVlY,88
@@ -20,8 +23,8 @@ haiku/rag/store/repositories/__init__.py,sha256=uIBhxjQh-4o3O-ck8b7BQ58qXQTuJdPv
20
23
  haiku/rag/store/repositories/base.py,sha256=cm3VyQXhtxvRfk1uJHpA0fDSxMpYN-mjQmRiDiLsQ68,1008
21
24
  haiku/rag/store/repositories/chunk.py,sha256=6zABVlb5zbMQ4s50z9qb53ieHYaiv4CjgxpbsXxs814,14639
22
25
  haiku/rag/store/repositories/document.py,sha256=xpWOpjHFbhVwNJ1gpusEKNY6l_Qyibg9y_bdHCwcfpk,7133
23
- haiku_rag-0.1.0.dist-info/METADATA,sha256=kDmX6IcmvyL8ss4Go30_UDaSBA4TTzpkp6unzcDOgnM,6141
24
- haiku_rag-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
25
- haiku_rag-0.1.0.dist-info/entry_points.txt,sha256=G1U3nAkNd5YDYd4v0tuYFbriz0i-JheCsFuT9kIoGCI,48
26
- haiku_rag-0.1.0.dist-info/licenses/LICENSE,sha256=eXZrWjSk9PwYFNK9yUczl3oPl95Z4V9UXH7bPN46iPo,1065
27
- haiku_rag-0.1.0.dist-info/RECORD,,
26
+ haiku_rag-0.2.0.dist-info/METADATA,sha256=o9PPoiXU7VIRAuQVwFvfQg4w-8ufz5aLo9PuG0ykWuI,7468
27
+ haiku_rag-0.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
28
+ haiku_rag-0.2.0.dist-info/entry_points.txt,sha256=G1U3nAkNd5YDYd4v0tuYFbriz0i-JheCsFuT9kIoGCI,48
29
+ haiku_rag-0.2.0.dist-info/licenses/LICENSE,sha256=eXZrWjSk9PwYFNK9yUczl3oPl95Z4V9UXH7bPN46iPo,1065
30
+ haiku_rag-0.2.0.dist-info/RECORD,,