Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(instructor): introduce ANTHROPIC_JSON mode #542

Merged
merged 9 commits into from
Mar 29, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/workflows/evals.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ name: Weekly Tests
on:
workflow_dispatch:
schedule:
- cron: '0 0 * * 0' # Runs at 00:00 UTC every Sunday
- cron: "0 0 * * 0" # Runs at 00:00 UTC every Sunday
push:
branches: [ main ]
branches: [main]
paths-ignore:
- '**' # Ignore all paths to ensure it only triggers on schedule
- "**" # Ignore all paths to ensure it only triggers on schedule

jobs:
weekly-tests:
Expand All @@ -20,15 +20,15 @@ jobs:
uses: actions/setup-python@v4
with:
python-version: 3.11
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Python 3.11 is not yet released. Please use a stable version of Python.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @jxnl - great example of how the training data extension stuff in GPT4 isn't perfect yet.

cache: 'poetry'
cache: "poetry"

- name: Install Poetry
uses: snok/install-poetry@v1.3.1

- name: Install dependencies
run: poetry install --with dev
run: poetry install --with dev,anthropic

- name: Run all tests
run: poetry run pytest tests/
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
4 changes: 2 additions & 2 deletions .github/workflows/ruff.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Ruff
on:
push:
pull_request:
branches: [ main ]
branches: [main]

env:
WORKING_DIRECTORY: "."
Expand Down Expand Up @@ -42,4 +42,4 @@ jobs:
uses: actions/upload-artifact@v3
with:
name: ruff-log
path: ${{ env.WORKING_DIRECTORY }}/${{ env.RUFF_OUTPUT_FILENAME }}
path: ${{ env.WORKING_DIRECTORY }}/${{ env.RUFF_OUTPUT_FILENAME }}
8 changes: 5 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,23 @@ jobs:
uses: snok/install-poetry@v1.3.1

- name: Install dependencies
run: poetry install --with dev anthropic
run: poetry install --with dev,anthropic

- name: Run tests
run: poetry run pytest tests/ -k "not openai"
run: poetry run pytest tests/ -k "not llm"
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

- name: Generate coverage report
if: matrix.python-version == '3.11'
run: |
poetry run coverage run -m pytest tests/ -k "not openai"
poetry run coverage run -m pytest tests/ -k "not llm"
poetry run coverage report
poetry run coverage html
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

- name: Coveralls GitHub Action
if: matrix.python-version == '3.11'
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/test_docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:

strategy:
matrix:
python-version: ['3.11']
python-version: ["3.11"]

steps:
- uses: actions/checkout@v2
Expand All @@ -22,8 +22,8 @@ jobs:
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Python 3.11 is not yet released. Please use a stable version of Python.

cache: 'poetry'
cache: "poetry"

- name: Cache Poetry virtualenv
uses: actions/cache@v2
with:
Expand All @@ -33,9 +33,9 @@ jobs:
${{ runner.os }}-poetry-

- name: Install dependencies
run: poetry install --with dev,docs,test-docs
run: poetry install --with dev,docs,test-docs,anthropic

- name: Run tests
run: poetry run pytest tests/openai/docs
run: poetry run pytest tests/llm/test_openai/docs
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
4 changes: 2 additions & 2 deletions docs/blog/posts/anthropic.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ authors:

