97e91a83f3
Ruff / Ruff (push) Waiting to run
Test / Core Tests (push) Waiting to run
Test / Offline Coverage Tests (Python 3.10) (push) Waiting to run
Test / Offline Coverage Tests (Python 3.11) (push) Waiting to run
Test / Offline Coverage Tests (Python 3.12) (push) Waiting to run
Test / Offline Coverage Tests (Python 3.13) (push) Waiting to run
Test / Offline Coverage Tests (Python 3.9) (push) Waiting to run
Test / Full Coverage (Python 3.11) (push) Waiting to run
Test / Core Provider Tests (OpenAI) (push) Blocked by required conditions
Test / Core Provider Tests (Anthropic) (push) Blocked by required conditions
Test / Core Provider Tests (Google) (push) Blocked by required conditions
Test / Core Provider Tests (Other) (push) Blocked by required conditions
Test / Anthropic Tests (push) Blocked by required conditions
Test / Gemini Tests (push) Blocked by required conditions
Test / Google GenAI Tests (push) Blocked by required conditions
Test / Vertex AI Tests (push) Blocked by required conditions
Test / OpenAI Tests (push) Blocked by required conditions
Test / Writer Tests (push) Blocked by required conditions
Test / Auto Client Tests (push) Blocked by required conditions
ty / type-check (push) Waiting to run
112 lines
4.3 KiB
Markdown
112 lines
4.3 KiB
Markdown
# Refactor OpenAISchema class methods to standalone functions
|
|
|
|
## Summary
|
|
|
|
Currently, schema generation for different LLM providers requires models to inherit from `OpenAISchema` or be wrapped with the `@openai_schema` decorator. This creates an unnecessary inheritance requirement and couples schema generation to class-based patterns.
|
|
|
|
We should refactor the schema generation logic into standalone, provider-agnostic functions.
|
|
|
|
## Current State Analysis
|
|
|
|
**Current usage pattern**: `response_model.openai_schema` (where response_model inherits from OpenAISchema)
|
|
|
|
**Affected files with usage counts**:
|
|
- `instructor/utils/` (12 calls across cerebras.py, writer.py, fireworks.py, openai.py, mistral.py)
|
|
- `instructor/process_response.py` (11 calls)
|
|
- `instructor/dsl/parallel.py` (3 calls - handles parallel tools)
|
|
- `instructor/distil.py` (1 call)
|
|
- `instructor/function_calls.py` (13 calls - method definitions and internal usage)
|
|
- `instructor/utils/core.py` (1 call - decorator application)
|
|
- `instructor/utils/anthropic.py` (1 call - anthropic_schema)
|
|
- `instructor/utils/google.py` (1 call - gemini_schema)
|
|
- Examples and tests (20+ calls)
|
|
|
|
**Total**: ~60 usages across codebase
|
|
|
|
## Proposed Solution
|
|
|
|
### 1. Create `instructor/schema_utils.py` with standalone functions:
|
|
|
|
```python
|
|
from __future__ import annotations
|
|
import functools
|
|
from typing import Any, Type
|
|
from docstring_parser import parse
|
|
from pydantic import BaseModel
|
|
|
|
@functools.lru_cache(maxsize=256)
|
|
def generate_openai_schema(model: Type[BaseModel]) -> dict[str, Any]:
|
|
"""Generate OpenAI function schema from Pydantic model."""
|
|
# Move logic from OpenAISchema.openai_schema here
|
|
|
|
def generate_anthropic_schema(model: Type[BaseModel]) -> dict[str, Any]:
|
|
"""Generate Anthropic tool schema from Pydantic model."""
|
|
# Move logic from OpenAISchema.anthropic_schema here
|
|
|
|
def generate_gemini_schema(model: Type[BaseModel]) -> Any:
|
|
"""Generate Gemini function schema from Pydantic model."""
|
|
# Move logic from OpenAISchema.gemini_schema here
|
|
```
|
|
|
|
### 2. Update OpenAISchema class to delegate to new functions:
|
|
|
|
```python
|
|
class OpenAISchema(BaseModel):
|
|
@classproperty
|
|
def openai_schema(cls):
|
|
return generate_openai_schema(cls)
|
|
|
|
@classproperty
|
|
def anthropic_schema(cls):
|
|
return generate_anthropic_schema(cls)
|
|
|
|
@classproperty
|
|
def gemini_schema(cls):
|
|
return generate_gemini_schema(cls)
|
|
```
|
|
|
|
### 3. Migration path:
|
|
|
|
**Phase 1**: Add new functions, maintain backward compatibility
|
|
- All existing `response_model.openai_schema` calls continue working
|
|
- New code can use `generate_openai_schema(response_model)` directly
|
|
|
|
**Phase 2**: Internal migration
|
|
- Replace internal usage in utils/ and process_response.py
|
|
- Update parallel tools handling in dsl/parallel.py
|
|
|
|
**Phase 3**: Deprecation
|
|
- Mark `@openai_schema` decorator as deprecated
|
|
- Encourage users to migrate to standalone functions
|
|
|
|
## Benefits
|
|
|
|
1. **No inheritance requirement** - Any Pydantic model can generate schemas
|
|
2. **Provider-agnostic** - Clean separation of schema generation logic
|
|
3. **Better testability** - Functions are easier to unit test
|
|
4. **Performance** - LRU cache maintains current performance characteristics
|
|
5. **Backward compatibility** - Zero breaking changes during transition
|
|
6. **Cleaner API** - More functional approach vs class-based inheritance
|
|
|
|
## Implementation Checklist
|
|
|
|
- [ ] Create `instructor/schema_utils.py` with standalone functions
|
|
- [ ] Update `OpenAISchema` class to delegate to new functions
|
|
- [ ] Add comprehensive tests comparing old vs new output
|
|
- [ ] Update internal usage in utils/ (12 locations)
|
|
- [ ] Update process_response.py (11 locations)
|
|
- [ ] Update parallel tools handling in dsl/parallel.py
|
|
- [ ] Update distil.py usage
|
|
- [ ] Mark decorator as deprecated with warning
|
|
- [ ] Update documentation and examples
|
|
- [ ] Run full test suite to ensure no regressions
|
|
|
|
## Special Considerations
|
|
|
|
- **Parallel tools**: `dsl/parallel.py` uses both `openai_schema(model).openai_schema` and `openai_schema(model).anthropic_schema` patterns
|
|
- **Caching**: Current `@classproperty` provides implicit memoization - maintain with `@lru_cache`
|
|
- **Error handling**: Preserve current validation and error behavior
|
|
- **Provider compatibility**: Ensure schema output remains identical for all providers
|
|
|
|
This refactoring will modernize the schema generation approach while maintaining full backward compatibility.
|