refactor: 🔨 Mostly working Ollama migration, few tweaks left

This commit is contained in:
2026-05-16 09:11:21 +01:00
parent 968b98f6a2
commit fc08f1a814
6 changed files with 43 additions and 37 deletions
+12 -12
View File
@@ -1,24 +1,24 @@
# --- Connection Settings --- # --- Connection Settings ---
api: api:
base_url: "http://framework.tawny-bellatrix.ts.net:1234" base_url: "http://100.110.238.94:11434"
api_version: "/v1/" api_version: "/v1/"
# --- Model Settings --- # --- Model Settings ---
models: models:
enrich: "lm_studio/qwen-" # will have an identifier, based on amount of active LLMs see ./load_ingestion_llms.sh enrich: "ollama/granite4.1:3b"
embedding: "text-embedding-qwen3-embedding-8b" embedding: "qwen3-embedding:4b"
retrieval: "lm_studio/qwen/qwen3-30b-a3b-2507" retrieval: "ollama/qwen3.6:latest"
expansion: "lm_studio/qwen/qwen3-30b-a3b-2507" expansion: "ollama/granite4.1:3b"
# --- Ingestion Settings --- # --- Ingestion Settings ---
ingestion: ingestion:
data_dir: "/home/jake/DnD" data_dir: "/home/jake/DnD"
db_path: "./data/" db_path: "./data/"
db_name: "dmv.db" db_name: "dmv.db"
active_llms: 2 active_llms: 1
parallel_requests_per_llm: 2 parallel_requests_per_llm: 6
chunk_size: 800 chunk_size: 1200
chunk_overlap: 100 chunk_overlap: 200
embedding_batch_size: 32 embedding_batch_size: 32
time_file_location: "./data/time_file.txt" time_file_location: "./data/time_file.txt"
@@ -29,7 +29,7 @@ ingestion_agent:
Analyze the provided notes and extract a concise synopsis and relevant metadata. Analyze the provided notes and extract a concise synopsis and relevant metadata.
synopsis = A one-sentence summary of the document. synopsis = A one-sentence summary of the document.
tags = Relevant tags (NPCs, Locations, Items, Plot Points). tags = Relevant tags (NPCs, Locations, Items, Plot Points).
entities = a list of Key names of people, places, or factions. entities = a list of names for people, places, or factions.
"note -> synopsis:str, tags: list[str], entities: list[str]" "note -> synopsis:str, tags: list[str], entities: list[str]"
retrieval_agent: retrieval_agent:
@@ -41,7 +41,7 @@ retrieval_agent:
expansion_agent: expansion_agent:
expansion_signature: | expansion_signature: |
You are a query expansion expert, specialised in Dungeons and Dragons. You are an expert Dungeon Master's assistant.
Given a user's question, generate 3-5 similar but enhanced search queries that would help find more relevant information. Given a user's question, generate 3-5 similar but enhanced search queries that would help find more relevant information in DnD notes.
Each expanded query should be distinct and add different perspective to the original question. Each expanded query should be distinct and add different perspective to the original question.
Return only the queries as a JSON list with key "queries".""" Return only the queries as a JSON list with key "queries"."""
+2 -2
View File
@@ -1,5 +1,5 @@
lms load qwen-4b-instruct-2507 --parallel 2 --identifier "qwen-0" --ttl 1800 # lms load qwen-4b-instruct-2507 --parallel 2 --identifier "qwen-0" --ttl 1800
lms load qwen-4b-instruct-2507 --parallel 2 --identifier "qwen-1" --ttl 1800 # lms load qwen-4b-instruct-2507 --parallel 2 --identifier "qwen-1" --ttl 1800
# lms load qwen-4b-instruct-2507 --parallel 2 --identifier "qwen-2" --ttl 1800 # lms load qwen-4b-instruct-2507 --parallel 2 --identifier "qwen-2" --ttl 1800
# lms load qwen-4b-instruct-2507 --parallel 2 --identifier "qwen-3" --ttl 1800 # lms load qwen-4b-instruct-2507 --parallel 2 --identifier "qwen-3" --ttl 1800
# lms load qwen-4b-instruct-2507 --parallel 2 --identifier "qwen-4" --ttl 1800 # lms load qwen-4b-instruct-2507 --parallel 2 --identifier "qwen-4" --ttl 1800
+3 -2
View File
@@ -10,7 +10,7 @@ API_VERSION = CFG["api"]["api_version"]
class LocalLMEmbeddings(Embeddings): class LocalLMEmbeddings(Embeddings):
def __init__(self, model: str, base_url: str = API_BASE, batch_size: int = 32): def __init__(self, model: str, base_url: str = API_BASE, batch_size: int = 32):
self.url = f"{base_url}/{API_VERSION}embeddings" self.url = f"{base_url}/api/embed"
self.model = model self.model = model
self.batch_size = batch_size self.batch_size = batch_size
@@ -22,10 +22,11 @@ class LocalLMEmbeddings(Embeddings):
response = requests.post( response = requests.post(
self.url, json=payload, timeout=120 self.url, json=payload, timeout=120
) # Longer timeout for batches ) # Longer timeout for batches
# print(response)
response.raise_for_status() response.raise_for_status()
data = response.json() data = response.json()
# print(data) # print(data)
return [item["embedding"] for item in data["data"]] return data["embeddings"]
except Exception as e: except Exception as e:
print(f"❌ Batch request failed: {e}") print(f"❌ Batch request failed: {e}")
# Returning empty lists to maintain index integrity if needed, # Returning empty lists to maintain index integrity if needed,
+10 -12
View File
@@ -18,7 +18,7 @@ EXPANSION_CONFIG = CFG["expansion_agent"]
def retrieve_from_turso(embedded_question, k=5): def retrieve_from_turso(embedded_question, k=5):
query = f""" query = f"""
SELECT file_path, synopsis, tags, entities, chunk_data, SELECT file_path, synopsis, tags, chunk_data,
vector_distance_cos(embedding, vector32('{embedded_question}')) AS distance vector_distance_cos(embedding, vector32('{embedded_question}')) AS distance
FROM notes FROM notes
ORDER BY distance ASC ORDER BY distance ASC
@@ -55,9 +55,7 @@ class DnDRAG(dspy.Module):
base_url=API_BASE, base_url=API_BASE,
# batch_size=1, # batch_size=1,
) )
self.retrieval_lm = dspy.LM( self.retrieval_lm = dspy.LM(model=CFG["models"]["retrieval"], api_base=API_BASE)
model=CFG["models"]["retrieval"], api_base=API_BASE + CFG["api"]["api_version"]
)
with dspy.context(lm=self.retrieval_lm, signature=ExpansionSignature): with dspy.context(lm=self.retrieval_lm, signature=ExpansionSignature):
self.query_expander = dspy.Predict("question -> queries:list[str]") self.query_expander = dspy.Predict("question -> queries:list[str]")
@@ -68,9 +66,9 @@ class DnDRAG(dspy.Module):
print("Enhancing Question") print("Enhancing Question")
with dspy.context(lm=self.retrieval_lm): with dspy.context(lm=self.retrieval_lm):
expanded_queries = self.query_expander(question=question).queries expanded_queries = self.query_expander(question=question).queries
print("Enhanced Queries:") # print("Enhanced Queries:")
for q in expanded_queries: # for q in expanded_queries:
print(" ", q) # print(" ", q)
all_embeddings = self.embeddings_model.embed_documents([question] + expanded_queries) all_embeddings = self.embeddings_model.embed_documents([question] + expanded_queries)
# print(all_embeddings) # print(all_embeddings)
all_results = [] all_results = []
@@ -81,7 +79,7 @@ class DnDRAG(dspy.Module):
seen = set() seen = set()
unique_results = [] unique_results = []
for row in all_results: for row in all_results:
key = (row[0], row[4]) key = (row[0], row[3])
if key not in seen: if key not in seen:
seen.add(key) seen.add(key)
unique_results.append(row) unique_results.append(row)
@@ -91,18 +89,18 @@ class DnDRAG(dspy.Module):
source = row[0] source = row[0]
synopsis = row[1] synopsis = row[1]
tags = row[2] tags = row[2]
entities = row[3] # entities = row[3]
content = row[4] content = row[3]
closeness = row[5] closeness = row[4]
context_parts.append(f""" context_parts.append(f"""
--- Chunk {i + 1} from {source} --- --- Chunk {i + 1} from {source} ---
synopsis: {synopsis}, synopsis: {synopsis},
tags: {tags}, tags: {tags},
entities: {entities},
closeness: {closeness}, closeness: {closeness},
{content} {content}
""") """)
# entities: {entities},
context = "\n\n".join(context_parts) context = "\n\n".join(context_parts)
+15 -7
View File
@@ -83,7 +83,7 @@ def enrich_chunks(chunks: list) -> list:
try: try:
with dspy.context( with dspy.context(
lm=dspy.LM(model=f"{MODEL_BASE}{lm_index}", api_base=API_BASE + API_VERSION), lm=dspy.LM(model=f"{MODEL_BASE}", api_base=API_BASE),
chat_template_kwargs={"enable_thinking": False}, chat_template_kwargs={"enable_thinking": False},
): ):
response = IngestionAgent().ingest(note=chunk.page_content) response = IngestionAgent().ingest(note=chunk.page_content)
@@ -140,10 +140,11 @@ def embed_chunks(chunks: List[Any], batch_size: int = EMBEDDING_BATCH_SIZE) -> L
# Process chunks in batches # Process chunks in batches
for i in tqdm(range(0, total_chunks, batch_size), desc="Embedding batches"): for i in tqdm(range(0, total_chunks, batch_size), desc="Embedding batches"):
batch = chunks[i : i + batch_size] batch = chunks[i : i + batch_size]
print(f"🚀 Processing batch {(i // batch_size) + 1} (Size: {len(batch)})...") # print(f"🚀 Processing batch {(i // batch_size) + 1} (Size: {len(batch)})...")
batch_content = [chunk.page_content for chunk in batch] batch_content = [chunk.page_content for chunk in batch]
try: try:
batch_embeddings = embeddings_model.embed_documents(batch_content) batch_embeddings = embeddings_model.embed_documents(batch_content)
# print(len(batch_embeddings[0]))
# Process each chunk in the batch # Process each chunk in the batch
for j, (chunk, embedding) in enumerate(zip(batch, batch_embeddings)): for j, (chunk, embedding) in enumerate(zip(batch, batch_embeddings)):
# Extract metadata # Extract metadata
@@ -233,8 +234,15 @@ def save_to_db(chunk_dicts):
# SQL with named placeholders for clarity and safety # SQL with named placeholders for clarity and safety
insert_sql = """ insert_sql = """
INSERT INTO notes ( INSERT INTO notes (
file_path, file_name, chunk_data, synopsis, tags, entities, embedding, timestamp file_path,
) VALUES (?, ?, ?, ?, ?, ?, vector32(?), ?) file_name,
chunk_data,
synopsis,
tags,
-- entities,
embedding,
timestamp
) VALUES (?, ?, ?, ?, ?, vector32(?), ?)
""" """
# Prepare batch data: convert each dict to a tuple in correct order # Prepare batch data: convert each dict to a tuple in correct order
@@ -250,7 +258,7 @@ def save_to_db(chunk_dicts):
entry["chunk_data"], entry["chunk_data"],
entry["synopsis"], entry["synopsis"],
",".join(entry["tags"]), # Store as comma-separated string ",".join(entry["tags"]), # Store as comma-separated string
",".join(entry["entities"]), # Store as comma-separated string # ",".join(entry["entities"]), # Store as comma-separated string
embedding_str, embedding_str,
entry["timestamp"], entry["timestamp"],
) )
@@ -277,8 +285,8 @@ def create_db():
chunk_data TEXT NOT NULL, chunk_data TEXT NOT NULL,
synopsis TEXT, synopsis TEXT,
tags TEXT, -- comma-separated tags TEXT, -- comma-separated
entities TEXT, -- comma-separated -- entities TEXT, -- comma-separated
embedding F32_BLOB(4096), embedding F32_BLOB(2560),
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
) )
""") """)
+1 -2
View File
@@ -87,8 +87,7 @@ def main():
dspy.configure(verbose_errors=True) dspy.configure(verbose_errors=True)
dspy.configure(callbacks=[CallbackHandler(logger)]) dspy.configure(callbacks=[CallbackHandler(logger)])
# 1. Setup the LLM # 1. Setup the LLM
print("🚀 Initializing Qwen-8B via LM Studio...") lm = dspy.LM(RETRIEVE_MODEL, api_base=API_BASE)
lm = dspy.LM(RETRIEVE_MODEL, api_base=API_BASE + API_VERSION)
dspy.configure(lm=lm) dspy.configure(lm=lm)
# 2. Load the RAG System (only happens once!) # 2. Load the RAG System (only happens once!)