Skip to main content

Django AI Services Framework — DB-driven LLM routing, quality tiers, NL2SQL

Project description

iil-aifw — Django AI Services Framework

DB-driven LLM provider, model & usage management for Django projects.

PyPI Python License: MIT

Installation

pip install iil-aifw
# With NL2SQL support:
pip install "iil-aifw[nl2sql]"

Extras / Optional Dependencies

Extra Additional deps Purpose
nl2sql (none — activation-only) Activates aifw.nl2sql INSTALLED_APPS entry + migrations; no additional download weight
rag pgvector>=0.3 Schema-RAG via pgvector (ADR-009 Phase 1, planned)
dev pytest, hatch, ruff, iil-promptfw Development toolchain

Quick Start

# settings.py
INSTALLED_APPS = [
    ...
    "aifw",
]

# Run migrations
python manage.py migrate aifw
python manage.py init_aifw_config   # seed default providers & models
from aifw.service import sync_completion

result = sync_completion(
    action_code="story_writing",
    messages=[{"role": "user", "content": "Write a short story about a dragon."}],
)

if result.success:
    print(result.content)
else:
    print(result.error)

Features

  • DB-driven model routing — swap LLM providers/models via Django Admin, zero code changes
  • Multi-provider — OpenAI, Anthropic, Google, Ollama, any LiteLLM-compatible provider
  • Quality-level routing — map subscription tiers to model quality (economy/balanced/premium)
  • Async & synccompletion() async, sync_completion() sync, completion_with_fallback()
  • Streamingsync_completion_stream() for Django StreamingHttpResponse
  • Usage logging — automatic token & latency tracking per action, per tenant
  • Fallback models — configure primary + fallback model per action type
  • NL2SQL — natural language → SQL engine with few-shot examples and self-healing retry
  • Hybrid cache — process-local (30s) + Django cache framework (600s, Redis-aware)

Core API

from aifw.service import (
    completion,                  # async
    sync_completion,             # sync wrapper
    completion_with_fallback,    # async, tries fallback model on error
    sync_completion_with_fallback,
    sync_completion_stream,      # streaming generator
    get_quality_level_for_tier,  # tier name → QualityLevel int
    check_action_code,           # bool — action code exists in DB
)

Quality-Level Routing (v0.10.2)

Each AIActionType row can be scoped to a quality_level (1–9) and/or priority ('fast'|'balanced'|'quality'). A NULL in either dimension acts as catch-all. Lookup uses a deterministic 4-step cascade: exact → ql-only → prio-only → catch-all.

from aifw.constants import QualityLevel
from aifw.service import get_quality_level_for_tier, sync_completion

# Map a subscription tier to a quality level (DB-driven via TierQualityMapping)
ql = get_quality_level_for_tier("pro")          # → QualityLevel.BALANCED (5)
ql = get_quality_level_for_tier("premium")      # → QualityLevel.PREMIUM  (8)
ql = get_quality_level_for_tier("freemium")     # → QualityLevel.ECONOMY  (2)

result = sync_completion(
    "story_writing",
    messages,
    quality_level=ql,
    priority="quality",
)

Multi-Tenant Usage Tracking (v0.5.0)

result = sync_completion(
    "story_writing",
    messages,
    tenant_id=user.organization_id,
    object_id=f"story-{story.pk}",
    metadata={"source": "web", "plan": "pro"},
)

