Skip to content

A document's request history should go with the document (and a memory's with the memory) #6098

Open

Description

@Tacit-Intelligence

#5948 deletes a bank's llm_requests together with the bank. delete_document and memory invalidation leave theirs behind. A client that offers its users a real "forget this conversation" can delete the document, its memories and its observations, but the raw prompt and answer of every model call that read the text stay in llm_requests until the retention sweep runs. That covers retain extraction, consolidation and refreshes.

The rows are already addressable:

  • retain rows carry metadata.document_id and metadata.memory_ids;
  • consolidation rows carry memory_ids and source_memory_ids;
  • GET /v1/default/banks/{bank_id}/llm-requests already filters on document_id and memory_id.

Nothing can delete them, though. There is no delete endpoint, and no admin command.

Proposal, in three independent parts:

  1. A document's request history goes with the document. delete_document(bank_id, document_id) also runs the following, in the same transaction, with $3 the unit ids it is deleting (it already has them), and gated by _llm_requests_persistable() like fix(banks): delete a bank's LLM request history with the bank #5948:
    DELETE FROM llm_requests
    WHERE bank_id = $1
      AND (metadata->>'document_id' = $2
           OR metadata->'memory_ids' ?| $3
           OR metadata->'source_memory_ids' ?| $3)
  2. A delete endpoint with the GET's filters. DELETE /v1/default/banks/{bank_id}/llm-requests takes the same filters as the list route (document_id, memory_id, trace_id, operation, scope, start_date, end_date), with at least one required. It runs under a new BankWriteOperation.DELETE_LLM_REQUESTS, so an operation validator can gate it, and returns {"deleted": n}. This lets a client purge the traces of a memory it invalidates rather than deletes.
  3. Every row that quotes a memory is reachable by memory_id. Today refresh_mental_model, reflect and rerank rows carry no link. The refresh and reflect paths would call record_source_memory_ids() with the memories their prompt quoted, and the rerank trace would carry the candidate memory ids beside the texts.

Rough size: part 1 is about 15 lines beside the #5948 block. Part 2 is about 80 lines: an engine method reusing list_llm_requests' filter builder, plus a route and a test. Part 3 is about 20 lines. Tracing stays on by default, so debugging keeps working; only the forgotten text goes. We'd be glad to send a PR for any part if that helps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions