调用 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 提供一个响应模型,让响应的使用更加类型安全:
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,定义一个全新的响应模型:
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
选择模型
查看可用模型:
from typesafe_sdk import TypeSafeClient
print(TypeSafeClient().models.list())
在构造客户端时选择模型:
client = TypeSafeClient(model="jev")
详情参见 Models 资源参考。
配置基础 URL
要让 SDK 使用其他 API URL,请在客户端上设置 base_url,或设置 TYPESAFE_BASE_URL 环境变量。
例如,使用 AI 网关的 API 密钥和模型 ID 通过它连接:
使用 OpenRouter API 密钥和 OpenRouter 模型 ID:
skip: next
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 配合使用:
skip: next
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)
这要求替代 API 遵循 TypeSafe OpenAPI 规范。
重试
在客户端上或按调用传入自定义的 RetryPolicy 作为 retry。无效的 API 密钥会在客户端创建期间、任何请求或重试之前抛出 TypeSafeError。
from typesafe_sdk import RetryPolicy, TypeSafeClient
client = TypeSafeClient(retry=RetryPolicy(max_retries=3, backoff_max=0.2, timeout=1.0))
from typesafe_sdk import RetryPolicy
client.system_one(
state, questions, retry=RetryPolicy(max_retries=3, backoff_max=0.2, timeout=1.0)
)
错误处理
处理 SDK 抛出的异常:
from typesafe_sdk import TypeSafeAPIError
try:
client.system_one(state, questions)
except TypeSafeAPIError as error:
print(error.status, error.request_id)
日志
SDK 通过 typesafe_sdk 记录器输出日志。可按照标准日志指南进行配置:
import logging
logging.getLogger("typesafe_sdk").setLevel(logging.DEBUG)
也可以在导入 SDK 之前将 TYPESAFE_LOG_LEVEL 设置为 debug、info、warning、error 或 off 之一。
info 为每个请求记录一行摘要;debug 还会记录请求和响应的头部与主体。敏感头部(包括 authorization、API 密钥、cookie,以及名称中包含 token 或 secret 的任何头部)会在日志输出中脱敏。请求和响应主体不会脱敏。
环境变量
SDK 会读取并使用以下环境变量:
| 变量 | 配置内容 | 默认值 |
|---|---|---|
TYPESAFE_API_KEY | API 密钥(必填) | — |
TYPESAFE_BASE_URL | API 根 URL | https://api.typesafe.ai |
TYPESAFE_DEFAULT_MODEL | 默认模型 | jev-latest |
TYPESAFE_LOG_LEVEL | typesafe_sdk 记录器级别,导入时应用一次 | 未设置 |
SDK 默认值参见常量参考。
通过 api_key 或 TYPESAFE_API_KEY 提供的 API 密钥会去除首尾空白字符,包括密钥文件中的换行符。空密钥、内部空白字符、控制字符以及非 ASCII 字符会在发送请求之前被拒绝。显式传入的空密钥不会回退到环境变量。
向前兼容
随着 TypeSafe API 的演进,SDK 仍能继续工作,因此你可以在 SDK 新版本提供一等支持之前就采用新的 API 特性。
额外的请求字段
使用 extra_body 发送额外的 API 请求字段。下面的 beam_width 字段仅为示例;请只发送 API 支持的字段。
skip: next
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},
)
原始问题字典
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。
未知的答案类型
SDK 会记录警告并跳过无法识别的答案类型。使用 raw_http_response 可以查看完整的 API 响应,包括这些答案:
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"]
未知的响应字段
已识别响应上的未知额外字段会被忽略。