Skip to content

Natural Language Query (Read)

NLQueryExecutor converts plain English questions into structured database filters, executes them against your AMSDAL models, and returns matching records.

Basic Usage

from amsdal_ml.ml_models.openai_model import OpenAIModel
from amsdal_ml.ml_retrievers.query_retriever import NLQueryExecutor

llm = OpenAIModel()
llm.setup()

executor = NLQueryExecutor(llm=llm, queryset=Customer.objects.all())

# Analyze and execute
analysis = await executor.analyze('active customers in New York')
print(analysis.filters)   # generated ORM filters

results = await executor.execute(analysis)
for record in results:
    print(record)

Two-Step: Analyze Then Execute

For preview or confirmation workflows:

# Step 1: Generate filters without executing
analysis = await executor.analyze('customers who signed up last month')
print(analysis.filters)  # inspect the generated filters

# Step 2: Execute from analysis
results = await executor.execute(analysis)

How It Works

  1. Schema introspection — the executor inspects your model's fields and builds a search schema describing available filters
  2. LLM analysis — the query is sent to the LLM along with the schema, and the LLM generates structured filters
  3. Filter execution — generated filters are applied to the model's QuerySet via the AMSDAL ORM

Supported Field Types

The executor supports filtering on:

  • Primitives: str, int, float, bool
  • Date/time: date, datetime
  • Enums and Literals
  • Unions of supported types
  • Foreign key relationships (nested model fields)

Unsupported types (dicts, lists, callables) are skipped during schema generation.

Document-Oriented Facade

NLQueryRetriever wraps the executor and returns serialized Document objects instead of model instances:

from amsdal_ml.ml_retrievers.query_retriever import NLQueryRetriever

retriever = NLQueryRetriever(llm=llm, queryset=Customer.objects.all())

analysis = await retriever.analyze('VIP customers')
documents = await retriever.invoke(analysis)

for doc in documents:
    print(doc.page_content)  # serialized record
    print(doc.metadata)      # object class, id, etc.

Authorization

Pass a callback to check permissions on each loaded object:

executor = NLQueryExecutor(
    llm=llm,
    queryset=Customer.objects.all(),
    on_object_loaded=my_auth_callback,
)

Base Filters

Pre-apply filters that always run (e.g., tenant isolation):

executor = NLQueryExecutor(
    llm=llm,
    queryset=Customer.objects.all(),
    base_filters={'is_active': True},
)