Privacy-by-Design Logging (issue #8)

AIUsageLog can pseudonymise or anonymise itself at write time — PII never reaches the database. Select the policy with one Django setting (DSGVO Art. 25 / Art. 5 Abs. 1 lit. c):

# settings.py
from aifw.constants import PrivacyMode
AIFW_PRIVACY_MODE = PrivacyMode.PSEUDONYMOUS   # "full" (default) | "pseudonymous" | "anonymous"
AIFW_PRIVACY_HMAC_SECRET = env("AIFW_PRIVACY_HMAC_SECRET")  # optional; falls back to SECRET_KEY
# Call site stays the same — the hook transforms the row before it is written:
sync_completion(
    "nl2sql", messages,
    user=request.user,
    tenant_id=user.organization_id,
    metadata={"nl_question": question},
)
Mode user metadata
full (default) stored raw (FK) stored raw
pseudonymous NULL user_hash = HMAC(user.pk); nl_questiontopic
anonymous NULL reduced to {"day_bucket": "<ISO date>"} (tenant + tokens survive)

The privacy_mode column records which policy was applied to each row.

Topic classification is a plain callable — aifw never imports iil-promptfw. The built-in pseudonymous classifier emits a coarse placeholder ("unclassified"); wire a real classifier via a custom hook:

# myapp/privacy.py
from aifw.privacy import PseudonymousHook

def my_hook():
    from promptfw import classify_topic
    return PseudonymousHook(topic_classifier=classify_topic)

# settings.py
AIFW_PRIVACY_HOOK = "myapp.privacy:my_hook"   # dotted path → PrivacyHook / subclass / factory

A custom hook receives the create-payload dict (keys: user, tenant_id, metadata, …) and returns it rewritten. If a configured non-full hook raises, logging fails closeduser/metadata are scrubbed rather than leaking PII.

For supervisor-facing reports, AIUsageLog.objects.aggregate_with_k_anonymity( *group_by, k=3) returns only buckets with ≥ k entries (suppresses re-identifiable small groups).

Default-shift roadmap: the default stays "full" in the 0.11.x line to keep existing audit views (bfagent, weltenhub, travel-beat) working unchanged. A shift to "pseudonymous" as the default is planned for 0.12.0 as a documented breaking change.

Streaming (v0.4.0)

from aifw.service import sync_completion_stream
from django.http import StreamingHttpResponse

def stream_story(request):
    def generate():
        for chunk in sync_completion_stream("story_writing", messages):
            yield chunk
    return StreamingHttpResponse(generate(), content_type="text/plain")

Django Models

Model Purpose
LLMProvider Provider config (API key env var, base URL)
LLMModel Model config (max tokens, cost per million tokens)
AIActionType Action → model mapping; supports quality_level + priority dimensions
AIUsageLog Token/latency/cost tracking per request, with tenant_id + metadata + privacy_mode
TierQualityMapping Subscription tier → quality_level mapping (DB-driven)

NL2SQL (v0.7.0+)

# settings.py
INSTALLED_APPS = [
    ...
    "aifw",
    "aifw.nl2sql",   # activates NL2SQL models + migrations
]

python manage.py migrate aifw_nl2sql
python manage.py seed_nl2sql_examples   # seed verified few-shot examples
from aifw.nl2sql import NL2SQLEngine

engine = NL2SQLEngine(schema_xml="<schema>...</schema>")
result = engine.query("How many open orders do we have?")

if result.success:
    print(result.sql)
    print(result.rows)
  • Few-shot examples injected automatically from NL2SQLExample model
  • Self-healing retry — on SQL execution error, a second LLM call includes the error as context
  • Feedback captureNL2SQLFeedback auto-created on every execution error
  • Management commands: seed_nl2sql_examples, promote_feedback, validate_schema

Management Commands

python manage.py init_aifw_config       # seed default providers & models
python manage.py check_aifw_config      # CI gate — verify all action codes have a catch-all row
python manage.py seed_nl2sql_examples   # seed NL2SQL few-shot examples
python manage.py promote_feedback       # promote corrected feedback → NL2SQLExample
python manage.py validate_schema        # validate schema-XML against real DB (exit 1 on error)

Cache Tuning

# Environment variables (optional)
AIFW_LOCAL_CACHE_TTL=30     # process-local TTL in seconds (default: 30)
AIFW_CACHE_TTL=600          # shared cache TTL in seconds  (default: 600)
from aifw.service import invalidate_action_cache, invalidate_tier_cache

invalidate_action_cache("story_writing")   # clear one action's cache entries
invalidate_action_cache()                  # clear all
invalidate_tier_cache("pro")               # clear one tier

Constants

from aifw.constants import QualityLevel

QualityLevel.ECONOMY   # 2
QualityLevel.BALANCED  # 5
QualityLevel.PREMIUM   # 8
QualityLevel.ALL       # (2, 5, 8)

Configuration

Django Settings

INSTALLED_APPS = [
    "aifw",           # core: LLMProvider, LLMModel, AIActionType, AIUsageLog, TierQualityMapping
    "aifw.nl2sql",    # optional: NL2SQL models + migrations (requires `nl2sql` extra)
]

Run once after installation to seed default providers and models:

python manage.py migrate aifw
python manage.py init_aifw_config

Environment Variables (optional)

Variable Default Purpose
AIFW_LOCAL_CACHE_TTL 30 Process-local cache TTL in seconds
AIFW_CACHE_TTL 600 Shared cache TTL in seconds (Redis-backed)
AIFW_PRIVACY_MODE full Privacy policy for AIUsageLog: full | pseudonymous | anonymous (issue #8)
AIFW_PRIVACY_HOOK Dotted path to a custom PrivacyHook (overrides AIFW_PRIVACY_MODE)
AIFW_PRIVACY_HMAC_SECRET SECRET_KEY HMAC key for pseudonymous user_hash

AIFW_PRIVACY_* are read from Django settings (not env), like Django's own SECRET_KEY. The cache TTLs above are read from the process environment.

No Django settings dict is required — all configuration is managed via Django Admin models.

Changelog

See CHANGELOG.md.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

iil_aifw-0.11.5.tar.gz (90.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

iil_aifw-0.11.5-py3-none-any.whl (77.8 kB view details)

Uploaded Python 3

File details

Details for the file iil_aifw-0.11.5.tar.gz.

File metadata

  • Download URL: iil_aifw-0.11.5.tar.gz
  • Upload date:
  • Size: 90.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for iil_aifw-0.11.5.tar.gz
Algorithm Hash digest
SHA256 7902f91404e791ae65c78297c1047be77d94a89bfe46b1e69ce7adbfa4a0ac66
MD5 4804fb6eb0ba6d955daef03e5da50e33
BLAKE2b-256 9179fee1ceca39bd8222169affb179fea922edae141f9efd752d902cd978b565

See more details on using hashes here.

Provenance

The following attestation bundles were made for iil_aifw-0.11.5.tar.gz:

Publisher: publish.yml on achimdehnert/aifw

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file iil_aifw-0.11.5-py3-none-any.whl.

File metadata

  • Download URL: iil_aifw-0.11.5-py3-none-any.whl
  • Upload date:
  • Size: 77.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for iil_aifw-0.11.5-py3-none-any.whl
Algorithm Hash digest
SHA256 cb7ac08b72bcd5f1632a982c869a24262879445d560af16eca81a47056f9a45a
MD5 7160972e2c88fa985d3ed3b85a0c705b
BLAKE2b-256 66134b8574a8116ba12ac3e7553796d37215fa66b760c3bad298bf4ab4ebe794

See more details on using hashes here.

Provenance

The following attestation bundles were made for iil_aifw-0.11.5-py3-none-any.whl:

Publisher: publish.yml on achimdehnert/aifw

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page