|
1 | 1 | import { FromSchema, JSONSchema } from 'json-schema-to-ts'; |
2 | 2 | import { Promisable, BetaRunnableTool } from '../../lib/tools/BetaRunnableTool'; |
3 | 3 | import { BetaToolResultContentBlockParam } from '../../resources/beta'; |
| 4 | +import { AutoParseableBetaOutputFormat } from '../../lib/beta-parser'; |
| 5 | +import { AnthropicError } from '../..'; |
| 6 | +import { transformJSONSchema } from '../../lib/transform-json-schema'; |
4 | 7 |
|
5 | 8 | type NoInfer<T> = T extends infer R ? R : never; |
6 | 9 |
|
@@ -30,3 +33,43 @@ export function betaTool<const Schema extends Exclude<JSONSchema, boolean> & { t |
30 | 33 | parse: (content: unknown) => content as FromSchema<Schema>, |
31 | 34 | } as any; |
32 | 35 | } |
| 36 | + |
| 37 | +/** |
| 38 | + * Creates a JSON schema output format object from the given JSON schema. |
| 39 | + * If this is passed to the `.parse()` method then the response message will contain a |
| 40 | + * `.parsed` property that is the result of parsing the content with the given JSON schema. |
| 41 | + * |
| 42 | + */ |
| 43 | +export function betaJSONSchemaOutputFormat< |
| 44 | + const Schema extends Exclude<JSONSchema, boolean> & { type: 'object' }, |
| 45 | +>( |
| 46 | + jsonSchema: Schema, |
| 47 | + options?: { |
| 48 | + transform?: boolean; |
| 49 | + }, |
| 50 | +): AutoParseableBetaOutputFormat<NoInfer<FromSchema<Schema>>> { |
| 51 | + if (jsonSchema.type !== 'object') { |
| 52 | + throw new Error(`JSON schema for tool must be an object, but got ${jsonSchema.type}`); |
| 53 | + } |
| 54 | + |
| 55 | + const transform = options?.transform ?? true; |
| 56 | + if (transform) { |
| 57 | + // todo: doing this is arguably necessary, but it does change the schema the user passed in |
| 58 | + // so I'm not sure how we should handle that |
| 59 | + jsonSchema = transformJSONSchema(jsonSchema) as Schema; |
| 60 | + } |
| 61 | + |
| 62 | + return { |
| 63 | + type: 'json_schema', |
| 64 | + schema: { |
| 65 | + ...jsonSchema, |
| 66 | + }, |
| 67 | + parse: (content) => { |
| 68 | + try { |
| 69 | + return JSON.parse(content); |
| 70 | + } catch (error) { |
| 71 | + throw new AnthropicError(`Failed to parse structured output: ${error}`); |
| 72 | + } |
| 73 | + }, |
| 74 | + }; |
| 75 | +} |
0 commit comments