ドキュメント

使用ガイド

使用ガイド

TypeSafe Python SDK を使うためのガイドとパターン。

System One API の呼び出し

import asyncio

from typesafe_sdk import AsyncTypeSafeClient, Choice, Noul, Score

async def main() -> None:
    async with AsyncTypeSafeClient() as client:
        result = await client.system_one(
            "I was charged twice. Please help ASAP.",
            {
                "billing": Noul(instructions="Is this about billing?"),
                "tone": Choice(
                    instructions="What is the tone?",
                    criteria={"calm": None, "angry": None},
                ),
                "urgency": Score(
                    instructions="How urgent is this?",
                    criteria=["low", "medium", "high"],
                ),
            },
        )
        print(
            result.nouls["billing"].noul,
            result.choices["tone"].choice,
            result.scores["urgency"].score,
        )

asyncio.run(main())
from typesafe_sdk import Choice, Noul, Score, TypeSafeClient

client = TypeSafeClient()
state = "I was charged twice. Please help ASAP."
questions = {
    "billing": Noul(instructions="Is this about billing?"),
    "tone": Choice(
        instructions="What is the tone?", criteria={"calm": None, "angry": None}
    ),
    "urgency": Score(
        instructions="How urgent is this?", criteria=["low", "medium", "high"]
    ),
}
result = client.system_one(state, questions)
print(
    result.nouls["billing"].noul,
    result.choices["tone"].choice,
    result.scores["urgency"].score,
)

型付きの system_one レスポンス

system_one にレスポンスモデルを渡すと、レスポンスの利用をより型安全にできます:

import asyncio

from typesafe_sdk import AsyncTypeSafeClient, Noul, NoulAnswer, SystemOneResponse

class BillingResponse(SystemOneResponse):
    billing: NoulAnswer

async def main() -> None:
    async with AsyncTypeSafeClient() as client:
        result = await client.system_one(
            "I was charged twice.",
            {"billing": Noul(instructions="Is this about billing?")},
            response_model=BillingResponse,
        )
        assert 0 <= result.billing.noul <= 1
        assert result.billing == result.nouls["billing"]
        print(result.request_id)

asyncio.run(main())
from typesafe_sdk import Noul, NoulAnswer, SystemOneResponse, TypeSafeClient

class BillingResponse(SystemOneResponse):
    billing: NoulAnswer

with TypeSafeClient() as client:
    result = client.system_one(
        "I was charged twice.",
        {"billing": Noul(instructions="Is this about billing?")},
        response_model=BillingResponse,
    )
    assert 0 <= result.billing.noul <= 1
    assert result.billing == result.nouls["billing"]
    print(result.request_id)

カスタムレスポンス型

SystemOneResponse を継承せずに、まったく新しいレスポンスモデルを定義することもできます:

import asyncio

from pydantic import BaseModel

from typesafe_sdk import AsyncTypeSafeClient, Noul, NoulAnswer

class BillingAnswers(BaseModel):
    billing: NoulAnswer

class BillingResponse(BaseModel):
    answers: BillingAnswers

async def main() -> None:
    async with AsyncTypeSafeClient() as client:
        result = await client.system_one(
            "I was charged twice.",
            {"billing": Noul(instructions="Is this about billing?")},
            response_model=BillingResponse,
        )
        assert 0 <= result.answers.billing.noul <= 1

asyncio.run(main())
from pydantic import BaseModel

from typesafe_sdk import Noul, NoulAnswer, TypeSafeClient

class BillingAnswers(BaseModel):
    billing: NoulAnswer

class BillingResponse(BaseModel):
    answers: BillingAnswers

result = TypeSafeClient().system_one(
    "I was charged twice.",
    {"billing": Noul(instructions="Is this about billing?")},
    response_model=BillingResponse,
)
assert 0 <= result.answers.billing.noul <= 1

モデルの選択

利用できるモデルを確認します:

import asyncio

from typesafe_sdk import AsyncTypeSafeClient

async def main() -> None:
    async with AsyncTypeSafeClient() as client:
        print(await client.models.list())

asyncio.run(main())
from typesafe_sdk import TypeSafeClient

print(TypeSafeClient().models.list())

クライアントを構築するときにモデルを選びます:

client = AsyncTypeSafeClient(model="jev")
client = TypeSafeClient(model="jev")

詳しくは Models リソースのリファレンスを参照してください。

ベース URL の設定

SDK を別の API URL で使うには、クライアントの base_url か TYPESAFE_BASE_URL 環境変数を設定します。これには、代替の API が TypeSafe OpenAPI 仕様 に従っている必要があります。

