From cac233ab43bb1ee09e8d04d2e01882186f0b57a1 Mon Sep 17 00:00:00 2001 From: Jason Liu Date: Fri, 27 Jun 2025 11:12:25 -0700 Subject: [PATCH 1/7] feat: add Azure OpenAI support to auto_client.py (#1633) Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> --- docs/integrations/azure.md | 32 ++++++++++++++++++++++ instructor/auto_client.py | 55 ++++++++++++++++++++++++++++++++++++++ tests/test_auto_client.py | 1 + 3 files changed, 88 insertions(+) diff --git a/docs/integrations/azure.md b/docs/integrations/azure.md index c3742f29d..7d3553f5c 100644 --- a/docs/integrations/azure.md +++ b/docs/integrations/azure.md @@ -51,6 +51,38 @@ client = AzureOpenAI( client = instructor.from_openai(client) ``` +## Using Auto Client (Recommended) + +The easiest way to get started with Azure OpenAI is using the `from_provider` method: + +```python +import instructor +import os + +# Set your Azure OpenAI credentials +os.environ["AZURE_OPENAI_API_KEY"] = "your-api-key" +os.environ["AZURE_OPENAI_ENDPOINT"] = "https://your-resource.openai.azure.com/" + +# Create client using the provider string +client = instructor.from_provider("azure_openai/gpt-4o-mini") + +# Or async client +async_client = instructor.from_provider("azure_openai/gpt-4o-mini", async_client=True) +``` + +You can also pass credentials as parameters: + +```python +import instructor + +client = instructor.from_provider( + "azure_openai/gpt-4o-mini", + api_key="your-api-key", + azure_endpoint="https://your-resource.openai.azure.com/", + api_version="2024-02-01" # Optional, defaults to 2024-02-01 +) +``` + ## Basic Usage Here's a simple example using a Pydantic model: diff --git a/instructor/auto_client.py b/instructor/auto_client.py index 68d3097c8..8552a9868 100644 --- a/instructor/auto_client.py +++ b/instructor/auto_client.py @@ -11,6 +11,7 @@ # List of supported providers supported_providers = [ "openai", + "azure_openai", "anthropic", "google", "mistral", @@ -82,10 +83,12 @@ def from_provider( >>> import instructor >>> # Sync clients >>> client = instructor.from_provider("openai/gpt-4") + >>> client = instructor.from_provider("azure_openai/gpt-4") >>> client = instructor.from_provider("anthropic/claude-3-sonnet") >>> client = instructor.from_provider("ollama/llama2") >>> # Async clients >>> async_client = instructor.from_provider("openai/gpt-4", async_client=True) + >>> async_client = instructor.from_provider("azure_openai/gpt-4", async_client=True) """ try: provider, model_name = model.split("/", 1) @@ -117,6 +120,58 @@ def from_provider( "Install it with `pip install openai`." ) from None + elif provider == "azure_openai": + try: + import os + from openai import AzureOpenAI, AsyncAzureOpenAI + from instructor import from_openai + + # Get required Azure OpenAI configuration from environment + api_key = kwargs.pop("api_key", os.environ.get("AZURE_OPENAI_API_KEY")) + azure_endpoint = kwargs.pop("azure_endpoint", os.environ.get("AZURE_OPENAI_ENDPOINT")) + api_version = kwargs.pop("api_version", "2024-02-01") + + if not api_key: + from instructor.exceptions import ConfigurationError + raise ConfigurationError( + "AZURE_OPENAI_API_KEY is not set. " + "Set it with `export AZURE_OPENAI_API_KEY=` or pass it as kwarg api_key=" + ) + + if not azure_endpoint: + from instructor.exceptions import ConfigurationError + raise ConfigurationError( + "AZURE_OPENAI_ENDPOINT is not set. " + "Set it with `export AZURE_OPENAI_ENDPOINT=` or pass it as kwarg azure_endpoint=" + ) + + client = ( + AsyncAzureOpenAI( + api_key=api_key, + api_version=api_version, + azure_endpoint=azure_endpoint, + ) + if async_client + else AzureOpenAI( + api_key=api_key, + api_version=api_version, + azure_endpoint=azure_endpoint, + ) + ) + return from_openai( + client, + model=model_name, + mode=mode if mode else instructor.Mode.TOOLS, + **kwargs, + ) + except ImportError: + from instructor.exceptions import ConfigurationError + + raise ConfigurationError( + "The openai package is required to use the Azure OpenAI provider. " + "Install it with `pip install openai`." + ) from None + elif provider == "anthropic": try: import anthropic diff --git a/tests/test_auto_client.py b/tests/test_auto_client.py index 4d8300f00..753fab177 100644 --- a/tests/test_auto_client.py +++ b/tests/test_auto_client.py @@ -21,6 +21,7 @@ class User(BaseModel): "anthropic/claude-3-5-haiku-latest", "google/gemini-2.0-flash", "openai/gpt-4o-mini", + "azure_openai/gpt-4o-mini", "mistral/ministral-8b-latest", "cohere/command-r-plus", "perplexity/sonar-pro", From 65190a775f9991276fb41d481e57c872d2980e56 Mon Sep 17 00:00:00 2001 From: Ivan Leo Date: Sat, 28 Jun 2025 02:13:38 +0800 Subject: [PATCH 2/7] fix: expose exception classes in public API (#1613) Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> Co-authored-by: Jason Liu --- pyproject.toml | 13 +- requirements.txt | 303 +++++++++++++++++++++++++++------------ tests/test_exceptions.py | 222 ++++++++++++++++++++++++++++ 3 files changed, 444 insertions(+), 94 deletions(-) create mode 100644 tests/test_exceptions.py diff --git a/pyproject.toml b/pyproject.toml index e0a6e1bf6..82f3479c1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -59,16 +59,17 @@ dev = [ "pytest-examples>=0.0.15", ] docs = [ - "mkdocs<2.0.0,>=1.4.3", + "mkdocs<2.0.0,>=1.6.1", "mkdocs-material[imaging]<10.0.0,>=9.5.9", - "mkdocstrings>=0.26.1,<0.30.0", - "mkdocstrings-python<2.0.0,>=1.11.1", + "mkdocstrings>=0.27.1,<0.30.0", + "mkdocstrings-python<2.0.0,>=1.12.2", "pytest-examples>=0.0.15", "mkdocs-jupyter<0.26.0,>=0.24.6", "mkdocs-rss-plugin<2.0.0,>=1.12.0", "mkdocs-minify-plugin<1.0.0,>=0.8.0", "mkdocs-redirects<2.0.0,>=1.2.1", - "material>=0.1", + "mkdocs-material-extensions>=1.3.1", + "mkdocs-material>=9.6.14", ] test-docs = [ "fastapi<0.116.0,>=0.109.2", @@ -125,6 +126,7 @@ docs = [ "mkdocs-rss-plugin<2.0.0,>=1.12.0", "mkdocs-minify-plugin<1.0.0,>=0.8.0", "mkdocs-redirects<2.0.0,>=1.2.1", + "mkdocs-material-extensions>=1.3.1", "material>=0.1", "cairosvg>=2.7.1", "pillow>=10.4.0", @@ -167,7 +169,8 @@ cohere = ["cohere<6.0.0,>=5.1.8"] cerebras_cloud_sdk = ["cerebras-cloud-sdk<2.0.0,>=1.5.0"] fireworks-ai = ["fireworks-ai<1.0.0,>=0.15.4"] writer = ["writer-sdk<3.0.0,>=2.2.0"] -google-genai=["google-genai>=1.5.0","jsonref<2.0.0,>=1.1.0",] + +google-genai = ["google-genai>=1.5.0","jsonref<2.0.0,>=1.1.0"] examples = [ "cohere>=5.13.4", "datasets>=3.2.0", diff --git a/requirements.txt b/requirements.txt index 9f978b4d0..411fb75a5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,102 +1,227 @@ -# This file was autogenerated by uv via the following command: -# uv pip compile pyproject.toml -o requirements.txt -aiohappyeyeballs==2.6.1 - # via aiohttp -aiohttp==3.12.12 - # via instructor (pyproject.toml) +aiohappyeyeballs==2.4.4 +aiohttp==3.11.11 aiosignal==1.3.2 - # via aiohttp annotated-types==0.7.0 - # via pydantic -anyio==4.9.0 - # via - # httpx - # openai -attrs==25.3.0 - # via aiohttp -certifi==2025.4.26 - # via - # httpcore - # httpx - # requests -charset-normalizer==3.4.2 - # via requests +anthropic==0.53.0 +anyio==4.8.0 +appnope==0.1.4 +asttokens==3.0.0 +async-timeout==5.0.1 +attrs==24.3.0 +babel==2.16.0 +backrefs==5.8 +beautifulsoup4==4.12.3 +black==24.10.0 +bleach==6.2.0 +boto3==1.37.10 +botocore==1.37.10 +cachecontrol==0.14.1 +cachetools==5.5.0 +cairocffi==1.7.1 +cairosvg==2.7.1 +cerebras-cloud-sdk==1.15.0 +certifi==2024.12.14 +cffi==1.17.1 +charset-normalizer==3.4.1 click==8.1.8 - # via typer +cohere==5.13.4 +colorama==0.4.6 +comm==0.2.2 +courlan==1.3.2 +coverage==7.6.9 +csscompressor==0.9.5 +cssselect2==0.7.0 +datasets==3.2.0 +dateparser==1.2.0 +debugpy==1.8.11 +decorator==5.1.1 +defusedxml==0.7.1 +dill==0.3.8 +diskcache==5.6.3 distro==1.9.0 - # via openai docstring-parser==0.16 - # via instructor (pyproject.toml) -frozenlist==1.7.0 - # via - # aiohttp - # aiosignal -h11==0.16.0 - # via httpcore -httpcore==1.0.9 - # via httpx +eval-type-backport==0.2.2 +exceptiongroup==1.2.2 +executing==2.1.0 +fastapi==0.115.6 +fastavro==1.10.0 +fastjsonschema==2.21.1 +filelock==3.16.1 +fireworks-ai==0.15.10 +frozenlist==1.5.0 +fsspec==2024.9.0 +ghp-import==2.1.0 +gitdb==4.0.11 +gitpython==3.1.43 +google-ai-generativelanguage==0.6.10 +google-api-core==2.24.0 +google-api-python-client==2.156.0 +google-auth==2.37.0 +google-auth-httplib2==0.2.0 +google-cloud-aiplatform==1.75.0 +google-cloud-bigquery==3.27.0 +google-cloud-core==2.4.1 +google-cloud-resource-manager==1.14.0 +google-cloud-storage==2.19.0 +google-crc32c==1.6.0 +google-genai==1.5.0 +google-generativeai==0.8.3 +google-resumable-media==2.7.2 +googleapis-common-protos==1.66.0 +graphviz==0.20.3 +griffe==1.5.1 +groq==0.13.1 +grpc-google-iam-v1==0.13.1 +grpcio==1.68.1 +grpcio-status==1.68.1 +h11==0.14.0 +htmldate==1.9.2 +htmlmin2==0.1.13 +httpcore==1.0.7 +httplib2==0.22.0 httpx==0.28.1 - # via openai +httpx-sse==0.4.0 +httpx-ws==0.7.0 +huggingface-hub==0.27.0 idna==3.10 - # via - # anyio - # httpx - # requests - # yarl -jinja2==3.1.6 - # via instructor (pyproject.toml) -jiter==0.10.0 - # via - # instructor (pyproject.toml) - # openai +importlib-metadata==8.5.0 +iniconfig==2.0.0 +-e file:///Users/ivanleo/Documents/coding/instructor +ipykernel==6.29.5 +ipython==8.18.1 +jedi==0.19.2 +jinja2==3.1.5 +jiter==0.8.2 +jmespath==1.0.1 +jsmin==3.0.1 +jsonpath-python==1.0.6 +jsonref==1.1.0 +jsonschema==4.23.0 +jsonschema-specifications==2024.10.1 +jupyter-client==8.6.3 +jupyter-core==5.7.2 +jupyterlab-pygments==0.3.0 +jupytext==1.16.6 +justext==3.0.1 +litellm==1.53.3 +lxml==5.3.0 +lxml-html-clean==0.4.1 +markdown==3.7 markdown-it-py==3.0.0 - # via rich markupsafe==3.0.2 - # via jinja2 +matplotlib-inline==0.1.7 +mdit-py-plugins==0.4.2 mdurl==0.1.2 - # via markdown-it-py -multidict==6.4.4 - # via - # aiohttp - # yarl -openai==1.86.0 - # via instructor (pyproject.toml) -propcache==0.3.2 - # via - # aiohttp - # yarl -pydantic==2.11.5 - # via - # instructor (pyproject.toml) - # openai -pygments==2.19.1 - # via rich -requests==2.32.4 - # via instructor (pyproject.toml) -rich==14.0.0 - # via - # instructor (pyproject.toml) - # typer +mergedeep==1.3.4 +mistralai==1.5.1 +mistune==3.0.2 +mkdocs==1.6.1 +mkdocs-autorefs==1.4.2 +mkdocs-get-deps==0.2.0 +mkdocs-jupyter==0.25.1 +mkdocs-material==9.6.14 +mkdocs-material-extensions==1.3.1 +mkdocs-minify-plugin==0.8.0 +mkdocs-redirects==1.2.2 +mkdocs-rss-plugin==1.17.1 +mkdocstrings==0.29.1 +mkdocstrings-python==1.12.2 +msgpack==1.1.0 +multidict==6.1.0 +multiprocess==0.70.16 +mypy-extensions==1.0.0 +nbclient==0.10.2 +nbconvert==7.16.4 +nbformat==5.10.4 +nest-asyncio==1.6.0 +nodeenv==1.9.1 +numpy==2.0.2 +openai==1.82.0 +packaging==24.2 +paginate==0.5.7 +pandas==2.2.3 +pandocfilters==1.5.1 +parameterized==0.9.0 +parso==0.8.4 +pathspec==0.12.1 +pexpect==4.9.0 +phonenumbers==8.13.52 +pillow==10.4.0 +platformdirs==4.3.6 +pluggy==1.5.0 +prompt-toolkit==3.0.48 +propcache==0.2.1 +proto-plus==1.25.0 +protobuf==5.29.2 +psutil==6.1.1 +ptyprocess==0.7.0 +pure-eval==0.2.3 +pyarrow==18.1.0 +pyasn1==0.6.1 +pyasn1-modules==0.4.1 +pycparser==2.22 +pydantic==2.10.4 +pydantic-core==2.27.2 +pydantic-extra-types==2.10.1 +pydub==0.25.1 +pygments==2.18.0 +pymdown-extensions==10.13 +pyparsing==3.2.0 +pyright==1.1.391 +pytest==8.3.4 +pytest-asyncio==0.25.0 +pytest-examples==0.0.15 +python-dateutil==2.9.0.post0 +python-dotenv==1.0.1 +pytz==2024.2 +pyyaml==6.0.2 +pyyaml-env-tag==0.1 +pyzmq==26.2.0 +redis==5.2.1 +referencing==0.35.1 +regex==2024.11.6 +requests==2.32.3 +rich==13.9.4 +rpds-py==0.22.3 +rsa==4.9 +ruff==0.8.4 +s3transfer==0.11.4 +shapely==2.0.6 shellingham==1.5.4 - # via typer +six==1.17.0 +smmap==5.0.1 sniffio==1.3.1 - # via - # anyio - # openai -tenacity==9.1.2 - # via instructor (pyproject.toml) +soupsieve==2.6 +sqlalchemy==2.0.36 +sqlmodel==0.0.22 +stack-data==0.6.3 +starlette==0.41.3 +tabulate==0.9.0 +tenacity==9.0.0 +tiktoken==0.8.0 +tinycss2==1.4.0 +tld==0.13 +tokenizers==0.21.0 +tomli==2.2.1 +tornado==6.4.2 tqdm==4.67.1 - # via openai -typer==0.16.0 - # via instructor (pyproject.toml) -typing-extensions==4.13.2 - # via - # anyio - # openai - # pydantic - # pydantic-core - # typer -urllib3==2.4.0 - # via requests -yarl==1.20.1 - # via aiohttp +trafilatura==2.0.0 +traitlets==5.14.3 +typer==0.15.1 +types-requests==2.32.0.20241016 +typing-extensions==4.12.2 +typing-inspect==0.9.0 +tzdata==2024.2 +tzlocal==5.2 +uritemplate==4.1.1 +urllib3==2.3.0 +watchdog==6.0.0 +wcwidth==0.2.13 +webencodings==0.5.1 +websockets==14.2 +writer-sdk==2.2.0 +wsproto==1.2.0 +xmltodict==0.14.2 +xxhash==3.5.0 +yarl==1.18.3 +zipp==3.21.0 diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py new file mode 100644 index 000000000..3eecdee72 --- /dev/null +++ b/tests/test_exceptions.py @@ -0,0 +1,222 @@ +"""Test that all instructor exceptions can be imported and caught properly.""" + +import pytest +from instructor.exceptions import ( + InstructorError, + IncompleteOutputException, + InstructorRetryException, + ValidationError, + ProviderError, + ConfigurationError, + ModeError, + ClientError, +) + + +def test_all_exceptions_can_be_imported(): + """Test that all exceptions can be imported from instructor base package""" + # This test passes if the imports above succeed + assert InstructorError is not None + assert IncompleteOutputException is not None + assert InstructorRetryException is not None + assert ValidationError is not None + assert ProviderError is not None + assert ConfigurationError is not None + assert ModeError is not None + assert ClientError is not None + + +def test_exception_hierarchy(): + """Test that all exceptions inherit from InstructorError.""" + assert issubclass(IncompleteOutputException, InstructorError) + assert issubclass(InstructorRetryException, InstructorError) + assert issubclass(ValidationError, InstructorError) + assert issubclass(ProviderError, InstructorError) + assert issubclass(ConfigurationError, InstructorError) + assert issubclass(ModeError, InstructorError) + assert issubclass(ClientError, InstructorError) + + +def test_base_instructor_error_can_be_caught(): + """Test that InstructorError can catch all instructor exceptions.""" + with pytest.raises(InstructorError): + raise IncompleteOutputException() + + with pytest.raises(InstructorError): + raise InstructorRetryException(n_attempts=3, total_usage=100) + + with pytest.raises(InstructorError): + raise ValidationError("Validation failed") + + with pytest.raises(InstructorError): + raise ProviderError("openai", "API error") + + with pytest.raises(InstructorError): + raise ConfigurationError("Invalid config") + + with pytest.raises(InstructorError): + raise ModeError("tools", "openai", ["json"]) + + with pytest.raises(InstructorError): + raise ClientError("Client initialization failed") + + +def test_incomplete_output_exception(): + """Test IncompleteOutputException attributes and catching.""" + last_completion = {"content": "partial response"} + + with pytest.raises(IncompleteOutputException) as exc_info: + raise IncompleteOutputException(last_completion=last_completion) + + assert exc_info.value.last_completion == last_completion + assert "incomplete due to a max_tokens length limit" in str(exc_info.value) + + +def test_instructor_retry_exception(): + """Test InstructorRetryException attributes and catching.""" + last_completion = {"content": "failed response"} + messages = [{"role": "user", "content": "test"}] + n_attempts = 3 + total_usage = 150 + create_kwargs = {"model": "gpt-3.5-turbo"} + + with pytest.raises(InstructorRetryException) as exc_info: + raise InstructorRetryException( + last_completion=last_completion, + messages=messages, + n_attempts=n_attempts, + total_usage=total_usage, + create_kwargs=create_kwargs, + ) + + exception = exc_info.value + assert exception.last_completion == last_completion + assert exception.messages == messages + assert exception.n_attempts == n_attempts + assert exception.total_usage == total_usage + assert exception.create_kwargs == create_kwargs + + +def test_validation_error(): + """Test ValidationError can be caught.""" + error_message = "Field validation failed" + + with pytest.raises(ValidationError) as exc_info: + raise ValidationError(error_message) + + assert str(exc_info.value) == error_message + + +def test_provider_error(): + """Test ProviderError attributes and catching.""" + provider = "anthropic" + message = "Rate limit exceeded" + + with pytest.raises(ProviderError) as exc_info: + raise ProviderError(provider, message) + + exception = exc_info.value + assert exception.provider == provider + assert f"{provider}: {message}" in str(exception) + + +def test_configuration_error(): + """Test ConfigurationError can be caught.""" + error_message = "Missing required configuration" + + with pytest.raises(ConfigurationError) as exc_info: + raise ConfigurationError(error_message) + + assert str(exc_info.value) == error_message + + +def test_mode_error(): + """Test ModeError attributes and catching.""" + mode = "invalid_mode" + provider = "openai" + valid_modes = ["json", "tools", "functions"] + + with pytest.raises(ModeError) as exc_info: + raise ModeError(mode, provider, valid_modes) + + exception = exc_info.value + assert exception.mode == mode + assert exception.provider == provider + assert exception.valid_modes == valid_modes + assert f"Invalid mode '{mode}' for provider '{provider}'" in str(exception) + assert "json, tools, functions" in str(exception) + + +def test_client_error(): + """Test ClientError can be caught.""" + error_message = "Client not properly initialized" + + with pytest.raises(ClientError) as exc_info: + raise ClientError(error_message) + + assert str(exc_info.value) == error_message + + +def test_specific_exception_catching(): + """Test that specific exceptions can be caught individually.""" + # Test that we can catch specific exceptions without catching others + + with pytest.raises(IncompleteOutputException): + try: + raise IncompleteOutputException() + except InstructorRetryException: + pytest.fail("Should not catch InstructorRetryException") + except IncompleteOutputException: + raise # Re-raise to be caught by pytest.raises + + with pytest.raises(ProviderError): + try: + raise ProviderError("test", "error") + except ConfigurationError: + pytest.fail("Should not catch ConfigurationError") + except ProviderError: + raise # Re-raise to be caught by pytest.raises + + +def test_multiple_exception_handling(): + """Test handling multiple exception types in a single try-except block.""" + + def raise_exception(exc_type: str): + if exc_type == "incomplete": + raise IncompleteOutputException() + elif exc_type == "retry": + raise InstructorRetryException(n_attempts=3, total_usage=100) + elif exc_type == "validation": + raise ValidationError("validation failed") + else: + raise ValueError("unknown exception type") + + # Test catching multiple specific exceptions + for exc_type in ["incomplete", "retry", "validation"]: + with pytest.raises( + (IncompleteOutputException, InstructorRetryException, ValidationError) + ): + raise_exception(exc_type) + + # Test that base exception catches all instructor exceptions + for exc_type in ["incomplete", "retry", "validation"]: + with pytest.raises(InstructorError): + raise_exception(exc_type) + + # Test that non-instructor exceptions are not caught + with pytest.raises(ValueError): + raise_exception("unknown") + + +def test_exception_import_from_instructor(): + """Test that exceptions can be imported from the main instructor module.""" + # Test importing from instructor.exceptions (already done in module imports) + from instructor.exceptions import InstructorError as ImportedError + + assert ImportedError is InstructorError + + # Test that exceptions are accessible and can be used in real scenarios + try: + raise ImportedError("test error") + except InstructorError as e: + assert str(e) == "test error" From 05de0f3548ee318f5cb9d9a010cef027147b7443 Mon Sep 17 00:00:00 2001 From: Emmanuel KOUPOH Date: Mon, 30 Jun 2025 23:07:52 +0000 Subject: [PATCH 3/7] =?UTF-8?q?Update=20TaskAction=20method=20description?= =?UTF-8?q?=20for=20clarity=20on=20task=20creation=20and=E2=80=A6=20(#1637?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/multi-actions/run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/multi-actions/run.py b/examples/multi-actions/run.py index d748a5739..f46f84209 100644 --- a/examples/multi-actions/run.py +++ b/examples/multi-actions/run.py @@ -33,7 +33,7 @@ class Buckets(enum.Enum): class TaskAction(BaseModel): id: int method: Action = Field( - description="Method of creating, for closing a task the task, to close a task only a id is required" + description="Method of creating and closing a task: to close a task, only an ID is required" ) waiting_on: Optional[list[int]] = Field( None, description="IDs of tasks that this task is waiting on" From a54cccd522507f34dbb5ab8be484d68f60a012d9 Mon Sep 17 00:00:00 2001 From: Jason Liu Date: Fri, 4 Jul 2025 17:13:36 -0400 Subject: [PATCH 4/7] Fix SambaNova capitalization (#1651) --- docs/integrations/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/integrations/index.md b/docs/integrations/index.md index 17b389670..b7f1aaa20 100644 --- a/docs/integrations/index.md +++ b/docs/integrations/index.md @@ -35,7 +35,7 @@ Learn how to integrate Instructor with various AI model providers. These compreh [:octicons-arrow-right-16: Cerebras](./cerebras.md) · [:octicons-arrow-right-16: Writer](./writer.md) · [:octicons-arrow-right-16: Perplexity](./perplexity.md) - [:octicons-arrow-right-16: Sambanova](./sambanova.md) + [:octicons-arrow-right-16: SambaNova](./sambanova.md) - :material-open-source-initiative: **Open Source** From 31a75312e596703ad7f55450597241f8a90d7db1 Mon Sep 17 00:00:00 2001 From: David Okpare Date: Mon, 7 Jul 2025 19:45:00 +0100 Subject: [PATCH 5/7] refactor: simplify safety settings configuration for Gemini API (#1659) --- instructor/utils.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/instructor/utils.py b/instructor/utils.py index 8a72cead0..746209aad 100644 --- a/instructor/utils.py +++ b/instructor/utils.py @@ -774,7 +774,7 @@ def update_genai_kwargs( """ Update keyword arguments for google.genai package from OpenAI format. """ - from google.genai.types import HarmCategory, HarmBlockThreshold + from google.genai.types import HarmCategory new_kwargs = kwargs.copy() @@ -798,18 +798,16 @@ def update_genai_kwargs( base_config[gemini_key] = val safety_settings = new_kwargs.pop("safety_settings", {}) - base_config["safety_settings"] = [] - for category in HarmCategory: - if category == HarmCategory.HARM_CATEGORY_UNSPECIFIED: - continue - threshold = safety_settings.get(category, HarmBlockThreshold.OFF) - base_config["safety_settings"].append( + if safety_settings: + base_config["safety_settings"] = [ { "category": category, "threshold": threshold, } - ) + for category, threshold in safety_settings.items() + if category != HarmCategory.HARM_CATEGORY_UNSPECIFIED + ] return base_config From 24f9a1e00cda47bee4089deadc6f356c5c8847de Mon Sep 17 00:00:00 2001 From: Jason Liu Date: Mon, 7 Jul 2025 11:50:09 -0700 Subject: [PATCH 6/7] Update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 82f3479c1..ec0a8f136 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,7 @@ dependencies = [ "mkdocs-material>=9.5.49", ] name = "instructor" -version = "1.9.0" +version = "1.9.1" description = "structured outputs for llm" readme = "README.md" From 793fd6bbb5cad3c61ea810ebd958edd6d8ff0466 Mon Sep 17 00:00:00 2001 From: Canttuchdiz <75583497+Canttuchdiz@users.noreply.github.com> Date: Mon, 7 Jul 2025 14:50:19 -0400 Subject: [PATCH 7/7] Json schema fix (#1657) Co-authored-by: Jason Liu --- instructor/process_response.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/instructor/process_response.py b/instructor/process_response.py index dc792bd37..08442652f 100644 --- a/instructor/process_response.py +++ b/instructor/process_response.py @@ -396,8 +396,11 @@ def handle_json_modes( new_kwargs["response_format"] = {"type": "json_object"} elif mode == Mode.JSON_SCHEMA: new_kwargs["response_format"] = { - "type": "json_object", - "schema": response_model.model_json_schema(), + "type": "json_schema", + "json_schema": { + "name": response_model.__name__, + "schema": response_model.model_json_schema(), + }, } elif mode == Mode.MD_JSON: new_kwargs["messages"].append(