-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathingest.py
More file actions
115 lines (91 loc) · 3.78 KB
/
Copy pathingest.py
File metadata and controls
115 lines (91 loc) · 3.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
"""
RAG Ingestion Script
Reads all markdown files from data/RAG, embeds them using Google's
text-embedding-004 model, and stores them in a local ChromaDB collection.
Run this script once (and re-run whenever the RAG data changes):
python ingest.py
"""
import os
import glob
import time
from google import genai
from google.genai import types
from dotenv import load_dotenv
from sqlmodel import Session
from sqlalchemy import text
from database import engine
from models import RAGKnowledge
load_dotenv()
# ── Config ────────────────────────────────────────────────────────────────────
RAG_DATA_DIR = os.path.join(os.path.dirname(__file__), "data", "RAG")
COLLECTION_NAME = "rag_knowledge"
EMBEDDING_MODEL = "models/gemini-embedding-001"
# ── Google Generative AI (new SDK) ───────────────────────────────────────────
GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
if not GOOGLE_API_KEY:
raise EnvironmentError("GOOGLE_API_KEY is not set in your .env file.")
client = genai.Client(api_key=GOOGLE_API_KEY)
def embed_text(text: str, retries=5) -> list[float]:
"""Embed a single text using Google's text-embedding-004, with retry logic."""
for attempt in range(retries):
try:
response = client.models.embed_content(
model=EMBEDDING_MODEL,
contents=text,
config=types.EmbedContentConfig(task_type="RETRIEVAL_DOCUMENT"),
)
time.sleep(1) # Delay to prevent hitting rate limits
return response.embeddings[0].values
except Exception as e:
if attempt < retries - 1:
wait_time = 2 ** attempt
print(f" [!] API Error: {e}. Retrying in {wait_time}s...")
time.sleep(wait_time)
else:
raise e
def load_markdown_files() -> list[dict]:
"""Recursively find and read all .md files under RAG_DATA_DIR."""
pattern = os.path.join(RAG_DATA_DIR, "**", "*.md")
files = glob.glob(pattern, recursive=True)
documents = []
for path in files:
with open(path, "r", encoding="utf-8") as f:
content = f.read().strip()
rel_path = os.path.relpath(path, RAG_DATA_DIR)
source = rel_path.replace("\\", "/")
parts = rel_path.split(os.sep)
category = parts[0] if len(parts) > 1 else "general"
documents.append({
"id": source,
"content": content,
"metadata": {
"source": source,
"category": category,
"filename": os.path.basename(path),
},
})
return documents
def ingest():
docs = load_markdown_files()
print(f"\nFound {len(docs)} markdown files - embedding...\n")
with Session(engine) as session:
# Clear existing knowledge base
session.execute(text("TRUNCATE TABLE ragknowledge CASCADE"))
session.commit()
print("Cleared existing database collection")
for i, doc in enumerate(docs, 1):
print(f" [{i:2}/{len(docs)}] {doc['metadata']['source']}")
embedding = embed_text(doc["content"])
db_item = RAGKnowledge(
id=doc["id"],
content=doc["content"],
embedding=embedding,
source=doc["metadata"]["source"],
category=doc["metadata"]["category"],
filename=doc["metadata"]["filename"]
)
session.add(db_item)
session.commit()
print(f"\nDone! {len(docs)} documents indexed into Neon pgvector")
if __name__ == "__main__":
ingest()