2024-07-26 21:40:34 +00:00
|
|
|
import type { z } from 'zod'
|
|
|
|
|
2024-07-31 16:12:00 +00:00
|
|
|
import type * as types from './types'
|
|
|
|
import { safeParseStructuredOutput } from './parse-structured-output'
|
|
|
|
import { stringifyForModel } from './utils'
|
|
|
|
import { zodToJsonSchema } from './zod-to-json-schema'
|
2024-07-26 21:40:34 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Used to mark schemas so we can support both Zod and custom schemas.
|
|
|
|
*/
|
2024-08-04 02:36:44 +00:00
|
|
|
export const schemaSymbol = Symbol('agentic.schema')
|
2024-07-26 21:40:34 +00:00
|
|
|
export const validatorSymbol = Symbol('agentic.validator')
|
|
|
|
|
|
|
|
export type Schema<TData = unknown> = {
|
|
|
|
/**
|
|
|
|
* The JSON Schema.
|
|
|
|
*/
|
|
|
|
readonly jsonSchema: types.JSONSchema
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Optional. Validates that the structure of a value matches this schema,
|
|
|
|
* and returns a typed version of the value if it does.
|
|
|
|
*/
|
|
|
|
readonly validate?: types.ValidatorFn<TData>
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Used to mark schemas so we can support both Zod and custom schemas.
|
|
|
|
*/
|
|
|
|
[schemaSymbol]: true
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Schema type for inference.
|
|
|
|
*/
|
|
|
|
_type: TData
|
|
|
|
}
|
|
|
|
|
|
|
|
export function isSchema(value: unknown): value is Schema {
|
|
|
|
return (
|
|
|
|
typeof value === 'object' &&
|
|
|
|
value !== null &&
|
|
|
|
schemaSymbol in value &&
|
|
|
|
value[schemaSymbol] === true &&
|
|
|
|
'jsonSchema' in value &&
|
|
|
|
'validate' in value
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
export function isZodSchema(value: unknown): value is z.ZodType {
|
|
|
|
return (
|
|
|
|
typeof value === 'object' &&
|
|
|
|
value !== null &&
|
|
|
|
'_type' in value &&
|
|
|
|
'_output' in value &&
|
|
|
|
'_input' in value &&
|
|
|
|
'_def' in value &&
|
|
|
|
'parse' in value &&
|
|
|
|
'safeParse' in value
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
export function asSchema<TData>(
|
2024-08-07 09:52:49 +00:00
|
|
|
schema: z.Schema<TData> | Schema<TData>,
|
|
|
|
opts: { strict?: boolean } = {}
|
2024-07-26 21:40:34 +00:00
|
|
|
): Schema<TData> {
|
2024-08-07 09:52:49 +00:00
|
|
|
return isSchema(schema) ? schema : createSchemaFromZodSchema(schema, opts)
|
2024-07-26 21:40:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a schema from a JSON Schema.
|
|
|
|
*/
|
|
|
|
export function createSchema<TData = unknown>(
|
|
|
|
jsonSchema: types.JSONSchema,
|
|
|
|
{
|
|
|
|
validate
|
|
|
|
}: {
|
|
|
|
validate?: types.ValidatorFn<TData>
|
|
|
|
} = {}
|
|
|
|
): Schema<TData> {
|
|
|
|
return {
|
|
|
|
[schemaSymbol]: true,
|
|
|
|
_type: undefined as TData,
|
|
|
|
jsonSchema,
|
|
|
|
validate
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function createSchemaFromZodSchema<TData>(
|
2024-08-07 09:52:49 +00:00
|
|
|
zodSchema: z.Schema<TData>,
|
|
|
|
opts: { strict?: boolean } = {}
|
2024-07-26 21:40:34 +00:00
|
|
|
): Schema<TData> {
|
2024-08-07 09:52:49 +00:00
|
|
|
return createSchema(zodToJsonSchema(zodSchema, opts), {
|
2024-07-26 21:40:34 +00:00
|
|
|
validate: (value) => {
|
|
|
|
return safeParseStructuredOutput(value, zodSchema)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
const DEFAULT_SCHEMA_PREFIX = `
|
|
|
|
---
|
|
|
|
|
|
|
|
Respond with JSON using the following JSON schema:
|
|
|
|
|
|
|
|
\`\`\`json`
|
|
|
|
const DEFAULT_SCHEMA_SUFFIX = '```'
|
|
|
|
|
|
|
|
export function augmentSystemMessageWithJsonSchema({
|
|
|
|
schema,
|
|
|
|
system,
|
|
|
|
schemaPrefix = DEFAULT_SCHEMA_PREFIX,
|
|
|
|
schemaSuffix = DEFAULT_SCHEMA_SUFFIX
|
|
|
|
}: {
|
|
|
|
schema: types.JSONSchema
|
|
|
|
system?: string
|
|
|
|
schemaPrefix?: string
|
|
|
|
schemaSuffix?: string
|
|
|
|
}): string {
|
|
|
|
return [system, schemaPrefix, stringifyForModel(schema), schemaSuffix]
|
|
|
|
.filter(Boolean)
|
|
|
|
.join('\n')
|
|
|
|
.trim()
|
|
|
|
}
|