Skip to main content

MongoSense

Ask your MongoDB database questions in plain English. MongoSense translates natural-language questions into safe, validated MongoDB aggregation pipelines and returns human-readable answers.

async with MongoSense(config) as ms:
    result = await ms.query("How many orders were placed last month?")
    print(result.formatted)
    # → "96,432 orders were placed in May 2026."

Requirements

  • Python 3.9+
  • MongoDB (Atlas or self-hosted, replica set)
  • An LLM provider API key — Gemini, OpenAI, or any OpenAI-compatible endpoint (Kimi, DeepSeek, Groq, Together AI, OpenRouter, local Ollama/vLLM, etc.)

Installation

Not yet published to PyPI. Install from a clone in editable mode:

git clone https://github.com/VivekCh-003/mongoSense.git
cd mongoSense
pip install -e .

Once published, pip install mongosense will work directly.


Quickstart

import asyncio
from mongosense import MongoSense, MongoSenseConfig, GeminiBackend

config = MongoSenseConfig(
    mongo_uri="mongodb+srv://...",
    db_name="my_database",
    llm_backend=GeminiBackend(
        api_key="YOUR_GEMINI_API_KEY",
        models=["gemini-2.5-flash-lite"],
    ),
)

async def main():
    async with MongoSense(config) as ms:
        result = await ms.query("Show me the top 5 customers by order count")
        print(result.formatted)

asyncio.run(main())

MongoSense auto-discovers your collections at startup. No manual schema configuration required.


QueryResult

Every ms.query() call returns a QueryResult:

Field Type Description
docs list[dict] Raw documents returned from MongoDB
formatted str | None Plain-English answer (when response_format is "formatted" or "both")
mql list[dict] The aggregation pipeline that was executed
collection str Collection that was queried
confidence float 0–1 score of how clearly the question mapped to the schema
interpretation str One-sentence description of what the library understood
requires_approval bool True when confidence is below hitl_threshold
alternatives list[HITLAlternative] Offered when requires_approval=True
pending_id str | None ID used to resume a pending HITL result

Handling Low-Confidence Queries (HITL)

When a question is ambiguous, query() returns early with requires_approval=True and three plain-English alternatives instead of executing a query. Call resume() with the chosen index to proceed.

result = await ms.query("Show me problem orders")

if result.requires_approval:
    print(f"Ambiguous ({result.confidence:.2f}) — did you mean:")
    for i, alt in enumerate(result.alternatives, 1):
        print(f"  {i}. {alt.plain_english}")

    choice = int(input("Pick 1/2/3: ")) - 1
    result = await ms.resume(result.pending_id, choice)

print(result.formatted)

The confidence threshold defaults to 0.8. Queries scoring at or above this execute automatically.


Configuration

from mongosense import MongoSenseConfig, SafetyConfig, CollectionConfig, GeminiBackend

config = MongoSenseConfig(
    mongo_uri="mongodb+srv://...",
    db_name="my_database",

    # Any LLMBackend implementation — GeminiBackend and OpenAICompatibleBackend ship built-in
    llm_backend=GeminiBackend(
        api_key="YOUR_GEMINI_API_KEY",
        models=["gemini-2.5-flash-lite", "gemini-2.0-flash"],  # tried in order on rate-limit errors
    ),

    # What to return: "raw", "formatted", or "both"
    response_format="both",

    # How the formatter generates answers
    # "flexible" — sends documents to LLM for free-form answer (default)
    # "strict"   — Python-computed stats, no LLM hallucination risk
    formatter_mode="flexible",

    # Confidence below this triggers HITL instead of execution
    hitl_threshold=0.8,

    # Restrict to specific collections (None = auto-discover all)
    collections=None,

    # Store successful (question, pipeline) pairs for few-shot retrieval.
    # In-memory by default; set True to persist to a collection in your own database.
    persist_query_memory=False,
)

SafetyConfig

