haiku.rag 0.12.0__py3-none-any.whl → 0.13.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.

@@ -1,32 +1,33 @@
1
1
  haiku/rag/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- haiku/rag/app.py,sha256=lOoWMqQLNokMXcGh_6tYAeq_qaIV1dPOqDlQuetHLl0,21217
3
- haiku/rag/chunker.py,sha256=PVe6ysv8UlacUd4Zb3_8RFWIaWDXnzBAy2VDJ4TaUsE,1555
4
- haiku/rag/cli.py,sha256=7pf8lcLlu2S7KW-M8JqOPG7Xxy1aVbv6e8CPZbCL21w,13169
5
- haiku/rag/client.py,sha256=tk3BWa9u2mtUEMEOEcgF2ebUqdZRv4jSnu4S6Zho-Xg,24210
6
- haiku/rag/config.py,sha256=FBsMMijl5PxIfPGifk_AJVRjL4omb03jfoZm0P_VqxI,2743
2
+ haiku/rag/app.py,sha256=lNPYYWQLOPlOLYiwIR4yQuwzl-LrRNQn7n2nacgdq_k,21594
3
+ haiku/rag/chunker.py,sha256=pA0S0fFKAuvzGm2dGyp7FAkeFZA0YTCm_ata83Pnflw,1566
4
+ haiku/rag/cli.py,sha256=Y42tnlVFGvCZVjBcLWrIVgM0A7KjNYX9MAuk9-zQvvE,14523
5
+ haiku/rag/client.py,sha256=cG6DAhzJJ4vdo8QFn9p8iA6YTa0arMrTtIswoZc7sY0,26816
7
6
  haiku/rag/logging.py,sha256=dm65AwADpcQsH5OAPtRA-4hsw0w5DK-sGOvzYkj6jzw,1720
8
- haiku/rag/mcp.py,sha256=DZk-IJgVjAesu-vvqVd5BYnfDWKWNR6TQugKgdoFrvg,8976
9
- haiku/rag/migration.py,sha256=XldX0CTHPXNGrkdQ-gocr4kQGBsz-316WcE0ZDRfb48,11076
10
- haiku/rag/monitor.py,sha256=VP3bqY0mEodOP60eN4RMldgrL1ti5gMjuDuQ-_vBvFc,2759
7
+ haiku/rag/mcp.py,sha256=txuEnrUMWvs_shQBk15gEkJD7xNdSYzp3z75UUWaHFM,9328
8
+ haiku/rag/monitor.py,sha256=d92oRufhI8oYXH7oF6oYVf1_AcpFUafjM6tl4VhAupI,3322
11
9
  haiku/rag/reader.py,sha256=aW8LG0X31kVWS7kU2tKVpe8RqP3Ne_oIidd_X3UDLH0,3307
12
- haiku/rag/utils.py,sha256=dBzhKaOHI9KRiJqHErcXUnqtnXY2AgOK8PCLA3rhO0A,6115
13
- haiku/rag/a2a/__init__.py,sha256=4SlJBr9GUVZ0879o5VI6-qpcBKpieP2hW4hmNbm8NGg,5933
14
- haiku/rag/a2a/client.py,sha256=HIHF6HkIWU9UDmsl-hjJwMBRyNdHnSm0Z4K2d688CuQ,8644
10
+ haiku/rag/utils.py,sha256=47ehVYJlLz6Of_Ua89qj94JclO5ZPBFU9eyonifvnVg,6131
11
+ haiku/rag/a2a/__init__.py,sha256=tY_jLSUM0zKzyBctMkjpqmDWpxWc9QVEK1qAsb-plGs,5933
12
+ haiku/rag/a2a/client.py,sha256=awuiHXgVHn1uzaEXE98RIqqKHj1JjszOvn9WI3Jtth8,8760
15
13
  haiku/rag/a2a/context.py,sha256=SofkFUZcGonoJcgZh-RGqHTh0UWT4J7Zl4Mz6WDkMl4,2053
16
14
  haiku/rag/a2a/models.py,sha256=XhGYj2g3rgVM4JoCDXlll0YjaysqdalybJrBqFXSwl4,689
17
15
  haiku/rag/a2a/prompts.py,sha256=yCla8x0hbOhKrkuaqVrF1upn-YjQM3-2NsE2TSnet0M,3030
