haiku.rag 0.3.4__py3-none-any.whl → 0.4.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/chunker.py +10 -19
- haiku/rag/client.py +66 -14
- haiku/rag/config.py +7 -0
- haiku/rag/embeddings/base.py +5 -2
- haiku/rag/embeddings/ollama.py +0 -3
- haiku/rag/embeddings/openai.py +0 -4
- haiku/rag/embeddings/voyageai.py +0 -4
- haiku/rag/qa/prompts.py +2 -1
- haiku/rag/reranking/__init__.py +37 -0
- haiku/rag/reranking/base.py +13 -0
- haiku/rag/reranking/cohere.py +34 -0
- haiku/rag/reranking/mxbai.py +28 -0
- haiku/rag/utils.py +19 -20
- {haiku_rag-0.3.4.dist-info → haiku_rag-0.4.0.dist-info}/METADATA +6 -2
- {haiku_rag-0.3.4.dist-info → haiku_rag-0.4.0.dist-info}/RECORD +18 -14
- {haiku_rag-0.3.4.dist-info → haiku_rag-0.4.0.dist-info}/WHEEL +0 -0
- {haiku_rag-0.3.4.dist-info → haiku_rag-0.4.0.dist-info}/entry_points.txt +0 -0
- {haiku_rag-0.3.4.dist-info → haiku_rag-0.4.0.dist-info}/licenses/LICENSE +0 -0
haiku/rag/chunker.py
CHANGED
|
@@ -6,15 +6,11 @@ from haiku.rag.config import Config
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class Chunker:
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
chunk_size : int
|
|
15
|
-
The maximum size of a chunk in characters.
|
|
16
|
-
chunk_overlap : int
|
|
17
|
-
The number of characters of overlap between chunks.
|
|
9
|
+
"""A class that chunks text into smaller pieces for embedding and retrieval.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
chunk_size: The maximum size of a chunk in tokens.
|
|
13
|
+
chunk_overlap: The number of tokens of overlap between chunks.
|
|
18
14
|
"""
|
|
19
15
|
|
|
20
16
|
encoder: ClassVar[tiktoken.Encoding] = tiktoken.encoding_for_model("gpt-4o")
|
|
@@ -28,18 +24,13 @@ class Chunker:
|
|
|
28
24
|
self.chunk_overlap = chunk_overlap
|
|
29
25
|
|
|
30
26
|
async def chunk(self, text: str) -> list[str]:
|
|
31
|
-
"""
|
|
32
|
-
Split the text into chunks.
|
|
27
|
+
"""Split the text into chunks based on token boundaries.
|
|
33
28
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
text : str
|
|
37
|
-
The text to be split into chunks.
|
|
29
|
+
Args:
|
|
30
|
+
text: The text to be split into chunks.
|
|
38
31
|
|
|
39
|
-
Returns
|
|
40
|
-
|
|
41
|
-
list
|
|
42
|
-
A list of text chunks.
|
|
32
|
+
Returns:
|
|
33
|
+
A list of text chunks with token-based boundaries and overlap.
|
|
43
34
|
"""
|
|
44
35
|
if not text:
|
|
45
36
|
return []
|
haiku/rag/client.py
CHANGED
|
@@ -10,6 +10,7 @@ import httpx
|
|
|
10
10
|
|
|
11
11
|
from haiku.rag.config import Config
|
|
12
12
|
from haiku.rag.reader import FileReader
|
|
13
|
+
from haiku.rag.reranking import get_reranker
|
|
13
14
|
from haiku.rag.store.engine import Store
|
|
14
15
|
from haiku.rag.store.models.chunk import Chunk
|
|
15
16
|
from haiku.rag.store.models.document import Document
|
|
@@ -26,7 +27,12 @@ class HaikuRAG:
|
|
|
26
27
|
/ "haiku.rag.sqlite",
|
|
27
28
|
skip_validation: bool = False,
|
|
28
29
|
):
|
|
29
|
-
"""Initialize the RAG client with a database path.
|
|
30
|
+
"""Initialize the RAG client with a database path.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
db_path: Path to the SQLite database file or ":memory:" for in-memory database.
|
|
34
|
+
skip_validation: Whether to skip configuration validation on database load.
|
|
35
|
+
"""
|
|
30
36
|
if isinstance(db_path, Path):
|
|
31
37
|
if not db_path.parent.exists():
|
|
32
38
|
Path.mkdir(db_path.parent, parents=True)
|
|
@@ -46,7 +52,16 @@ class HaikuRAG:
|
|
|
46
52
|
async def create_document(
|
|
47
53
|
self, content: str, uri: str | None = None, metadata: dict | None = None
|
|
48
54
|
) -> Document:
|
|
49
|
-
"""Create a new document with optional URI and metadata.
|
|
55
|
+
"""Create a new document with optional URI and metadata.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
content: The text content of the document.
|
|
59
|
+
uri: Optional URI identifier for the document.
|
|
60
|
+
metadata: Optional metadata dictionary.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
The created Document instance.
|
|
64
|
+
"""
|
|
50
65
|
document = Document(
|
|
51
66
|
content=content,
|
|
52
67
|
uri=uri,
|
|
@@ -219,11 +234,25 @@ class HaikuRAG:
|
|
|
219
234
|
return ".html"
|
|
220
235
|
|
|
221
236
|
async def get_document_by_id(self, document_id: int) -> Document | None:
|
|
222
|
-
"""Get a document by its ID.
|
|
237
|
+
"""Get a document by its ID.
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
document_id: The unique identifier of the document.
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
The Document instance if found, None otherwise.
|
|
244
|
+
"""
|
|
223
245
|
return await self.document_repository.get_by_id(document_id)
|
|
224
246
|
|
|
225
247
|
async def get_document_by_uri(self, uri: str) -> Document | None:
|
|
226
|
-
"""Get a document by its URI.
|
|
248
|
+
"""Get a document by its URI.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
uri: The URI identifier of the document.
|
|
252
|
+
|
|
253
|
+
Returns:
|
|
254
|
+
The Document instance if found, None otherwise.
|
|
255
|
+
"""
|
|
227
256
|
return await self.document_repository.get_by_uri(uri)
|
|
228
257
|
|
|
229
258
|
async def update_document(self, document: Document) -> Document:
|
|
@@ -237,32 +266,55 @@ class HaikuRAG:
|
|
|
237
266
|
async def list_documents(
|
|
238
267
|
self, limit: int | None = None, offset: int | None = None
|
|
239
268
|
) -> list[Document]:
|
|
240
|
-
"""List all documents with optional pagination.
|
|
269
|
+
"""List all documents with optional pagination.
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
limit: Maximum number of documents to return.
|
|
273
|
+
offset: Number of documents to skip.
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
List of Document instances.
|
|
277
|
+
"""
|
|
241
278
|
return await self.document_repository.list_all(limit=limit, offset=offset)
|
|
242
279
|
|
|
243
280
|
async def search(
|
|
244
|
-
self, query: str, limit: int =
|
|
281
|
+
self, query: str, limit: int = 3, k: int = 60, rerank=Config.RERANK
|
|
245
282
|
) -> list[tuple[Chunk, float]]:
|
|
246
|
-
"""Search for relevant chunks using hybrid search (vector similarity + full-text search).
|
|
283
|
+
"""Search for relevant chunks using hybrid search (vector similarity + full-text search) with reranking.
|
|
247
284
|
|
|
248
285
|
Args:
|
|
249
|
-
query: The search query string
|
|
250
|
-
limit: Maximum number of results to return
|
|
251
|
-
k: Parameter for Reciprocal Rank Fusion (default: 60)
|
|
286
|
+
query: The search query string.
|
|
287
|
+
limit: Maximum number of results to return.
|
|
288
|
+
k: Parameter for Reciprocal Rank Fusion (default: 60).
|
|
252
289
|
|
|
253
290
|
Returns:
|
|
254
|
-
List of (chunk, score) tuples ordered by relevance
|
|
291
|
+
List of (chunk, score) tuples ordered by relevance.
|
|
255
292
|
"""
|
|
256
|
-
|
|
293
|
+
|
|
294
|
+
if not rerank:
|
|
295
|
+
return await self.chunk_repository.search_chunks_hybrid(query, limit, k)
|
|
296
|
+
|
|
297
|
+
# Get more initial results (3X) for reranking
|
|
298
|
+
search_results = await self.chunk_repository.search_chunks_hybrid(
|
|
299
|
+
query, limit * 3, k
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
# Apply reranking
|
|
303
|
+
reranker = get_reranker()
|
|
304
|
+
chunks = [chunk for chunk, _ in search_results]
|
|
305
|
+
reranked_results = await reranker.rerank(query, chunks, top_n=limit)
|
|
306
|
+
|
|
307
|
+
# Return reranked results with scores from reranker
|
|
308
|
+
return reranked_results
|
|
257
309
|
|
|
258
310
|
async def ask(self, question: str) -> str:
|
|
259
311
|
"""Ask a question using the configured QA agent.
|
|
260
312
|
|
|
261
313
|
Args:
|
|
262
|
-
question: The question to ask
|
|
314
|
+
question: The question to ask.
|
|
263
315
|
|
|
264
316
|
Returns:
|
|
265
|
-
The generated answer as a string
|
|
317
|
+
The generated answer as a string.
|
|
266
318
|
"""
|
|
267
319
|
from haiku.rag.qa import get_qa_agent
|
|
268
320
|
|
haiku/rag/config.py
CHANGED
|
@@ -19,6 +19,10 @@ class AppConfig(BaseModel):
|
|
|
19
19
|
EMBEDDINGS_MODEL: str = "mxbai-embed-large"
|
|
20
20
|
EMBEDDINGS_VECTOR_DIM: int = 1024
|
|
21
21
|
|
|
22
|
+
RERANK: bool = True
|
|
23
|
+
RERANK_PROVIDER: str = "mxbai"
|
|
24
|
+
RERANK_MODEL: str = "mixedbread-ai/mxbai-rerank-base-v2"
|
|
25
|
+
|
|
22
26
|
QA_PROVIDER: str = "ollama"
|
|
23
27
|
QA_MODEL: str = "qwen3"
|
|
24
28
|
|
|
@@ -31,6 +35,7 @@ class AppConfig(BaseModel):
|
|
|
31
35
|
VOYAGE_API_KEY: str = ""
|
|
32
36
|
OPENAI_API_KEY: str = ""
|
|
33
37
|
ANTHROPIC_API_KEY: str = ""
|
|
38
|
+
COHERE_API_KEY: str = ""
|
|
34
39
|
|
|
35
40
|
@field_validator("MONITOR_DIRECTORIES", mode="before")
|
|
36
41
|
@classmethod
|
|
@@ -52,3 +57,5 @@ if Config.VOYAGE_API_KEY:
|
|
|
52
57
|
os.environ["VOYAGE_API_KEY"] = Config.VOYAGE_API_KEY
|
|
53
58
|
if Config.ANTHROPIC_API_KEY:
|
|
54
59
|
os.environ["ANTHROPIC_API_KEY"] = Config.ANTHROPIC_API_KEY
|
|
60
|
+
if Config.COHERE_API_KEY:
|
|
61
|
+
os.environ["CO_API_KEY"] = Config.COHERE_API_KEY
|
haiku/rag/embeddings/base.py
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
from haiku.rag.config import Config
|
|
2
|
+
|
|
3
|
+
|
|
1
4
|
class EmbedderBase:
|
|
2
|
-
_model: str =
|
|
3
|
-
_vector_dim: int =
|
|
5
|
+
_model: str = Config.EMBEDDINGS_MODEL
|
|
6
|
+
_vector_dim: int = Config.EMBEDDINGS_VECTOR_DIM
|
|
4
7
|
|
|
5
8
|
def __init__(self, model: str, vector_dim: int):
|
|
6
9
|
self._model = model
|
haiku/rag/embeddings/ollama.py
CHANGED
|
@@ -5,9 +5,6 @@ from haiku.rag.embeddings.base import EmbedderBase
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class Embedder(EmbedderBase):
|
|
8
|
-
_model: str = Config.EMBEDDINGS_MODEL
|
|
9
|
-
_vector_dim: int = 1024
|
|
10
|
-
|
|
11
8
|
async def embed(self, text: str) -> list[float]:
|
|
12
9
|
client = AsyncClient(host=Config.OLLAMA_BASE_URL)
|
|
13
10
|
res = await client.embeddings(model=self._model, prompt=text)
|
haiku/rag/embeddings/openai.py
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
try:
|
|
2
2
|
from openai import AsyncOpenAI
|
|
3
3
|
|
|
4
|
-
from haiku.rag.config import Config
|
|
5
4
|
from haiku.rag.embeddings.base import EmbedderBase
|
|
6
5
|
|
|
7
6
|
class Embedder(EmbedderBase):
|
|
8
|
-
_model: str = Config.EMBEDDINGS_MODEL
|
|
9
|
-
_vector_dim: int = 1536
|
|
10
|
-
|
|
11
7
|
async def embed(self, text: str) -> list[float]:
|
|
12
8
|
client = AsyncOpenAI()
|
|
13
9
|
response = await client.embeddings.create(
|
haiku/rag/embeddings/voyageai.py
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
try:
|
|
2
2
|
from voyageai.client import Client # type: ignore
|
|
3
3
|
|
|
4
|
-
from haiku.rag.config import Config
|
|
5
4
|
from haiku.rag.embeddings.base import EmbedderBase
|
|
6
5
|
|
|
7
6
|
class Embedder(EmbedderBase):
|
|
8
|
-
_model: str = Config.EMBEDDINGS_MODEL
|
|
9
|
-
_vector_dim: int = 1024
|
|
10
|
-
|
|
11
7
|
async def embed(self, text: str) -> list[float]:
|
|
12
8
|
client = Client()
|
|
13
9
|
res = client.embed([text], model=self._model, output_dtype="float")
|
haiku/rag/qa/prompts.py
CHANGED
|
@@ -6,7 +6,7 @@ Your process:
|
|
|
6
6
|
2. Search with specific keywords and phrases from the user's question
|
|
7
7
|
3. Review the search results and their relevance scores
|
|
8
8
|
4. If you need additional context, perform follow-up searches with different keywords
|
|
9
|
-
5. Provide a comprehensive answer based only on the retrieved documents
|
|
9
|
+
5. Provide a short and to the point comprehensive answer based only on the retrieved documents
|
|
10
10
|
|
|
11
11
|
Guidelines:
|
|
12
12
|
- Base your answers strictly on the provided document content
|
|
@@ -15,6 +15,7 @@ Guidelines:
|
|
|
15
15
|
- Indicate when information is incomplete or when you need to search for additional context
|
|
16
16
|
- If the retrieved documents don't contain sufficient information, clearly state: "I cannot find enough information in the knowledge base to answer this question."
|
|
17
17
|
- For complex questions, consider breaking them down and performing multiple searches
|
|
18
|
+
- Stick to the answer, do not ellaborate or provde context unless asked for it.
|
|
18
19
|
|
|
19
20
|
Be concise, and always maintain accuracy over completeness. Prefer short, direct answers that are well-supported by the documents.
|
|
20
21
|
"""
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from haiku.rag.config import Config
|
|
2
|
+
from haiku.rag.reranking.base import RerankerBase
|
|
3
|
+
|
|
4
|
+
try:
|
|
5
|
+
from haiku.rag.reranking.cohere import CohereReranker
|
|
6
|
+
except ImportError:
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
_reranker: RerankerBase | None = None
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_reranker() -> RerankerBase:
|
|
13
|
+
"""
|
|
14
|
+
Factory function to get the appropriate reranker based on the configuration.
|
|
15
|
+
"""
|
|
16
|
+
global _reranker
|
|
17
|
+
if _reranker is not None:
|
|
18
|
+
return _reranker
|
|
19
|
+
if Config.RERANK_PROVIDER == "mxbai":
|
|
20
|
+
from haiku.rag.reranking.mxbai import MxBAIReranker
|
|
21
|
+
|
|
22
|
+
_reranker = MxBAIReranker()
|
|
23
|
+
return _reranker
|
|
24
|
+
|
|
25
|
+
if Config.RERANK_PROVIDER == "cohere":
|
|
26
|
+
try:
|
|
27
|
+
from haiku.rag.reranking.cohere import CohereReranker
|
|
28
|
+
except ImportError:
|
|
29
|
+
raise ImportError(
|
|
30
|
+
"Cohere reranker requires the 'cohere' package. "
|
|
31
|
+
"Please install haiku.rag with the 'cohere' extra:"
|
|
32
|
+
"uv pip install haiku.rag --extra cohere"
|
|
33
|
+
)
|
|
34
|
+
_reranker = CohereReranker()
|
|
35
|
+
return _reranker
|
|
36
|
+
|
|
37
|
+
raise ValueError(f"Unsupported reranker provider: {Config.RERANK_PROVIDER}")
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from haiku.rag.config import Config
|
|
2
|
+
from haiku.rag.store.models.chunk import Chunk
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class RerankerBase:
|
|
6
|
+
_model: str = Config.RERANK_MODEL
|
|
7
|
+
|
|
8
|
+
async def rerank(
|
|
9
|
+
self, query: str, chunks: list[Chunk], top_n: int = 10
|
|
10
|
+
) -> list[tuple[Chunk, float]]:
|
|
11
|
+
raise NotImplementedError(
|
|
12
|
+
"Reranker is an abstract class. Please implement the rerank method in a subclass."
|
|
13
|
+
)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from haiku.rag.config import Config
|
|
2
|
+
from haiku.rag.reranking.base import RerankerBase
|
|
3
|
+
from haiku.rag.store.models.chunk import Chunk
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
import cohere
|
|
7
|
+
except ImportError as e:
|
|
8
|
+
raise ImportError(
|
|
9
|
+
"cohere is not installed. Please install it with `pip install cohere` or use the cohere optional dependency."
|
|
10
|
+
) from e
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CohereReranker(RerankerBase):
|
|
14
|
+
def __init__(self):
|
|
15
|
+
self._client = cohere.ClientV2(api_key=Config.COHERE_API_KEY)
|
|
16
|
+
|
|
17
|
+
async def rerank(
|
|
18
|
+
self, query: str, chunks: list[Chunk], top_n: int = 10
|
|
19
|
+
) -> list[tuple[Chunk, float]]:
|
|
20
|
+
if not chunks:
|
|
21
|
+
return []
|
|
22
|
+
|
|
23
|
+
documents = [chunk.content for chunk in chunks]
|
|
24
|
+
|
|
25
|
+
response = self._client.rerank(
|
|
26
|
+
model=self._model, query=query, documents=documents, top_n=top_n
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
reranked_chunks = []
|
|
30
|
+
for result in response.results:
|
|
31
|
+
original_chunk = chunks[result.index]
|
|
32
|
+
reranked_chunks.append((original_chunk, result.relevance_score))
|
|
33
|
+
|
|
34
|
+
return reranked_chunks
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from mxbai_rerank import MxbaiRerankV2
|
|
2
|
+
|
|
3
|
+
from haiku.rag.config import Config
|
|
4
|
+
from haiku.rag.reranking.base import RerankerBase
|
|
5
|
+
from haiku.rag.store.models.chunk import Chunk
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class MxBAIReranker(RerankerBase):
|
|
9
|
+
def __init__(self):
|
|
10
|
+
self._client = MxbaiRerankV2(
|
|
11
|
+
Config.RERANK_MODEL, disable_transformers_warnings=True
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
async def rerank(
|
|
15
|
+
self, query: str, chunks: list[Chunk], top_n: int = 10
|
|
16
|
+
) -> list[tuple[Chunk, float]]:
|
|
17
|
+
if not chunks:
|
|
18
|
+
return []
|
|
19
|
+
|
|
20
|
+
documents = [chunk.content for chunk in chunks]
|
|
21
|
+
|
|
22
|
+
results = self._client.rank(query=query, documents=documents, top_k=top_n)
|
|
23
|
+
reranked_chunks = []
|
|
24
|
+
for result in results:
|
|
25
|
+
original_chunk = chunks[result.index]
|
|
26
|
+
reranked_chunks.append((original_chunk, result.score))
|
|
27
|
+
|
|
28
|
+
return reranked_chunks
|
haiku/rag/utils.py
CHANGED
|
@@ -7,15 +7,14 @@ from packaging.version import Version, parse
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def get_default_data_dir() -> Path:
|
|
10
|
-
"""
|
|
11
|
-
Get the user data directory for the current system platform.
|
|
10
|
+
"""Get the user data directory for the current system platform.
|
|
12
11
|
|
|
13
12
|
Linux: ~/.local/share/haiku.rag
|
|
14
13
|
macOS: ~/Library/Application Support/haiku.rag
|
|
15
14
|
Windows: C:/Users/<USER>/AppData/Roaming/haiku.rag
|
|
16
15
|
|
|
17
|
-
:
|
|
18
|
-
|
|
16
|
+
Returns:
|
|
17
|
+
User Data Path.
|
|
19
18
|
"""
|
|
20
19
|
home = Path.home()
|
|
21
20
|
|
|
@@ -30,13 +29,13 @@ def get_default_data_dir() -> Path:
|
|
|
30
29
|
|
|
31
30
|
|
|
32
31
|
def semantic_version_to_int(version: str) -> int:
|
|
33
|
-
"""
|
|
34
|
-
|
|
32
|
+
"""Convert a semantic version string to an integer.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
version: Semantic version string.
|
|
35
36
|
|
|
36
|
-
:
|
|
37
|
-
|
|
38
|
-
:return: Integer representation of semantic version
|
|
39
|
-
:rtype: int
|
|
37
|
+
Returns:
|
|
38
|
+
Integer representation of semantic version.
|
|
40
39
|
"""
|
|
41
40
|
major, minor, patch = version.split(".")
|
|
42
41
|
major = int(major) << 16
|
|
@@ -46,13 +45,13 @@ def semantic_version_to_int(version: str) -> int:
|
|
|
46
45
|
|
|
47
46
|
|
|
48
47
|
def int_to_semantic_version(version: int) -> str:
|
|
49
|
-
"""
|
|
50
|
-
|
|
48
|
+
"""Convert an integer to a semantic version string.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
version: Integer representation of semantic version.
|
|
51
52
|
|
|
52
|
-
:
|
|
53
|
-
|
|
54
|
-
:return: Semantic version string
|
|
55
|
-
:rtype: str
|
|
53
|
+
Returns:
|
|
54
|
+
Semantic version string.
|
|
56
55
|
"""
|
|
57
56
|
major = version >> 16
|
|
58
57
|
minor = (version >> 8) & 255
|
|
@@ -61,11 +60,11 @@ def int_to_semantic_version(version: int) -> str:
|
|
|
61
60
|
|
|
62
61
|
|
|
63
62
|
async def is_up_to_date() -> tuple[bool, Version, Version]:
|
|
64
|
-
"""
|
|
65
|
-
Checks whether haiku.rag is current.
|
|
63
|
+
"""Check whether haiku.rag is current.
|
|
66
64
|
|
|
67
|
-
:
|
|
68
|
-
|
|
65
|
+
Returns:
|
|
66
|
+
A tuple containing a boolean indicating whether haiku.rag is current,
|
|
67
|
+
the running version and the latest version.
|
|
69
68
|
"""
|
|
70
69
|
|
|
71
70
|
async with httpx.AsyncClient() as client:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: haiku.rag
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Retrieval Augmented Generation (RAG) with SQLite
|
|
5
5
|
Author-email: Yiorgis Gozadinos <ggozadinos@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -21,6 +21,7 @@ Requires-Python: >=3.10
|
|
|
21
21
|
Requires-Dist: fastmcp>=2.8.1
|
|
22
22
|
Requires-Dist: httpx>=0.28.1
|
|
23
23
|
Requires-Dist: markitdown[audio-transcription,docx,pdf,pptx,xlsx]>=0.1.2
|
|
24
|
+
Requires-Dist: mxbai-rerank>=0.1.6
|
|
24
25
|
Requires-Dist: ollama>=0.5.1
|
|
25
26
|
Requires-Dist: pydantic>=2.11.7
|
|
26
27
|
Requires-Dist: python-dotenv>=1.1.0
|
|
@@ -31,6 +32,8 @@ Requires-Dist: typer>=0.16.0
|
|
|
31
32
|
Requires-Dist: watchfiles>=1.1.0
|
|
32
33
|
Provides-Extra: anthropic
|
|
33
34
|
Requires-Dist: anthropic>=0.56.0; extra == 'anthropic'
|
|
35
|
+
Provides-Extra: cohere
|
|
36
|
+
Requires-Dist: cohere>=5.16.1; extra == 'cohere'
|
|
34
37
|
Provides-Extra: openai
|
|
35
38
|
Requires-Dist: openai>=1.0.0; extra == 'openai'
|
|
36
39
|
Provides-Extra: voyageai
|
|
@@ -49,6 +52,7 @@ Retrieval-Augmented Generation (RAG) library on SQLite.
|
|
|
49
52
|
- **Multiple embedding providers**: Ollama, VoyageAI, OpenAI
|
|
50
53
|
- **Multiple QA providers**: Ollama, OpenAI, Anthropic
|
|
51
54
|
- **Hybrid search**: Vector + full-text search with Reciprocal Rank Fusion
|
|
55
|
+
- **Reranking**: Default search result reranking with MixedBread AI or Cohere
|
|
52
56
|
- **Question answering**: Built-in QA agents on your documents
|
|
53
57
|
- **File monitoring**: Auto-index files when run as server
|
|
54
58
|
- **40+ file formats**: PDF, DOCX, HTML, Markdown, audio, URLs
|
|
@@ -88,7 +92,7 @@ async with HaikuRAG("database.db") as client:
|
|
|
88
92
|
# Add document
|
|
89
93
|
doc = await client.create_document("Your content")
|
|
90
94
|
|
|
91
|
-
# Search
|
|
95
|
+
# Search (reranking enabled by default)
|
|
92
96
|
results = await client.search("query")
|
|
93
97
|
for chunk, score in results:
|
|
94
98
|
print(f"{score:.3f}: {chunk.content}")
|
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
haiku/rag/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
haiku/rag/app.py,sha256=FpLVyP1-zAq_XPmU8CPVLkuIAeuhBOGvMqhYS8RbN40,7649
|
|
3
|
-
haiku/rag/chunker.py,sha256=
|
|
3
|
+
haiku/rag/chunker.py,sha256=MbCtP66OfTFoIBvqmVT9T9c87fozsYYzAQzJJEfPBVI,1812
|
|
4
4
|
haiku/rag/cli.py,sha256=8PC7r5odIVLyksSm_BXor2rznIZ2KDug-YhzqbFPvms,5605
|
|
5
|
-
haiku/rag/client.py,sha256=
|
|
6
|
-
haiku/rag/config.py,sha256=
|
|
5
|
+
haiku/rag/client.py,sha256=W8iw22v9Muoq37e3uGww2DpbQnQhQzaPZiP9MVMRKJE,12554
|
|
6
|
+
haiku/rag/config.py,sha256=_Ss54kmfxVAJupExLKaYjYUlFxJgb7hEEdbG4-isapY,1662
|
|
7
7
|
haiku/rag/logging.py,sha256=zTTGpGq5tPdcd7RpCbd9EGw1IZlQDbYkrCg9t9pqRc4,580
|
|
8
8
|
haiku/rag/mcp.py,sha256=tMN6fNX7ZtAER1R6DL1GkC9HZozTC4HzuQs199p7icI,4551
|
|
9
9
|
haiku/rag/monitor.py,sha256=r386nkhdlsU8UECwIuVwnrSlgMk3vNIuUZGNIzkZuec,2770
|
|
10
10
|
haiku/rag/reader.py,sha256=S7-Z72pDvSHedvgt4-RkTOwZadG88Oed9keJ69SVITk,962
|
|
11
|
-
haiku/rag/utils.py,sha256=
|
|
11
|
+
haiku/rag/utils.py,sha256=Ez_tvNlRO_D8c2CBZ83Hs9Gmzcqdq4cmw_V5GBdKy_8,2214
|
|
12
12
|
haiku/rag/embeddings/__init__.py,sha256=4jUPe2FyIf8BGZ7AncWSlBdNXG3URejBbnkhQf3JiD0,1505
|
|
13
|
-
haiku/rag/embeddings/base.py,sha256=
|
|
14
|
-
haiku/rag/embeddings/ollama.py,sha256=
|
|
15
|
-
haiku/rag/embeddings/openai.py,sha256=
|
|
16
|
-
haiku/rag/embeddings/voyageai.py,sha256=
|
|
13
|
+
haiku/rag/embeddings/base.py,sha256=NTQvuzbZPu0LBo5wAu3qGyJ4xXUaRAt1fjBO0ygWn_Y,465
|
|
14
|
+
haiku/rag/embeddings/ollama.py,sha256=y6-lp0XpbnyIjoOEdtSzMdEVkU5glOwnWQ1FkpUZnpI,370
|
|
15
|
+
haiku/rag/embeddings/openai.py,sha256=i4Ui5hAJkcKqJkH9L3jJo7fuGYHn07td532w-ksg_T8,431
|
|
16
|
+
haiku/rag/embeddings/voyageai.py,sha256=0hiRTIqu-bpl-4OaCtMHvWfPdgbrzhnfZJowSV8pLRA,415
|
|
17
17
|
haiku/rag/qa/__init__.py,sha256=oso98Ypti7mBLTJ6Zk71YaSJ9Rgc89QXp9RSB6zSpYs,1501
|
|
18
18
|
haiku/rag/qa/anthropic.py,sha256=6I6cf6ySNkYbmDFdy22sA8r3GO5moiiH75tJnHcgJQA,4448
|
|
19
19
|
haiku/rag/qa/base.py,sha256=4ZTM_l5FAZ9cA0f8NeqRJiUAmjatwCTmSoclFw0gTFQ,1349
|
|
20
20
|
haiku/rag/qa/ollama.py,sha256=-UtNFErYlA_66g3WLU6lK38a1Y5zhAL6s_uZ5AP0TFs,2381
|
|
21
21
|
haiku/rag/qa/openai.py,sha256=dF32sGgVt8mZi5oVxByaeECs9NqLjvDiZnnpJBsrHm8,3968
|
|
22
|
-
haiku/rag/qa/prompts.py,sha256=
|
|
22
|
+
haiku/rag/qa/prompts.py,sha256=W6QYvqIGcW_VmnTA88quJqCi6h6bafEP4pyrcGze9TA,1303
|
|
23
|
+
haiku/rag/reranking/__init__.py,sha256=6tuQCrk5CEfyGaUjB7uaskWxsB15GaV8t9rm7VVSMrg,1125
|
|
24
|
+
haiku/rag/reranking/base.py,sha256=LM9yUSSJ414UgBZhFTgxGprlRqzfTe4I1vgjricz2JY,405
|
|
25
|
+
haiku/rag/reranking/cohere.py,sha256=1iTdiaa8vvb6oHVB2qpWzUOVkyfUcimVSZp6Qr4aq4c,1049
|
|
26
|
+
haiku/rag/reranking/mxbai.py,sha256=46sVTsTIkzIX9THgM3u8HaEmgY7evvEyB-N54JTHvK8,867
|
|
23
27
|
haiku/rag/store/__init__.py,sha256=hq0W0DAC7ysqhWSP2M2uHX8cbG6kbr-sWHxhq6qQcY0,103
|
|
24
28
|
haiku/rag/store/engine.py,sha256=4ouAD0s-TFwEoEHjVVw_KnV6aaw5nwhe9fdT8PRXfok,6061
|
|
25
29
|
haiku/rag/store/models/__init__.py,sha256=s0E72zneGlowvZrFWaNxHYjOAUjgWdLxzdYsnvNRVlY,88
|
|
@@ -32,8 +36,8 @@ haiku/rag/store/repositories/document.py,sha256=xpWOpjHFbhVwNJ1gpusEKNY6l_Qyibg9
|
|
|
32
36
|
haiku/rag/store/repositories/settings.py,sha256=dme3_ulQdQvyF9daavSjAd-SjZ5hh0MJoxP7iXgap-A,2492
|
|
33
37
|
haiku/rag/store/upgrades/__init__.py,sha256=kKS1YWT_P-CYKhKtokOLTIFNKf9jlfjFFr8lyIMeogM,100
|
|
34
38
|
haiku/rag/store/upgrades/v0_3_4.py,sha256=GLogKZdZ40NX1vBHKdOJju7fFzNUCHoEnjSZg17Hm2U,663
|
|
35
|
-
haiku_rag-0.
|
|
36
|
-
haiku_rag-0.
|
|
37
|
-
haiku_rag-0.
|
|
38
|
-
haiku_rag-0.
|
|
39
|
-
haiku_rag-0.
|
|
39
|
+
haiku_rag-0.4.0.dist-info/METADATA,sha256=59rxYHim3hZeKPkQhEmnNR_Tj6DuF9hLBj2GA2T3T_s,4235
|
|
40
|
+
haiku_rag-0.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
41
|
+
haiku_rag-0.4.0.dist-info/entry_points.txt,sha256=G1U3nAkNd5YDYd4v0tuYFbriz0i-JheCsFuT9kIoGCI,48
|
|
42
|
+
haiku_rag-0.4.0.dist-info/licenses/LICENSE,sha256=eXZrWjSk9PwYFNK9yUczl3oPl95Z4V9UXH7bPN46iPo,1065
|
|
43
|
+
haiku_rag-0.4.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|