たとえば、AI ゲートウェイをその API key とモデル ID で経由して接続します。

OpenRouter の API key と OpenRouter のモデル ID を使います:

import asyncio
import os

from typesafe_sdk import AsyncTypeSafeClient, Noul

async def main() -> None:
    async with AsyncTypeSafeClient(
        api_key=os.environ["OPENROUTER_API_KEY"],
        base_url="https://openrouter.ai/api",
        model="~typesafe/jev-latest",
    ) as client:
        result = await client.system_one(
            "I was charged twice.",
            {"billing": Noul(instructions="Is this about billing?")},
        )
        print(result.nouls["billing"].noul)

asyncio.run(main())
import os

from typesafe_sdk import Noul, TypeSafeClient

with TypeSafeClient(
    api_key=os.environ["OPENROUTER_API_KEY"],
    base_url="https://openrouter.ai/api",
    model="~typesafe/jev-latest",
) as client:
    result = client.system_one(
        "I was charged twice.",
        {"billing": Noul(instructions="Is this about billing?")},
    )
    print(result.nouls["billing"].noul)

Vercel の TypeSafe 互換 API は SDK で利用できます:

import asyncio
import os

from typesafe_sdk import AsyncTypeSafeClient, Noul

async def main() -> None:
    async with AsyncTypeSafeClient(
        api_key=os.environ["AI_GATEWAY_API_KEY"],
        base_url="https://ai-gateway.vercel.sh/typesafe",
        model="typesafe-ai/jev",
    ) as client:
        result = await client.system_one(
            "I was charged twice.",
            {"billing": Noul(instructions="Is this about billing?")},
        )
        print(result.nouls["billing"].noul)

asyncio.run(main())
import os

from typesafe_sdk import Noul, TypeSafeClient

with TypeSafeClient(
    api_key=os.environ["AI_GATEWAY_API_KEY"],
    base_url="https://ai-gateway.vercel.sh/typesafe",
    model="typesafe-ai/jev",
) as client:
    result = client.system_one(
        "I was charged twice.",
        {"billing": Noul(instructions="Is this about billing?")},
    )
    print(result.nouls["billing"].noul)

Pydantic AI Gateway の API key を使います:

import asyncio
import os

from typesafe_sdk import AsyncTypeSafeClient, Noul

async def main() -> None:
    async with AsyncTypeSafeClient(
        api_key=os.environ["PYDANTIC_AI_GATEWAY_API_KEY"],
        base_url="https://gateway-us.pydantic.dev/proxy/typesafe",
        model="jev-latest",
    ) as client:
        result = await client.system_one(
            "I was charged twice.",
            {"billing": Noul(instructions="Is this about billing?")},
        )
        print(result.nouls["billing"].noul)

asyncio.run(main())
import os

from typesafe_sdk import Noul, TypeSafeClient

with TypeSafeClient(
    api_key=os.environ["PYDANTIC_AI_GATEWAY_API_KEY"],
    base_url="https://gateway-us.pydantic.dev/proxy/typesafe",
    model="jev-latest",
) as client:
    result = client.system_one(
        "I was charged twice.",
        {"billing": Noul(instructions="Is this about billing?")},
    )
    print(result.nouls["billing"].noul)

HTTP/2

import httpx2

from typesafe_sdk import AsyncTypeSafeClient

client = AsyncTypeSafeClient(http_client=httpx2.AsyncClient(http2=True))
import httpx2

from typesafe_sdk import TypeSafeClient

client = TypeSafeClient(http_client=httpx2.Client(http2=True))

再試行

カスタムの RetryPolicy を、クライアントまたは呼び出しごとに retry として渡します。無効な API key は、リクエストや再試行の前に、クライアント作成時に TypeSafeError を送出します。

クライアントで:

from typesafe_sdk import AsyncTypeSafeClient, RetryPolicy

client = AsyncTypeSafeClient(
    retry=RetryPolicy(max_retries=3, backoff_max=0.2, timeout=1.0)
)
from typesafe_sdk import RetryPolicy, TypeSafeClient

client = TypeSafeClient(retry=RetryPolicy(max_retries=3, backoff_max=0.2, timeout=1.0))

呼び出しごとに:

import asyncio

from typesafe_sdk import AsyncTypeSafeClient, RetryPolicy

async def main() -> None:
    async with AsyncTypeSafeClient() as client:
        await client.system_one(
            state,
            questions,
            retry=RetryPolicy(max_retries=3, backoff_max=0.2, timeout=1.0),
        )

