Skip to content

Commit

Permalink
feat: implement Pyright for Type-Checking (instructor-ai#630)
Browse files Browse the repository at this point in the history
Co-authored-by: Jason Liu <[email protected]>
  • Loading branch information
max-muoto and jxnl authored Apr 29, 2024
1 parent 5b453ee commit fadeb47
Show file tree
Hide file tree
Showing 82 changed files with 752 additions and 712 deletions.
62 changes: 0 additions & 62 deletions .github/workflows/mypy.yml

This file was deleted.

52 changes: 52 additions & 0 deletions .github/workflows/pyright.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Pyright

on:
push:
pull_request:
branches: [ main ]

env:
WORKING_DIRECTORY: "."
PYRIGHT_OUTPUT_FILENAME: "pyright.log"

jobs:
Pyright:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
python-version: ["3.9", "3.10", "3.11"]

steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}

- name: Cache Poetry virtualenv
uses: actions/cache@v2
with:
path: ~/.cache/pypoetry/virtualenvs
key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }}
restore-keys: |
${{ runner.os }}-poetry-
- name: Install Poetry
uses: snok/[email protected]

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

- name: Run Static Type Checking with Pyright
run: |
set -e -o pipefail
poetry run pyright > ${{ env.WORKING_DIRECTORY }}/${{ env.PYRIGHT_OUTPUT_FILENAME }}
- name: Upload Artifacts
uses: actions/upload-artifact@v3
with:
name: pyright-log
path: ${{ env.WORKING_DIRECTORY }}/${{ env.PYRIGHT_OUTPUT_FILENAME }}
29 changes: 0 additions & 29 deletions .mypy.ini

This file was deleted.