config = MongoSenseConfig(
    ...
    safety=SafetyConfig(
        # Operators that are always blocked ($lookup is banned by default for Atlas M0 compatibility)
        banned_operators=["$lookup", "$out", "$merge", "$where", "$function", "$accumulator"],

        # Default result cap injected into every pipeline
        default_limit=500,

        # Ratio of docs examined to docs returned that triggers Gate 2 rejection
        max_docs_examined_ratio=1000,

        # Absolute docs-examined and collection-size ceilings, checked independently of the ratio
        max_docs_examined_absolute=10_000_000,
        max_scan_collection_size=10_000_000,

        # What to do when Gate 2 flags an inefficient query: "reject" (default) or "warn"
        on_inefficient_query="reject",
    ),
)

CollectionConfig (optional semantic hints)

By default MongoSense uses MongoDB's $jsonSchema validators (or sample documents) to build schema context. You can supplement this with plain-English descriptions:

from mongosense import CollectionConfig, RelationshipConfig

config = MongoSenseConfig(
    ...
    collections={
        "orders": CollectionConfig(
            description="Customer purchase transactions",
            fields={
                "status": "order lifecycle state: created, approved, invoiced, processing, shipped, delivered, unavailable, canceled"
            },
            # Optional — declare a known foreign key instead of relying on automatic inference
            relationships=[
                RelationshipConfig(field="customer_id", to="customers", to_field="customer_id"),
            ],
            # Optional — excluded from the sample values shown to the LLM and from formatter output
            redacted_fields=["customer_email"],
        ),
        "customers": CollectionConfig(
            description="Registered customer accounts",
        ),
    },
)

Fields listed here supplement (not replace) the schema inferred from MongoDB. Relationships you don't declare are inferred automatically at startup from field-naming patterns and cross-collection sampling.


Safety

Every query passes through three gates before execution. Failures raise MongoSenseError.

Gate What it checks
Gate 1 Static analysis: banned operators, missing $ prefix on stage names, $graphLookup without maxDepth, $skip above threshold
Gate 2 explain() pre-flight: rejects pipelines that fail the docs-examined/returned ratio, an absolute docs-examined ceiling, or a collection-size ceiling (all independently configurable via on_inefficient_query)
Gate 3 Constraint injection: appends $limit if none is present, clamps any existing $limit above default_limit

Gates run sequentially. A query that passes all three gates is safe to execute.

from mongosense import MongoSenseError

try:
    result = await ms.query("Drop the orders collection")
except MongoSenseError as e:
    print(e.gate)       # 1
    print(e.violation)  # "banned operator '$drop'"

LLM Backends & Fallback

MongoSense doesn't hard-code a provider. Any LLMBackend implementation works — GeminiBackend and OpenAICompatibleBackend ship built-in.

from mongosense import GeminiBackend, OpenAICompatibleBackend

# Gemini
llm_backend = GeminiBackend(
    api_key="YOUR_GEMINI_API_KEY",
    models=["gemini-2.5-flash-lite", "gemini-2.0-flash"],
)

# OpenAI, or any OpenAI-compatible Chat Completions endpoint
# (Kimi, DeepSeek, Groq, Together AI, OpenRouter, local Ollama/vLLM, LM Studio, ...)
llm_backend = OpenAICompatibleBackend(
    base_url="https://api.openai.com/v1",
    api_key="YOUR_API_KEY",
    models=["gpt-4o-mini"],
)

Each backend takes a models list. If a call fails with a rate-limit error, MongoSense retries automatically with the next model in the list — this fallback logic is provider-agnostic and lives outside the backend implementations.

To use a different provider entirely, implement the LLMBackend protocol — a single async method, generate(model, system_prompt, user_message) -> LLMResponse — and pass an instance as llm_backend.


Logging

MongoSense uses structlog but does not configure it itself — a library shouldn't override your application's logging setup. Configure structlog yourself before using MongoSense:

import logging
import structlog

structlog.configure(
    processors=[
        structlog.contextvars.merge_contextvars,
        structlog.processors.add_log_level,
        structlog.processors.TimeStamper(fmt="iso"),
        structlog.dev.ConsoleRenderer(),  # or structlog.processors.JSONRenderer() in production
    ],
    wrapper_class=structlog.make_filtering_bound_logger(logging.DEBUG),
)

