"""Coppie domanda->SQL risolte (kind `solved_question`) — memoria attiva, parte B. Una sessione finalizzata produce UN record nel vectordb: l'embedding e' la domanda riscritta (content), il metadata porta l'SQL finale e le tabelle promosse. Vive nella tabella pgvector `memory` con kind dedicato (nessuna DDL server-side); si consulta nelle fasi F4/F6/F7 con `tht memory solved-search` come materiale di riferimento (exemplar), NON come decisione da ri-applicare. Scrittura: SOLO upsert one-row stile D11 (`save_solved_question`). Questi record non passano MAI da `VectorStore.sync`/`RestVectorWriter.sync`: il passo delete-stale del sync, ricevendo il solo record corrente, cancellerebbe le coppie delle altre sessioni. Per lo stesso motivo l'hash di dedup e' calcolato qui (domanda+SQL) e non dal solo content come fa il sync. """ from tht.vectorstore.records import VectorRecord SOLVED_KIND = "solved_question" def solved_question_record( *, session_id: str, question: str, sql: str, tables: list[str] ) -> VectorRecord: return VectorRecord( id=f"solved:{session_id}", kind=SOLVED_KIND, ref=session_id, title=question[:120], content=question, metadata={ "question": question, "sql": sql, "tables": tables, "session_id": session_id, }, ) def _solved_hash(record: VectorRecord) -> str: # La domanda e' l'embedding (content); l'SQL vive solo nel metadata. L'hash # copre entrambi: un re-finalize che cambia solo l'SQL aggiorna la riga. from tht.vectorstore.store import content_hash return content_hash(record.content + "\n" + str(record.metadata.get("sql", ""))) def save_solved_question(record: VectorRecord, *, writer, embedder) -> int: """Upsert one-row della coppia domanda->SQL via writer key (stesso pattern di save_one_memory, spec D11): hash dedup client-side, embedding solo se domanda o SQL sono cambiati. `writer` e' un VectorRestClient (writer key). Ritorna il numero di righe upsertate (0 = invariata).""" from tht.vectorstore.rest_writer import pack_metadata new_hash = _solved_hash(record) existing = writer.existing_hashes("memory", [SOLVED_KIND]) if existing.get(record.id) == new_hash: return 0 embedding = embedder.embed_documents([record.content])[0] return writer.upsert_records("memory", [{ "record_key": record.id, "kind": record.kind, "content_hash": new_hash, "metadata": pack_metadata(record), "embedding": embedding, }])