18
16
  haiku/rag/a2a/skills.py,sha256=dwyD2Bn493eL3Vf4uQzmyxj_9IUSb66kQ-085FBAuCs,2701
19
17
  haiku/rag/a2a/storage.py,sha256=c8vmGCiZ3nuV9wUuTnwpoRD2HVVvK2JPySQOc5PVMvg,2759
20
18
  haiku/rag/a2a/worker.py,sha256=S9hiA1ncpJPdtN0eEmMjsvr5LQ4wMVN5R8CjYkTeohU,12367
21
- haiku/rag/embeddings/__init__.py,sha256=44IfDITGIFTflGT6UEmiYOwpWFVbYv5smLY59D0YeCs,1419
22
- haiku/rag/embeddings/base.py,sha256=BnSviKrlzjv3L0sZJs_T-pxfawd-bcTak-rsX-D2f3A,497
23
- haiku/rag/embeddings/ollama.py,sha256=c1BeKTgpymniZw1sm4iAIdK5vA0MYoRzHLcd2_pFA44,638
24
- haiku/rag/embeddings/openai.py,sha256=bwoUVlzu9UtbDpN7CtG6OPt0d5tfJNeje4lR81Btpl0,546
25
- haiku/rag/embeddings/vllm.py,sha256=7ocp9D9bD1R5rqRIC4-Vih9VlKQNuD429k8-9wu234E,669
26
- haiku/rag/embeddings/voyageai.py,sha256=I4kVdT2KPtwcbjxD22GWJmgcIQIEEHpkOY2_QbFh7mQ,712
19
+ haiku/rag/config/__init__.py,sha256=19sgLKkTB6tm26dY4V6s4QeIK8dWiniNe_U3sBt9EJk,1110
20
+ haiku/rag/config/loader.py,sha256=6Y1Wfu1-AFnpJDmOJIOUVHJsHbyWTGFXo5Xd8TQ1AR8,5360
21
+ haiku/rag/config/models.py,sha256=mNRFzvBC8OrLX0erHeZj2b8IpXtUhLD7oMPIdrPg3j4,2053
22
+ haiku/rag/embeddings/__init__.py,sha256=lA-IxsgLBhLQhcuictVOuaerKOMZsniVBTREG6CNDWM,1611
23
+ haiku/rag/embeddings/base.py,sha256=LRiP0e6YfVFMETVXEQnCi_7LsUteN-zaNlXnq9vU-GA,682
24
+ haiku/rag/embeddings/ollama.py,sha256=O8g8GsSs2-By0wAvmyGRZmlHnQmMFJx6uZQslPj58Ag,855
25
+ haiku/rag/embeddings/openai.py,sha256=BfmPni567DH8KqwLCPiOmr3q-dpzpOJkvFFoUuTR5as,731
26
+ haiku/rag/embeddings/vllm.py,sha256=_zKvyqGIuwRv6TCyuOQn4YCvHGa_FQ8siNUhmLE4gTs,864
27
+ haiku/rag/embeddings/voyageai.py,sha256=6vEuk6q510AJv-K2lL93P2dVrziAjELTOe_w_Zp5YT4,917
27
28
  haiku/rag/graph/__init__.py,sha256=BHfMchuUO_UhHKpjjGHjd6xPxNkrIwJzHn4YJiLqG1g,62
28
29
  haiku/rag/graph/base.py,sha256=DepZqLF9E64YCCkjmbqmgyp28oNp69WfJCXp614xzh0,819
29
- haiku/rag/graph/common.py,sha256=xTejucXei3x9tqbal3ZS_64lZAC6Bw3-QfXPniZcZEw,986
30
+ haiku/rag/graph/common.py,sha256=-Pdao6ZiTgv4ppNctrRpwLG0U6za-66aScQWZ0uCUjc,1016
30
31
  haiku/rag/graph/models.py,sha256=sgL5_wSbQJrNOITH615jryPBhTE8J3ZiZWVxO9Ty-JI,755
31
32
  haiku/rag/graph/prompts.py,sha256=xJgAHjUVczxCgk7YLPyy6DdQFi0lwj42vJqIFnPqcYw,2221
