Skip to content

Commit 01dd834

Browse files
committed
feat(v2): implement core infrastructure (registry, handlers, protocols)
1 parent 1801410 commit 01dd834

File tree

9 files changed

+1070
-0
lines changed

9 files changed

+1070
-0
lines changed

instructor/v2/__init__.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"""Instructor v2 - Mode registry system.
2+
3+
v2 uses a registry system that maps Mode enum values directly to handlers.
4+
This replaces the hardcoded dictionaries in v1 with a dynamic, extensible system.
5+
6+
Usage:
7+
from instructor import Mode
8+
from instructor.v2 import from_anthropic
9+
10+
client = from_anthropic(anthropic_client, mode=Mode.ANTHROPIC_TOOLS)
11+
12+
Benefits:
13+
- Dynamic registration: Modes register themselves via decorators
14+
- Lazy loading: Handlers loaded only when used
15+
- Extensible: Easy to add new modes without modifying core
16+
- Type-safe: Protocols ensure handler compatibility
17+
"""
18+
19+
from instructor import Mode, Provider
20+
from instructor.v2.core.handler import ModeHandler
21+
from instructor.v2.core.protocols import ReaskHandler, RequestHandler, ResponseParser
22+
from instructor.v2.core.registry import ModeHandlers, ModeRegistry, mode_registry
23+
24+
# Import providers (will auto-register modes)
25+
try:
26+
from instructor.v2.providers.anthropic import from_anthropic
27+
except ImportError:
28+
from_anthropic = None # type: ignore
29+
30+
__all__ = [
31+
# Core types
32+
"Provider",
33+
"Mode",
34+
# Registry
35+
"mode_registry",
36+
"ModeRegistry",
37+
"ModeHandlers",
38+
# Handler base class
39+
"ModeHandler",
40+
# Protocols
41+
"RequestHandler",
42+
"ReaskHandler",
43+
"ResponseParser",
44+
# Providers
45+
"from_anthropic",
46+
]

instructor/v2/core/__init__.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""Core v2 infrastructure - registry, protocols, and mode types."""
2+
3+
from instructor import Mode, Provider
4+
from instructor.v2.core.protocols import ReaskHandler, RequestHandler, ResponseParser
5+
from instructor.v2.core.registry import ModeHandlers, ModeRegistry, mode_registry
6+
7+
__all__ = [
8+
"Provider",
9+
"Mode",
10+
"mode_registry",
11+
"ModeRegistry",
12+
"ModeHandlers",
13+
"RequestHandler",
14+
"ReaskHandler",
15+
"ResponseParser",
16+
]

instructor/v2/core/decorators.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"""Decorator utilities for v2 mode registration."""
2+
3+
from instructor import Mode, Provider
4+
from instructor.v2.core.registry import mode_registry
5+
6+
7+
def register_mode_handler(
8+
provider: Provider,
9+
mode: Mode,
10+
):
11+
"""Decorator to register a mode handler class.
12+
13+
The decorated class must implement RequestHandler, ReaskHandler,
14+
and ResponseParser protocols via prepare_request, handle_reask,
15+
and parse_response methods.
16+
17+
Args:
18+
provider: Provider enum value (for tracking)
19+
mode: Mode enum value
20+
21+
Returns:
22+
Decorator function
23+
24+
Example:
25+
>>> from instructor import Mode
26+
>>> @register_mode_handler(Provider.ANTHROPIC, Mode.ANTHROPIC_TOOLS)
27+
... class AnthropicToolsHandler:
28+
... def prepare_request(self, response_model, kwargs):
29+
... return response_model, kwargs
30+
... def handle_reask(self, kwargs, response, exception):
31+
... return kwargs
32+
... def parse_response(self, response, response_model, **kwargs):
33+
... return response_model.model_validate(response)
34+
"""
35+
36+
def decorator(handler_class: type) -> type:
37+
"""Register the handler class."""
38+
# Instantiate the handler
39+
handler = handler_class()
40+
41+
# Register with the registry
42+
mode_registry.register(
43+
mode=mode,
44+
provider=provider,
45+
request_handler=handler.prepare_request,
46+
reask_handler=handler.handle_reask,
47+
response_parser=handler.parse_response,
48+
)
49+
50+
return handler_class
51+
52+
return decorator

instructor/v2/core/handler.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
"""Base handler class for v2 mode handlers.
2+
3+
Provides the common interface and default implementations for mode handlers.
4+
"""
5+
6+
from abc import ABC, abstractmethod
7+
from typing import Any
8+
9+
from pydantic import BaseModel
10+
11+
12+
class ModeHandler(ABC):
13+
"""Base class for mode handlers.
14+
15+
Subclasses must implement prepare_request, handle_reask, and parse_response.
16+
These methods define how requests are prepared, errors are handled, and
17+
responses are parsed for a specific mode.
18+
"""
19+
20+
@abstractmethod
21+
def prepare_request(
22+
self,
23+
response_model: type[BaseModel] | None,
24+
kwargs: dict[str, Any],
25+
) -> tuple[type[BaseModel] | None, dict[str, Any]]:
26+
"""Prepare request kwargs for this mode.
27+
28+
Args:
29+
response_model: Pydantic model to extract (or None for unstructured)
30+
kwargs: Original request kwargs from user
31+
32+
Returns:
33+
Tuple of (possibly modified response_model, modified kwargs)
34+
35+
Example:
36+
For TOOLS mode, this adds "tools" and "tool_choice" to kwargs.
37+
For JSON mode, this adds JSON schema to system message.
38+
"""
39+
...
40+
41+
@abstractmethod
42+
def handle_reask(
43+
self,
44+
kwargs: dict[str, Any],
45+
response: Any,
46+
exception: Exception,
47+
) -> dict[str, Any]:
48+
"""Handle validation failure and prepare retry request.
49+
50+
Args:
51+
kwargs: Original request kwargs
52+
response: Failed API response
53+
exception: Validation exception that occurred
54+
55+
Returns:
56+
Modified kwargs for retry attempt
57+
58+
Example:
59+
For TOOLS mode, appends tool_result with error message.
60+
For JSON mode, appends user message with validation error.
61+
"""
62+
...
63+
64+
@abstractmethod
65+
def parse_response(
66+
self,
67+
response: Any,
68+
response_model: type[BaseModel],
69+
validation_context: dict[str, Any] | None = None,
70+
strict: bool | None = None,
71+
) -> BaseModel:
72+
"""Parse API response into validated Pydantic model.
73+
74+
Args:
75+
response: Raw API response
76+
response_model: Pydantic model to validate against
77+
validation_context: Optional context for Pydantic validation
78+
strict: Optional strict validation mode
79+
80+
Returns:
81+
Validated Pydantic model instance
82+
83+
Raises:
84+
ValidationError: If response doesn't match model schema
85+
86+
Example:
87+
For TOOLS mode, extracts tool_use blocks and validates.
88+
For JSON mode, extracts JSON from text blocks and validates.
89+
"""
90+
...
91+
92+
def __repr__(self) -> str:
93+
"""String representation of handler."""
94+
return f"<{self.__class__.__name__}>"

0 commit comments

Comments
 (0)