15 changes: 3 additions & 12 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,7 @@ repos:
files: ^(instructor|tests|examples)/
- id: ruff-format # Run the formatter.
name: Run Formatter (Ruff)
- repo: local
- repo: https://github.com/RobertCraigie/pyright-python
rev: v1.1.360
hooks:
- id: ci_type_mypy
name: Run Type Check (Mypy)
entry: >
bash -c 'set -o pipefail;
export CUSTOM_PACKAGES="instructor/_types/_alias.py instructor/cli/cli.py instructor/cli/files.py instructor/cli/usage.py instructor/exceptions.py" &&
export CUSTOM_FLAGS="--python-version=3.9 --color-output --no-pretty --follow-imports=skip" &&
curl -sSL https://raw.githubusercontent.com/gao-hongnan/omniverse/2fd5de1b8103e955cd5f022ab016b72fa901fa8f/scripts/devops/continuous-integration/type_mypy.sh |
bash'
language: system
types: [python]
pass_filenames: false
- id: pyright
2 changes: 2 additions & 0 deletions .ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ select = [
"E722",
# unused arguments
"ARG",
# Enforce modern type-syntax
"UP006",
]
ignore = [
# mutable defaults
Expand Down
3 changes: 1 addition & 2 deletions examples/anthropic/run.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from pydantic import BaseModel
from typing import List
import anthropic
import instructor

Expand All @@ -15,7 +14,7 @@ class Properties(BaseModel):
class User(BaseModel):
name: str
age: int
properties: List[Properties]
properties: list[Properties]


user = client.messages.create(
Expand Down
10 changes: 5 additions & 5 deletions examples/auto-ticketer/run.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import instructor
from openai import OpenAI

from typing import List, Optional
from typing import Optional
from pydantic import BaseModel, Field
from enum import Enum

Expand Down Expand Up @@ -32,11 +32,11 @@ class Ticket(BaseModel):
name: str = Field(..., description="Title of the task")
description: str = Field(..., description="Detailed description of the task")
priority: PriorityEnum = Field(..., description="Priority level")
assignees: List[str] = Field(..., description="List of users assigned to the task")
subtasks: Optional[List[Subtask]] = Field(
assignees: list[str] = Field(..., description="List of users assigned to the task")
subtasks: Optional[list[Subtask]] = Field(
None, description="List of subtasks associated with the main task"
)
dependencies: Optional[List[int]] = Field(
dependencies: Optional[list[int]] = Field(
None, description="List of ticket IDs that this ticket depends on"
)

Expand All @@ -46,7 +46,7 @@ class ActionItems(BaseModel):
Correctly resolved set of action items from the given transcript
"""

items: List[Ticket]
items: list[Ticket]


def generate(data: str):
Expand Down
6 changes: 3 additions & 3 deletions examples/avail/run.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from pydantic import BaseModel, Field
from typing import Iterable, List, Literal
from typing import Iterable, Literal
from datetime import datetime, timedelta

from openai import OpenAI
Expand All @@ -17,7 +17,7 @@ class DateRange(BaseModel):
default=None,
description="If the date range repeats, and how often, this way we can generalize the date range to the future., if its special, then we can assume it is a one time event.",
)
days_of_week: List[
days_of_week: list[
Literal[
"monday",
"tuesday",
Expand All @@ -41,7 +41,7 @@ class DateRange(BaseModel):


class AvailabilityResponse(BaseModel):
availability: List[DateRange]
availability: list[DateRange]


def prepare_dates(n=7) -> str:
Expand Down
6 changes: 3 additions & 3 deletions examples/avail/run_mixtral.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import os
from pydantic import BaseModel, Field
from typing import List, Literal
from typing import Literal
from datetime import datetime, timedelta

from openai import OpenAI
Expand All @@ -25,7 +25,7 @@ class DateRange(BaseModel):
default=None,
description="If the date range repeats, and how often, this way we can generalize the date range to the future., if its special, then we can assume it is a one time event.",
)
days_of_week: List[
days_of_week: list[
Literal[
"monday",
"tuesday",
Expand All @@ -49,7 +49,7 @@ class DateRange(BaseModel):


class AvailabilityResponse(BaseModel):
availability: List[DateRange]
availability: list[DateRange]


def prepare_dates(n=7) -> str:
Expand Down
5 changes: 2 additions & 3 deletions examples/batch-classification/run-cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

from openai import AsyncOpenAI
from pydantic import BaseModel, Field, field_validator
from typing import List
from enum import Enum

client = instructor.from_openai(AsyncOpenAI(), mode=instructor.Mode.TOOLS)
Expand Down Expand Up @@ -40,7 +39,7 @@ class QuestionClassification(BaseModel):
chain_of_thought: str = Field(
..., description="The chain of thought that led to the classification"
)
classification: List[QuestionType] = Field(
classification: list[QuestionType] = Field(
description=f"An accuracy and correct prediction predicted class of question. Only allowed types: {[t.value for t in QuestionType]}, should be used",
)

Expand Down Expand Up @@ -68,7 +67,7 @@ async def classify(data: str):
)


async def main(questions: List[str]):
async def main(questions: list[str]):
tasks = [classify(question) for question in questions]
resps = []
for task in asyncio.as_completed(tasks):
Expand Down
5 changes: 2 additions & 3 deletions examples/batch-classification/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

from openai import AsyncOpenAI
from pydantic import BaseModel, Field, field_validator
from typing import List
from enum import Enum

client = AsyncOpenAI()
Expand Down Expand Up @@ -42,7 +41,7 @@ class QuestionClassification(BaseModel):
chain_of_thought: str = Field(
..., description="The chain of thought that led to the classification"
)
classification: List[QuestionType] = Field(
classification: list[QuestionType] = Field(
description=f"An accuracy and correct prediction predicted class of question. Only allowed types: {[t.value for t in QuestionType]}, should be used",
)

Expand All @@ -69,7 +68,7 @@ async def classify(data: str):
)


async def main(questions: List[str], *, path_to_jsonl: str = None):
async def main(questions: list[str], *, path_to_jsonl: str = None):
tasks = [classify(question) for question in questions]
for task in asyncio.as_completed(tasks):
question, label = await task
Expand Down
5 changes: 2 additions & 3 deletions examples/batch-classification/run_langsmith.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

from openai import AsyncOpenAI
from pydantic import BaseModel, Field, field_validator
from typing import List
from enum import Enum

client = wrap_openai(AsyncOpenAI())
Expand Down Expand Up @@ -44,7 +43,7 @@ class QuestionClassification(BaseModel):
chain_of_thought: str = Field(
..., description="The chain of thought that led to the classification"
)
classification: List[QuestionType] = Field(
classification: list[QuestionType] = Field(
description=f"An accuracy and correct prediction predicted class of question. Only allowed types: {[t.value for t in QuestionType]}, should be used",
)

Expand Down Expand Up @@ -73,7 +72,7 @@ async def classify(data: str):
)


async def main(questions: List[str]):
async def main(questions: list[str]):
tasks = [classify(question) for question in questions]
resps = []
for task in asyncio.as_completed(tasks):
Expand Down
9 changes: 4 additions & 5 deletions examples/chain-of-density/chain_of_density.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from pydantic import BaseModel, Field, field_validator
from typing import List
import instructor
import nltk
from openai import OpenAI
Expand Down Expand Up @@ -38,12 +37,12 @@ class RewrittenSummary(BaseModel):
...,
description="This is a new, denser summary of identical length which covers every entity and detail from the previous summary plus the Missing Entities. It should have the same length ( ~ 80 words ) as the previous summary and should be easily understood without the Article",
)
absent: List[str] = Field(
absent: list[str] = Field(
...,
default_factory=list,
description="this is a list of Entities found absent from the new summary that were present in the previous summary",
)
missing: List[str] = Field(
missing: list[str] = Field(
default_factory=list,
description="This is a list of 1-3 informative Entities from the Article that are missing from the new summary which should be included in the next generated summary.",
)
Expand Down Expand Up @@ -77,15 +76,15 @@ def min_length(cls, v: str):
return v

@field_validator("missing")
def has_missing_entities(cls, missing_entities: List[str]):
def has_missing_entities(cls, missing_entities: list[str]):
if len(missing_entities) == 0:
raise ValueError(
"You must identify 1-3 informative Entities from the Article which are missing from the previously generated summary to be used in a new summary"
)
return missing_entities

@field_validator("absent")
def has_no_absent_entities(cls, absent_entities: List[str]):
def has_no_absent_entities(cls, absent_entities: list[str]):
absent_entity_string = ",".join(absent_entities)
if len(absent_entities) > 0:
print(f"Detected absent entities of {absent_entity_string}")
Expand Down
Loading

0 comments on commit fadeb47

Please sign in to comment.