32
33
  haiku/rag/graph/nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -34,8 +35,8 @@ haiku/rag/graph/nodes/analysis.py,sha256=g-Aw3nPuCHWo0CXM96Ixa4vQI4TpI6tg6ooHT_J
34
35
  haiku/rag/graph/nodes/plan.py,sha256=Bb6Fva9vwArCU-5xBr24N4pM3wfLP-Vwufgss8HfXMQ,2622
35
36
  haiku/rag/graph/nodes/search.py,sha256=DdHhEY7fmWUqis6Nk0bj-di56-ML262B51N9zytzKYk,3699
36
37
  haiku/rag/graph/nodes/synthesize.py,sha256=WF0D44SwLP1OK8C6ViOAhFOtGQ0mj3aO54z5bemJb4E,1828
37
- haiku/rag/qa/__init__.py,sha256=eFRV5GFwe1UsqniEqOLdzAMT2J6QhSiHq5_Li7c6Fs4,520
38
- haiku/rag/qa/agent.py,sha256=sN2SVpaQAxg5Hm47LhrHpbo3ELVi1ev9DxKu_ec1c-Y,3123
38
+ haiku/rag/qa/__init__.py,sha256=Q18B5cjgYSuOdzwsJkXDeqcclAI2pu3tBIcWLcMTT5M,949
39
+ haiku/rag/qa/agent.py,sha256=ReuvluxVzaH82PhrFLNAAM3rVrSj-sKHkhki266SsGI,3181
39
40
  haiku/rag/qa/prompts.py,sha256=Lqwn3m4zCsu_CJiC4s9cLsuPNbb9nq6j2PqEF3lw1eA,3380
40
41
  haiku/rag/qa/deep/__init__.py,sha256=SnCpWxWip-TaFzVKlFyrOgYeXEqT_gpIlaSItEEJ6r0,50
41
42
  haiku/rag/qa/deep/dependencies.py,sha256=AKFqcC1D3N1VPudnFmLH29K5eJWEC5wtwUGkO4FM4jc,998
@@ -44,11 +45,11 @@ haiku/rag/qa/deep/models.py,sha256=siZMQXD21_3nk8kaLCv0BCuD9TydLYo-yC4-9CxQy3E,6
44
45
  haiku/rag/qa/deep/nodes.py,sha256=XbDujD_xg-NsJS2oYm3LqkfxeHZCzT2f9wBNhVh0wns,10442
45
46
  haiku/rag/qa/deep/prompts.py,sha256=t1fEvoD5Rdab92eAOvefv2wBVmkPFuR0BQ8Kh4X0-mY,2565
46
47
  haiku/rag/qa/deep/state.py,sha256=ICFIX0oRtBs6Sdq9YnmnP04BkGiQYwucfS3Mf8XLcCU,570
47
- haiku/rag/reranking/__init__.py,sha256=95ApqN51rcog9MLkTh_uNE69qOVozO1Z6KMbZZj8nH0,963
48
- haiku/rag/reranking/base.py,sha256=LM9yUSSJ414UgBZhFTgxGprlRqzfTe4I1vgjricz2JY,405
49
- haiku/rag/reranking/cohere.py,sha256=1iTdiaa8vvb6oHVB2qpWzUOVkyfUcimVSZp6Qr4aq4c,1049
50
- haiku/rag/reranking/mxbai.py,sha256=uveGFIdmNmepd2EQsvYr64wv0ra2_wB845hdSZXy5Cw,908
51
- haiku/rag/reranking/vllm.py,sha256=xVGH9ss-ISWdJ5SKUUHUbTqBo7PIEmA_SQv0ScdJ6XA,1479
48
+ haiku/rag/reranking/__init__.py,sha256=34xH2YZ7OCC3H8Yb-zyPuehTRQdijMSY9TC8AmZDOGQ,1296
49
+ haiku/rag/reranking/base.py,sha256=Yji15nAR8LyIJGqZvEZifTWmortNQ4k_7ZHst_5mRYk,408
50
+ haiku/rag/reranking/cohere.py,sha256=BhBPPnaSnDoVlkL_MHF74kegXQBrsZGKnWqC40ztiAk,1050
51
+ haiku/rag/reranking/mxbai.py,sha256=qR55dmpaBz15lSN_wXD3-Z6Kqr_bmNKU9q4Pwef_wB8,911
52
+ haiku/rag/reranking/vllm.py,sha256=Ip83qzV2RM7qXTj0mE2St66hvXykovoNW8Hu3AUebDc,1489
52
53
  haiku/rag/research/__init__.py,sha256=xfVzYkt8QETjZaP8v4LdK8MC2R_JmxKDD34LefvrJbo,201