asyncio.run(main())
from typesafe_sdk import RetryPolicy

client.system_one(
    state, questions, retry=RetryPolicy(max_retries=3, backoff_max=0.2, timeout=1.0)
)

エラー処理

SDK が送出する例外を処理します:

import asyncio

from typesafe_sdk import AsyncTypeSafeClient, TypeSafeAPIError

async def main() -> None:
    async with AsyncTypeSafeClient() as client:
        try:
            await client.system_one(state, questions)
        except TypeSafeAPIError as error:
            print(error.status, error.request_id)

asyncio.run(main())
from typesafe_sdk import TypeSafeAPIError

try:
    client.system_one(state, questions)
except TypeSafeAPIError as error:
    print(error.status, error.request_id)

ログ

SDK は typesafe_sdk logger に出力します。標準 logging ガイドに従って設定します:

import logging

logging.getLogger("typesafe_sdk").setLevel(logging.DEBUG)

または、SDK をインポートする前に TYPESAFE_LOG_LEVEL を debug、info、warning、error、off のいずれかに設定します。

info はリクエストごとに 1 行の要約を出力します。debug はさらにリクエストとレスポンスのヘッダーとボディも出力します。秘密のヘッダー——authorization、API key、cookie、名前に token や secret を含む任意のヘッダー——はログ出力から伏せられます。リクエストとレスポンスのボディは伏せられません。

環境変数

SDK は次の環境変数を読み取って使用します:

変数 設定内容 既定値
TYPESAFE_API_KEY API key(必須) —
TYPESAFE_BASE_URL API のルート URL https://api.typesafe.ai
TYPESAFE_DEFAULT_MODEL 既定のモデル jev-latest
TYPESAFE_LOG_LEVEL typesafe_sdk logger のレベル。インポート時に一度だけ適用 未設定

SDK の既定値は定数リファレンスを参照してください。

api_key または TYPESAFE_API_KEY で渡された API key は、キーファイル由来の改行を含め、先頭と末尾の空白が除去されます。空のキー、内部の空白、制御文字、非 ASCII 文字は、リクエストを送る前に拒否されます。明示的に空にしたキーは環境にフォールバックしません。

前方互換性

SDK は TypeSafe API の進化に合わせて動き続けるので、SDK のリリースが新機能のファーストクラスサポートを追加する前に、それを採用できます。

追加のリクエストフィールド

追加の API リクエストフィールドは extra_body で送ります。下の beam_width フィールドは例示用です。API がサポートするフィールドだけを送ってください。

import asyncio

from typesafe_sdk import AsyncTypeSafeClient, Noul

async def main() -> None:
    async with AsyncTypeSafeClient() as client:
        await client.system_one(
            "I was charged twice.",
            {"billing": Noul(instructions="About billing?")},
            extra_body={"beam_width": 4},
        )

asyncio.run(main())
from typesafe_sdk import Noul, TypeSafeClient

with TypeSafeClient() as client:
    client.system_one(
        "I was charged twice.",
        {"billing": Noul(instructions="About billing?")},
        extra_body={"beam_width": 4},
    )

生の質問辞書

import asyncio

from typesafe_sdk import AsyncTypeSafeClient

async def main() -> None:
    async with AsyncTypeSafeClient() as client:
        await client.system_one(
            "I was charged twice.",
            {
                "billing": {
                    "type": "noul",
                    "instructions": "About billing?",
                    "weight": 2,
                }
            },
        )

asyncio.run(main())
from typesafe_sdk import TypeSafeClient

with TypeSafeClient() as client:
    client.system_one(
        "I was charged twice.",
        {"billing": {"type": "noul", "instructions": "About billing?", "weight": 2}},
    )

未知の答えの種類

SDK は警告をログに出力し、認識できない答えの種類をスキップします。それらの答えを含む完全な API レスポンスを確認するには raw_http_response を使います:

import asyncio

from typesafe_sdk import AsyncTypeSafeClient, Noul

async def main() -> None:
    async with AsyncTypeSafeClient() as client:
        result = await client.system_one(
            "I was charged twice.",
            {"billing": Noul(instructions="Is this about billing?")},
        )
        print(result.raw_http_response.json()["answers"])

asyncio.run(main())
from typesafe_sdk import Noul, TypeSafeClient

result = TypeSafeClient().system_one(
    "I was charged twice.",
    {"billing": Noul(instructions="Is this about billing?")},
)
raw_answers = result.raw_http_response.json()["answers"]

未知のレスポンスフィールド

認識されたレスポンス上の未知の追加フィールドは無視されます。