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¶
- Schema introspection — the executor inspects your model's fields and builds a search schema describing available filters
- LLM analysis — the query is sent to the LLM along with the schema, and the LLM generates structured filters
- 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},
)