53
54
  haiku/rag/research/common.py,sha256=mrKXolTnDxcONjodmSPVAtXYWqbU0Bie2W-4lOTclGY,2988
54
55
  haiku/rag/research/dependencies.py,sha256=hiGImk7HyJF4LRYnTmsLFGzY1QrGjAyPW5vb_2JQRDo,8148
@@ -58,19 +59,19 @@ haiku/rag/research/prompts.py,sha256=opz4MXjoDHH1wjG6bPyiqT0LVzk3pBA6y_a9zpBW8yM
58
59
  haiku/rag/research/state.py,sha256=P8RXJMi3wA3l1j6yo8dsAyso6S27FgqS7fvZUUY447A,917
59
60
  haiku/rag/research/stream.py,sha256=amyGDimkNp_FHYUXCqtpbeDOx7sC1jQ-7DwoxuNOL1g,5576
60
61
  haiku/rag/store/__init__.py,sha256=R2IRcxtkFDxqa2sgMirqLq3l2-FPdWr6ydYStaqm5OQ,104
61
- haiku/rag/store/engine.py,sha256=n2IxztyN2UpLLSUVXurjL-e_ANthKUpWyB1gdHfgBMM,11468
62
+ haiku/rag/store/engine.py,sha256=FP1-9LOxoEvQBswYcM2GS_E2RpvSZct49vVktL-oPlo,11697
62
63
  haiku/rag/store/models/__init__.py,sha256=kc7Ctf53Jr483tk4QTIrcgqBbXDz4ZoeYSkFXfPnpks,89
63
64
  haiku/rag/store/models/chunk.py,sha256=3EuZav4QekJIeHBCub48EM8SjNX8HEJ6wVDXGot4PEQ,421
64
65
  haiku/rag/store/models/document.py,sha256=cZXy_jEti-hnhq7FKhuhCfd99ccY9fIHMLovB_Thbb8,425
65
66
  haiku/rag/store/repositories/__init__.py,sha256=Olv5dLfBQINRV3HrsfUpjzkZ7Qm7goEYyMNykgo_DaY,291
66
- haiku/rag/store/repositories/chunk.py,sha256=B0CowrBNy0fd8GLnVJVfqDaLoWxEPPJK3SODya0I0OI,14093
67
- haiku/rag/store/repositories/document.py,sha256=EtgD5pDjghXf6dloBOOEVJp8DI9O_celc_FbYzOywAE,8125
68
- haiku/rag/store/repositories/settings.py,sha256=ObrDrzxHn-yA1WcbgIoJoVmAbVvQHAFvEdRyJFt5Opc,5685
67
+ haiku/rag/store/repositories/chunk.py,sha256=bXa-NBfLdLKJuFLGEKQhFlsLi-XNbojhQYVyBjwUxz8,14205
68
+ haiku/rag/store/repositories/document.py,sha256=UOC_5QEUl-3RnGPJzn92KjrCnhmh5TmWln4yd5cJ4Ss,8133
69
+ haiku/rag/store/repositories/settings.py,sha256=15gS7Xj7cG4qetv_ioxZO_r31by7GuSqtpowOsMkHmc,6129
69
70
  haiku/rag/store/upgrades/__init__.py,sha256=RQ8A6rEXBASLb5PD9vdDnEas_m_GgRzzdVu4B88Snqc,1975
70
71
  haiku/rag/store/upgrades/v0_10_1.py,sha256=qNGnxj6hoHaHJ1rKTiALfw0c9NQOi0KAK-VZCD_073A,1959
71
72
  haiku/rag/store/upgrades/v0_9_3.py,sha256=NrjNilQSgDtFWRbL3ZUtzQzJ8tf9u0dDRJtnDFwwbdw,3322