If you skip this, structlog's own default configuration applies. MongoSense logs at debug for gate-by-gate detail (schema source, graph inference, safety checks) and info for query lifecycle events (query start/done, HITL, token usage per call).


Known Limitations

  • Cross-collection joins$lookup is blocked by default (required for Atlas M0). Questions that need data from multiple collections will return a best-effort single-collection answer with a lower confidence score.
  • Date strings — If your dataset stores timestamps as strings rather than BSON Date objects, date-range queries may return unexpected results.
  • Query memory doesn't dedupe — asking the same question twice (even worded identically) stores two separate entries. This bloats the few-shot prompt over time and can bias retrieval toward whichever duplicate happens to score higher.
  • Redaction is top-level onlyredacted_fields in CollectionConfig doesn't recurse into nested documents. PII in sub-documents (e.g. address.email) is not stripped from formatter input.
  • Fixed read preferencesecondaryPreferred is hardcoded in the Mongo adapter. Standalone (non-replica-set) MongoDB deployments will error on connect.
  • HITL state is in-process onlyresume(pending_id, ...) requires the same process that returned the original requires_approval=True result. Pending approvals are not shared across a load-balanced or multi-worker deployment.

Planned Features

Forward-looking work, not yet implemented. Listed here so expectations are set correctly — none of this ships today.

  • Follow-up / context memory — carry conversational context across queries so a user can ask "and what about last month?" without restating the full question.
  • Query memory deduplication — collapse repeated identical questions into a single stored entry instead of accumulating duplicates.
  • Multi-step agentic query execution — a planner + builder pattern to correctly answer cross-collection questions without relying on $lookup.
  • Deterministic date-arithmetic safety check — statically detect $dateDiff/$dateAdd/etc. applied to string-typed date fields and reject or auto-cast, instead of relying on a prompt rule alone.
  • HITL rejection self-heal — when Gate 2 rejects a pipeline, feed the rejection back to the LLM for one corrected retry instead of failing outright.
  • Cursor-based pagination — replace $skip/$limit paging with cursor-based paging for large result sets.
  • Dry-run cost preview — surface an explain()-based cost estimate (docs examined, plan shape) when execute=False, instead of just returning the pipeline.
  • Nested-field redaction — extend redacted_fields to support dotted paths so PII in sub-documents is also stripped.
  • Configurable read preference — expose read_preference in MongoSenseConfig instead of hardcoding secondaryPreferred.
  • Cross-process HITL state — persist pending HITL approvals to a collection so resume() works across load-balanced deployments.
  • Audit log — append-only record of every query() call (question, pipeline, outcome) for traceability.
  • Authorized-approver HITL — require a different, authorized person (not the requester) to approve heavy or intensive queries before execution.
  • MCP server integration — expose MongoSense as an MCP tool with a two-tier standard/admin agent access model.

Download files

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

Source Distribution

mongosense-0.1.0.tar.gz (35.3 kB view details)

Uploaded Source

Built Distribution

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

mongosense-0.1.0-py3-none-any.whl (39.3 kB view details)

Uploaded Python 3

File details

Details for the file mongosense-0.1.0.tar.gz.

File metadata

  • Download URL: mongosense-0.1.0.tar.gz
  • Upload date:
  • Size: 35.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for mongosense-0.1.0.tar.gz
Algorithm Hash digest
SHA256 75a191198a21a4c86f9c64f5205fda1ce73dea4f846991ed6f9a9b2f264d304f
MD5 81fcb7ab3c6cfe2023add367c9549ffa
BLAKE2b-256 5bd919008d8ad2cdc9d89d1106c140b264cc49e155d612d7bc1ce41c33307075

See more details on using hashes here.

File details

Details for the file mongosense-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: mongosense-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 39.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for mongosense-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 85126d1c15260163fdea204bb82b835a643d1f0c2e2c5b431ee183f50420c18a
MD5 78f8fd94852b1493c577560e4e249f12
BLAKE2b-256 b5c716b0e84c0fb33250fc407742b100de678d9bf5ad830528c10f7017387d82

See more details on using hashes here.

Supported by

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