Skip to content

feat: schema complexity analyzer + automatic mode selection#2394

Open
mimran-khan wants to merge 1 commit into
567-labs:mainfrom
mimran-khan:feat/schema-analyzer-and-auto-mode
Open

feat: schema complexity analyzer + automatic mode selection#2394
mimran-khan wants to merge 1 commit into
567-labs:mainfrom
mimran-khan:feat/schema-analyzer-and-auto-mode

Conversation

@mimran-khan

@mimran-khan mimran-khan commented Jun 24, 2026

Copy link
Copy Markdown

What this does

I noticed that picking the right mode is a common friction point, especially for users with complex nested schemas. This PR adds two utilities that solve this at the library level:

analyze_schema(response_model) - inspects a Pydantic model before any API call and reports:

  • Complexity score (0-100)
  • Depth, field count, recursion detection
  • Large enum warnings
  • Actionable suggestions for schema restructuring

select_mode(response_model, provider) - picks the optimal extraction mode by cross-referencing schema complexity against provider capabilities:

  • Simple schemas -> TOOLS (leverages native function calling)
  • Deep schemas -> JSON_SCHEMA (gives the LLM explicit structure)
  • Recursive schemas -> MD_JSON/JSON (avoids tool-schema limitations)
  • Always respects what the provider actually supports

Why

I ran into this myself while testing extraction on deeply nested models. The retry loop burned tokens because the schema was too complex for TOOLS mode, but there was no guidance about that. Checking the issues list, I found similar confusion across multiple discussions.

These utilities are pure functions with no side effects, no network calls, and zero additional dependencies. They are lazy-loaded so they don't affect import performance.

Files changed

  • instructor/v2/core/schema_analyzer.py - the complexity analysis engine
  • instructor/v2/core/auto_mode.py - mode selection logic using provider specs
  • instructor/__init__.py - top-level exports (analyze_schema, SchemaAnalysis, select_mode)
  • instructor/v2/core/__init__.py - v2 core exports
  • tests/test_schema_analyzer.py - 28 tests for the analyzer
  • tests/test_auto_mode.py - 31 tests for mode selection

Usage

import instructor
from pydantic import BaseModel

class Invoice(BaseModel):
    items: list["LineItem"]
    # ... deeply nested

# Check complexity before calling
analysis = instructor.analyze_schema(Invoice)
print(f"Complexity: {analysis.complexity_score}/100")
for finding in analysis.findings:
    print(f"  [{finding.severity.value}] {finding.message}")

# Auto-select the best mode
mode = instructor.select_mode(Invoice, instructor.Provider.OPENAI)
client = instructor.from_openai(openai.OpenAI(), mode=mode)

Test results

All 59 new tests pass. Broader suite (1537 tests) has no regressions.

Closes #2393

Checklist before requesting a review

  • I have performed a self-review of my code
  • If it is a core feature, I have added thorough tests.
  • If it is a core feature, I have added documentation.

@jxnl

jxnl commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

Reviewed for merge readiness. I would keep this open for #2393, but not merge it yet: it adds new public API (analyze_schema, select_mode) and hard-coded provider/mode heuristics, so this needs maintainer design approval rather than being treated as a small fix. A couple of things to tighten before review: document whether select_mode is advisory only or intended to become part of from_provider, and clean up the tests so model definitions do not rely on forward references before their classes exist / duplicate class definitions. CI should run on the refreshed branch before this gets reviewed for API shape.

Introduce two complementary utilities that help users get better extraction
results on the first attempt:

1. `analyze_schema(response_model)` inspects a Pydantic model's JSON schema
   and reports complexity metrics (depth, field count, recursion, large enums)
   along with actionable findings and a 0-100 complexity score.

2. `select_mode(response_model, provider)` picks the optimal extraction mode
   for a given provider by cross-referencing schema complexity with provider
   capabilities. Handles recursive schemas, deep nesting, and high-complexity
   patterns by routing to the mode most likely to succeed.

Both are exposed at `instructor.analyze_schema` and `instructor.select_mode`
for convenient top-level access.
@mimran-khan mimran-khan force-pushed the feat/schema-analyzer-and-auto-mode branch from 8972db1 to bc55f4f Compare June 29, 2026 14:28
@mimran-khan

Copy link
Copy Markdown
Author

Thanks for the feedback. I've addressed both points:

  1. Advisory-only documentation: Updated the module docstring and select_mode docstring to explicitly state this is advisory-only. It does not get called internally by from_provider or any client factory. Users opt in by passing the returned mode manually. The intent is a starting point for users unsure which mode fits their schema, not an automatic override.

  2. Test cleanup: Reordered all model definitions in both test files so dependencies are defined before usage (no forward references). Removed the duplicate ModelWithLargeEnum class that existed in test_schema_analyzer.py.

Branch is force-pushed so CI should pick it up fresh.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: schema complexity pre-flight analysis and automatic mode selection

2 participants