72
- haiku_rag-0.12.0.dist-info/METADATA,sha256=RNOibOVVUHbG7NMf_isKEKmsCnrcyCDXKN-34qZu_fU,7295
73
- haiku_rag-0.12.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
74
- haiku_rag-0.12.0.dist-info/entry_points.txt,sha256=G1U3nAkNd5YDYd4v0tuYFbriz0i-JheCsFuT9kIoGCI,48
75
- haiku_rag-0.12.0.dist-info/licenses/LICENSE,sha256=eXZrWjSk9PwYFNK9yUczl3oPl95Z4V9UXH7bPN46iPo,1065
76
- haiku_rag-0.12.0.dist-info/RECORD,,
73
+ haiku_rag-0.13.0.dist-info/METADATA,sha256=d7r87GHCKyBiEBM_-3lSc8kVgumsiTX0TQJqi_4LJ3s,8101
74
+ haiku_rag-0.13.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
75
+ haiku_rag-0.13.0.dist-info/entry_points.txt,sha256=G1U3nAkNd5YDYd4v0tuYFbriz0i-JheCsFuT9kIoGCI,48
76
+ haiku_rag-0.13.0.dist-info/licenses/LICENSE,sha256=eXZrWjSk9PwYFNK9yUczl3oPl95Z4V9UXH7bPN46iPo,1065
77
+ haiku_rag-0.13.0.dist-info/RECORD,,
haiku/rag/config.py DELETED
@@ -1,90 +0,0 @@
1
- import os
2
- from pathlib import Path
3
-
4
- from dotenv import load_dotenv
5
- from pydantic import BaseModel, field_validator
6
-
7
- from haiku.rag.utils import get_default_data_dir
8
-
9
- load_dotenv()
10
-
11
-
12
- class AppConfig(BaseModel):
13
- ENV: str = "production"
14
-
15
- LANCEDB_API_KEY: str = ""
16
- LANCEDB_URI: str = ""
17
- LANCEDB_REGION: str = ""
18
-
19
- DEFAULT_DATA_DIR: Path = get_default_data_dir()
20
- MONITOR_DIRECTORIES: list[Path] = []
21
-
22
- EMBEDDINGS_PROVIDER: str = "ollama"
23
- EMBEDDINGS_MODEL: str = "qwen3-embedding"
24
- EMBEDDINGS_VECTOR_DIM: int = 4096
25
-
26
- RERANK_PROVIDER: str = ""
27
- RERANK_MODEL: str = ""
28
-
29
- QA_PROVIDER: str = "ollama"
30
- QA_MODEL: str = "gpt-oss"
31
-
32
- # Research defaults (fallback to QA if not provided via env)
33
- RESEARCH_PROVIDER: str = "ollama"
34
- RESEARCH_MODEL: str = "gpt-oss"
35
-
36
- CHUNK_SIZE: int = 256
37
- CONTEXT_CHUNK_RADIUS: int = 0
38
-
39
- # Optional dotted path or file path to a callable that preprocesses
40
- # markdown content before chunking. Examples:
41
- MARKDOWN_PREPROCESSOR: str = ""
42
-
43
- OLLAMA_BASE_URL: str = "http://localhost:11434"
44
-
45
- VLLM_EMBEDDINGS_BASE_URL: str = ""
46
- VLLM_RERANK_BASE_URL: str = ""
47
- VLLM_QA_BASE_URL: str = ""
48
- VLLM_RESEARCH_BASE_URL: str = ""
49
-
50
- # Provider keys
51
- VOYAGE_API_KEY: str = ""
52
- OPENAI_API_KEY: str = ""
53
- ANTHROPIC_API_KEY: str = ""
54
- COHERE_API_KEY: str = ""
55
-
56
- # If true, refuse to auto-create a new LanceDB database or tables
57
- # and error out when the database does not already exist.
58
- DISABLE_DB_AUTOCREATE: bool = False
59
-
60
- # Vacuum retention threshold in seconds. Only versions older than this
61
- # threshold will be removed during vacuum operations. Default is 60 seconds
62
- # to allow concurrent connections to safely use recent versions.
63
- VACUUM_RETENTION_SECONDS: int = 60
64
-
65
- # Maximum number of A2A contexts to keep in memory. When exceeded, least
66
- # recently used contexts will be evicted. Default is 1000.
67
- A2A_MAX_CONTEXTS: int = 1000
68
-
69
- @field_validator("MONITOR_DIRECTORIES", mode="before")
70
- @classmethod
71
- def parse_monitor_directories(cls, v):
72
- if isinstance(v, str):
73
- if not v.strip():
74
- return []
75
- return [
76
- Path(path.strip()).absolute() for path in v.split(",") if path.strip()
77
- ]
78
- return v
79
-
80
-
81
- # Expose Config object for app to import
82
- Config = AppConfig.model_validate(os.environ)
83
- if Config.OPENAI_API_KEY:
84
- os.environ["OPENAI_API_KEY"] = Config.OPENAI_API_KEY
85
- if Config.VOYAGE_API_KEY:
86
- os.environ["VOYAGE_API_KEY"] = Config.VOYAGE_API_KEY
87
- if Config.ANTHROPIC_API_KEY:
88
- os.environ["ANTHROPIC_API_KEY"] = Config.ANTHROPIC_API_KEY
89
- if Config.COHERE_API_KEY:
90
- os.environ["CO_API_KEY"] = Config.COHERE_API_KEY
haiku/rag/migration.py DELETED
@@ -1,316 +0,0 @@
1
- import json
2
- import sqlite3
3
- import struct
4
- from pathlib import Path
5
- from uuid import uuid4
6
-
7
- from rich.console import Console
8
- from rich.progress import Progress, TaskID
9
-
10
- from haiku.rag.store.engine import Store
11
-
12
-
13
- def deserialize_sqlite_embedding(data: bytes) -> list[float]:
14
- """Deserialize sqlite-vec embedding from bytes."""
15
- if not data:
16
- return []
17
- # sqlite-vec stores embeddings as float32 arrays
18
- num_floats = len(data) // 4
19
- return list(struct.unpack(f"{num_floats}f", data))
20
-
21
-
22
- class SQLiteToLanceDBMigrator:
23
- """Migrates data from SQLite to LanceDB."""
24
-
25
- def __init__(self, sqlite_path: Path, lancedb_path: Path):
26
- self.sqlite_path = sqlite_path
27
- self.lancedb_path = lancedb_path
28
- self.console = Console()
29
-
30
- async def migrate(self) -> bool:
31
- """Perform the migration."""
32
- try:
33
- self.console.print(
34
- f"[blue]Starting migration from {self.sqlite_path} to {self.lancedb_path}[/blue]"
35
- )
36
-
37
- # Check if SQLite database exists
38
- if not self.sqlite_path.exists():
39
- self.console.print(
40
- f"[red]SQLite database not found: {self.sqlite_path}[/red]"
41
- )
42
- return False
43
-
44
- # Connect to SQLite database
45
- sqlite_conn = sqlite3.connect(self.sqlite_path)
46
- sqlite_conn.row_factory = sqlite3.Row
47
-
48
- # Load the sqlite-vec extension
49
- try:
50
- import sqlite_vec # type: ignore
51
-
52
- sqlite_conn.enable_load_extension(True)
53
- sqlite_vec.load(sqlite_conn)
54
- self.console.print("[cyan]Loaded sqlite-vec extension[/cyan]")
55
- except Exception as e:
56
- self.console.print(
57
- f"[yellow]Warning: Could not load sqlite-vec extension: {e}[/yellow]"
58
- )
59
- self.console.print(
60
- "[yellow]Install sqlite-vec with[/yellow]\n[green]uv pip install sqlite-vec [/green]"
61
- )
62
- exit(1)
63
-
64
- # Create LanceDB store
65
- lance_store = Store(self.lancedb_path, skip_validation=True)
66
-
67
- with Progress() as progress:
68
- # Migrate documents
69
- doc_task = progress.add_task(
70
- "[green]Migrating documents...", total=None
71
- )
72
- document_id_mapping = self._migrate_documents(
73
- sqlite_conn, lance_store, progress, doc_task
74
- )
75
-
76
- # Migrate chunks and embeddings
77
- chunk_task = progress.add_task(
78
- "[yellow]Migrating chunks and embeddings...", total=None
79
- )
80
- self._migrate_chunks(
81
- sqlite_conn, lance_store, progress, chunk_task, document_id_mapping
82
- )
83
-
84
- # Migrate settings
85
- settings_task = progress.add_task(
86
- "[blue]Migrating settings...", total=None
87
- )
88
- self._migrate_settings(
89
- sqlite_conn, lance_store, progress, settings_task
90
- )
91
-
92
- sqlite_conn.close()
93
-
94
- # Optimize and cleanup using centralized vacuum
95
- self.console.print("[cyan]Optimizing LanceDB...[/cyan]")
96
- try:
97
- await lance_store.vacuum()
98
- self.console.print("[green]✅ Optimization completed[/green]")
99
- except Exception as e:
100
- self.console.print(
101
- f"[yellow]Warning: Optimization failed: {e}[/yellow]"
102
- )
103
-
104
- lance_store.close()
105
-
106
- self.console.print("[green]✅ Migration completed successfully![/green]")
107
- self.console.print(
108
- f"[green]✅ Migrated {len(document_id_mapping)} documents[/green]"
109
- )
110
- return True
111
-
112
- except Exception as e:
113
- self.console.print(f"[red]❌ Migration failed: {e}[/red]")
114
- import traceback
115
-
116
- self.console.print(f"[red]{traceback.format_exc()}[/red]")
117
- return False
118
-
119
- def _migrate_documents(
120
- self,
121
- sqlite_conn: sqlite3.Connection,
122
- lance_store: Store,
123
- progress: Progress,
124
- task: TaskID,
125
- ) -> dict[int, str]:
126
- """Migrate documents from SQLite to LanceDB and return ID mapping."""
127
- cursor = sqlite_conn.cursor()
128
- cursor.execute(
129
- "SELECT id, content, uri, metadata, created_at, updated_at FROM documents ORDER BY id"
130
- )
131
-
132
- documents = []
133
- id_mapping = {} # Maps old integer ID to new UUID
134
-
135
- for row in cursor.fetchall():
136
- new_uuid = str(uuid4())
137
- id_mapping[row["id"]] = new_uuid
138
-
139
- doc_data = {
140
- "id": new_uuid,
141
- "content": row["content"],
142
- "uri": row["uri"],
143
- "metadata": json.loads(row["metadata"]) if row["metadata"] else {},
144
- "created_at": row["created_at"],
145
- "updated_at": row["updated_at"],
146
- }
147
- documents.append(doc_data)
148
-
149
- # Batch insert documents to LanceDB
150
- if documents:
151
- from haiku.rag.store.engine import DocumentRecord
152
-
153
- doc_records = [
154
- DocumentRecord(
155
- id=doc["id"],
156
- content=doc["content"],
157
- uri=doc["uri"],
158
- metadata=json.dumps(doc["metadata"]),
159
- created_at=doc["created_at"],
160
- updated_at=doc["updated_at"],
161
- )
162
- for doc in documents
163
- ]
164
- lance_store.documents_table.add(doc_records)
165
-
166
- progress.update(task, completed=len(documents), total=len(documents))
167
- return id_mapping
168
-
169
- def _migrate_chunks(
170
- self,
171
- sqlite_conn: sqlite3.Connection,
172
- lance_store: Store,
173
- progress: Progress,
174
- task: TaskID,
175
- document_id_mapping: dict[int, str],
176
- ):
177
- """Migrate chunks and embeddings from SQLite to LanceDB."""
178
- cursor = sqlite_conn.cursor()
179
-
180
- # Get chunks first
181
- cursor.execute("""
182
- SELECT id, document_id, content, metadata
183
- FROM chunks
184
- ORDER BY id
185
- """)
186
-
187
- chunks_data = cursor.fetchall()
188
-
189
- # Get embeddings using the sqlite-vec virtual table
190
- embeddings_map = {}
191
- try:
192
- # Use the virtual table to get embeddings properly
193
- cursor.execute("""
194
- SELECT chunk_id, embedding
195
- FROM chunk_embeddings
196
- """)
197
-
198
- for row in cursor.fetchall():
199
- chunk_id = row[0]
200
- embedding_blob = row[1]
201
- if embedding_blob and chunk_id not in embeddings_map:
202
- embeddings_map[chunk_id] = embedding_blob
203
-
204
- except sqlite3.OperationalError as e:
205
- self.console.print(
206
- f"[yellow]Warning: Could not extract embeddings from virtual table: {e}[/yellow]"
207
- )
208
-
209
- chunks = []
210
- for row in chunks_data:
211
- # Generate new UUID for chunk
212
- chunk_uuid = str(uuid4())
213
-
214
- # Map the old document_id to new UUID
215
- document_uuid = document_id_mapping.get(row["document_id"])
216
- if not document_uuid:
217
- self.console.print(
218
- f"[yellow]Warning: Document ID {row['document_id']} not found in mapping for chunk {row['id']}[/yellow]"
219
- )
220
- continue
221
-
222
- # Get embedding for this chunk
223
- embedding = []
224
- embedding_blob = embeddings_map.get(row["id"])
225
- if embedding_blob:
226
- try:
227
- embedding = deserialize_sqlite_embedding(embedding_blob)
228
- except Exception as e:
229
- self.console.print(
230
- f"[yellow]Warning: Failed to deserialize embedding for chunk {row['id']}: {e}[/yellow]"
231
- )
232
- # Generate a zero vector of the expected dimension
233
- embedding = [0.0] * lance_store.embedder._vector_dim
234
- else:
235
- # No embedding found, generate zero vector
236
- embedding = [0.0] * lance_store.embedder._vector_dim
237
-
238
- chunk_data = {
239
- "id": chunk_uuid,
240
- "document_id": document_uuid,
241
- "content": row["content"],
242
- "metadata": json.loads(row["metadata"]) if row["metadata"] else {},
243
- "vector": embedding,
244
- }
245
- chunks.append(chunk_data)
246
-
247
- # Batch insert chunks to LanceDB
248
- if chunks:
249
- chunk_records = [
250
- lance_store.ChunkRecord(
251
- id=chunk["id"],
252
- document_id=chunk["document_id"],
253
- content=chunk["content"],
254
- metadata=json.dumps(chunk["metadata"]),
255
- vector=chunk["vector"],
256
- )
257
- for chunk in chunks
258
- ]
259
- lance_store.chunks_table.add(chunk_records)
260
-
261
- progress.update(task, completed=len(chunks), total=len(chunks))
262
-
263
- def _migrate_settings(
264
- self,
265
- sqlite_conn: sqlite3.Connection,
266
- lance_store: Store,
267
- progress: Progress,
268
- task: TaskID,
269
- ):
270
- """Migrate settings from SQLite to LanceDB."""
271
- cursor = sqlite_conn.cursor()
272
-
273
- try:
274
- cursor.execute("SELECT id, settings FROM settings WHERE id = 1")
275
- row = cursor.fetchone()
276
-
277
- if row:
278
- settings_data = json.loads(row["settings"]) if row["settings"] else {}
279
-
280
- # Update the existing settings in LanceDB (use string ID)
281
- lance_store.settings_table.update(
282
- where="id = 'settings'",
283
- values={"settings": json.dumps(settings_data)},
284
- )
285
-
286
- progress.update(task, completed=1, total=1)
287
- else:
288
- progress.update(task, completed=0, total=0)
289
-
290
- except sqlite3.OperationalError:
291
- # Settings table doesn't exist in old SQLite database
292
- self.console.print(
293
- "[yellow]No settings table found in SQLite database[/yellow]"
294
- )
295
- progress.update(task, completed=0, total=0)
296
-
297
-
298
- async def migrate_sqlite_to_lancedb(
299
- sqlite_path: Path, lancedb_path: Path | None = None
300
- ) -> bool:
301
- """
302
- Migrate an existing SQLite database to LanceDB.
303
-
304
- Args:
305
- sqlite_path: Path to the existing SQLite database
306
- lancedb_path: Path for the new LanceDB database (optional, will auto-generate if not provided)
307
-
308
- Returns:
309
- True if migration was successful, False otherwise
310
- """
311
- if lancedb_path is None:
312
- # Auto-generate LanceDB path
313
- lancedb_path = sqlite_path.parent / (sqlite_path.stem + ".lancedb")
314
-
315
- migrator = SQLiteToLanceDBMigrator(sqlite_path, lancedb_path)
316
- return await migrator.migrate()