This is a WIP of the instructor project implemented in JavaScript. I am not a JavaScript developer, so this is a learning project for me, if you're interested in contributing seriously to this project, please contact me on Twitter
The simple goal of this project is to provide a simple, type-safe, and easy to use interface for the OpenAI API.
import { z } from "zod";
import { instruct } from "instructor";
import OpenAI from "openai";
const UserSchema = z.object({
age: z.number(),
name: z.string().refine((name) => name.includes(" "), {
message: "Name must contain a space",
}),
});
type User = z.infer<typeof UserSchema>;
const client = instruct.patch({
client: OpenAI(process.env.OPENAI_API_KEY, process.env.OPENAI_ORG_ID),
mode: instruct.MODES.TOOLS,
});
const user: User = await client.chat.completions.create({
messages: [{ role: "user", content: "Jason Liu is 30 years old" }],
model: "gpt-3.5-turbo",
response_model: UserSchema,
max_retries: 3,
});
Or if it makes more sense to you, you can use the builder pattern:
const response = await client.chat.completions.create({
messages: [{ role: "user", content: "Jason Liu is 30 years old" }],
model: "gpt-3.5-turbo",
response_model: UserSchema,
max_retries: 3,
});
const user: User = response.model;
- Patching with Tools and Functions
- Patching with JSON MODE
- Throwing Validation errors with ZOD
- Retrying Validations
To adapt these Python examples for TypeScript using Zod, you'll need to translate the Python class definitions and Pydantic models into Zod schemas. Zod is a TypeScript-first schema declaration and validation library that allows you to build complex schemas with great ease and efficiency. Here's how you can convert the given examples:
Translate the Role
and UserDetail
models to Zod schemas. Implement the chain of thought as a string field.
import { z } from "zod";
const RoleSchema = z.object({
chain_of_thought: z
.string()
.nonempty("Think step by step to determine the correct title"),
title: z.string(),
});
const UserDetailSchema = z.object({
age: z.number(),
name: z.string(),
role: RoleSchema,
});
For optional attributes, use Zod's .optional()
method.
const UserDetailSchema = z.object({
age: z.number(),
name: z.string(),
role: z.string().optional(),
});
Implement a wrapper class for handling errors using Zod's .union()
method.
const MaybeUserSchema = z.union([
UserDetailSchema,
z.object({
error: z.boolean().default(false),
message: z.string().optional(),
}),
]);
Use Zod's enum construct for standardized fields.
const RoleEnum = z.enum(["PRINCIPAL", "TEACHER", "STUDENT", "OTHER"]);
const UserDetailSchema = z.object({
age: z.number(),
name: z.string(),
role: RoleEnum,
});
Repeat complex instructions in the schema's descriptions.
const RoleSchema = z.object({
instructions: z
.string()
.nonempty(
"Restate the instructions and rules to correctly determine the title."
),
title: z.string(),
});
const UserDetailSchema = z.object({
age: z.number(),
name: z.string(),
role: RoleSchema,
});
For arbitrary properties, use a list of key-value pairs.
const PropertySchema = z.object({
key: z.string(),
value: z.string(),
});
const UserDetailSchema = z.object({
age: z.number(),
name: z.string(),
properties: z
.array(PropertySchema)
.max(5, "Extract any other properties that might be relevant."),
});
Explicitly define relationships in the schema.
const UserDetailSchema = z.object({
id: z.number(),
age: z.number(),
name: z.string(),
friends: z.array(z.number()),
});
const UserRelationshipsSchema = z.object({
users: z.array(UserDetailSchema),
});
Reuse components for different contexts.
const TimeRangeSchema = z.object({
start_time: z.number(),
end_time: z.number(),
});
const UserDetailSchema = z.object({
id: z.number(),
age: z.number(),
name: z.string(),
work_time: TimeRangeSchema,
leisure_time: TimeRangeSchema,
});
These translations provide a structured approach to creating TypeScript schemas with Zod, mirroring the functionality and intent of the original Python examples.