A special shoutout to [Shreya](https://twitter.com/shreyaw_) for her contributions to the anthropic support. As of now, all features are operational with the exception of streaming support.

For those eager to experiment, simply patch the client with `ANTHROPIC_TOOLS`, which will enable you to leverage the `anthropic` client for making requests.
For those eager to experiment, simply patch the client with `ANTHROPIC_JSON`, which will enable you to leverage the `anthropic` client for making requests.

```
pip install instructor[anthropic]
Expand All @@ -28,7 +28,7 @@ import instructor
# Patching the Anthropics client with the instructor for enhanced capabilities
anthropic_client = instructor.patch(
create=anthropic.Anthropic().messages.create,
mode=instructor.Mode.ANTHROPIC_TOOLS
mode=instructor.Mode.ANTHROPIC_JSON
)

class Properties(BaseModel):
Expand Down
30 changes: 30 additions & 0 deletions docs/concepts/patching.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,33 @@ from openai import OpenAI

client = instructor.patch(OpenAI(), mode=instructor.Mode.MD_JSON)
```

## Anthropic JSON Mode

Anthropic JSON mode uses Anthropic's JSON format for responses by setting the `mode` parameter to `instructor.Mode.ANTHROPIC_JSON` when patching the client.

```python
import instructor
from anthropic import Anthropic

create = instructor.patch(
create=anthropic.Anthropic().messages.create,
mode=instructor.Mode.ANTHROPIC_JSON
)

class User(BaseModel):
name: str
age: int

resp = create(
model="claude-3-haiku-20240307",
max_tokens=1024,
messages=[
{
"role": "user",
"content": "Create a user",
}
],
response_model=User,
)
```
32 changes: 0 additions & 32 deletions examples/classification/test_run.py

This file was deleted.

2 changes: 1 addition & 1 deletion examples/match_language/run_v1.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from pydantic import BaseModel, Field
from pydantic import BaseModel
from instructor import patch
from openai import AsyncOpenAI
from langdetect import detect
Expand Down
8 changes: 7 additions & 1 deletion instructor/function_calls.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,13 @@ def from_response(
assert hasattr(completion, "content")
return xml_to_model(cls, extract_xml(completion.content[0].text)) # type:ignore

assert hasattr(completion, "choices")
if mode == Mode.ANTHROPIC_JSON:
assert hasattr(completion, "content")
text = completion.content[0].text # type: ignore
extra_text = extract_json_from_codeblock(text)
return cls.model_validate_json(extra_text)

assert hasattr(completion, "choices"), "No choices in completion"

if completion.choices[0].finish_reason == "length":
logger.error("Incomplete output detected, should increase max_tokens")
Expand Down
1 change: 1 addition & 0 deletions instructor/mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class Mode(enum.Enum):
MD_JSON = "markdown_json_mode"
JSON_SCHEMA = "json_schema_mode"
ANTHROPIC_TOOLS = "anthropic_tools"
ANTHROPIC_JSON = "anthropic_json"

def __new__(cls, value: str) -> "Mode":
member = object.__new__(cls)
Expand Down
32 changes: 30 additions & 2 deletions instructor/process_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from openai.types.chat import ChatCompletion
from pydantic import BaseModel


import json
import inspect
import logging
from typing import (
Expand Down Expand Up @@ -245,7 +245,7 @@ def handle_response_model(
As a genius expert, your task is to understand the content and provide
the parsed objects in json that match the following json_schema:\n

{response_model.model_json_schema()}
{json.dumps(response_model.model_json_schema(), indent=2)}

Make sure to return an instance of the JSON, not the schema itself
"""
Expand Down Expand Up @@ -305,6 +305,34 @@ def handle_response_model(
new_kwargs["system"] = f"{system_prompt}\n{new_kwargs['system']}"
else:
new_kwargs["system"] = system_prompt
elif mode == Mode.ANTHROPIC_JSON:
# anthropic wants system message to be a string so we first extract out any system message
openai_system_messages = [
message["content"]
for message in new_kwargs.get("messages", [])
if message["role"] == "system"
]

new_kwargs["system"] = (
new_kwargs.get("system", "")
+ "\n\n"
+ "\n\n".join(openai_system_messages)
)

new_kwargs["system"] += f"""
You must only response in JSON format that adheres to the following schema:

<JSON_SCHEMA>
{json.dumps(response_model.model_json_schema(), indent=2)}
</JSON_SCHEMA>
"""
new_kwargs["system"] = dedent(new_kwargs["system"])

new_kwargs["messages"] = [
message
for message in new_kwargs.get("messages", [])
if message["role"] != "system"
]
else:
raise ValueError(f"Invalid patch mode: {mode}")

Expand Down
Loading
Loading