diff --git a/.changeset/healthy-moons-type.md b/.changeset/healthy-moons-type.md new file mode 100644 index 00000000000..1f38000ceda --- /dev/null +++ b/.changeset/healthy-moons-type.md @@ -0,0 +1,6 @@ +--- +'firebase': major +'@firebase/ai': major +--- + +Add support for `anyOf` schemas diff --git a/common/api-review/ai.api.md b/common/api-review/ai.api.md index d096d4c27f6..859cfb59688 100644 --- a/common/api-review/ai.api.md +++ b/common/api-review/ai.api.md @@ -65,6 +65,17 @@ export interface AIOptions { backend: Backend; } +// @public +export class AnyOfSchema extends Schema { + constructor(schemaParams: SchemaParams & { + anyOf: TypedSchema[]; + }); + // (undocumented) + anyOf: TypedSchema[]; + // @internal (undocumented) + toJSON(): SchemaRequest; +} + // @public export class ArraySchema extends Schema { constructor(schemaParams: SchemaParams, items: TypedSchema); @@ -776,6 +787,10 @@ export abstract class Schema implements SchemaInterface { constructor(schemaParams: SchemaInterface); [key: string]: unknown; // (undocumented) + static anyOf(anyOfParams: SchemaParams & { + anyOf: TypedSchema[]; + }): AnyOfSchema; + // (undocumented) static array(arrayParams: SchemaParams & { items: Schema; }): ArraySchema; @@ -804,12 +819,12 @@ export abstract class Schema implements SchemaInterface { static string(stringParams?: SchemaParams): StringSchema; // @internal toJSON(): SchemaRequest; - type: SchemaType; + type?: SchemaType; } // @public export interface SchemaInterface extends SchemaShared { - type: SchemaType; + type?: SchemaType; } // @public @@ -819,13 +834,14 @@ export interface SchemaParams extends SchemaShared { // @public export interface SchemaRequest extends SchemaShared { required?: string[]; - type: SchemaType; + type?: SchemaType; } // @public export interface SchemaShared { // (undocumented) [key: string]: unknown; + anyOf?: T[]; description?: string; enum?: string[]; example?: unknown; @@ -900,7 +916,7 @@ export interface ToolConfig { } // @public -export type TypedSchema = IntegerSchema | NumberSchema | StringSchema | BooleanSchema | ObjectSchema | ArraySchema; +export type TypedSchema = IntegerSchema | NumberSchema | StringSchema | BooleanSchema | ObjectSchema | ArraySchema | AnyOfSchema; // @public export interface UsageMetadata { diff --git a/docs-devsite/_toc.yaml b/docs-devsite/_toc.yaml index b77a6b5910e..0f9372bf72a 100644 --- a/docs-devsite/_toc.yaml +++ b/docs-devsite/_toc.yaml @@ -12,6 +12,8 @@ toc: path: /docs/reference/js/ai.aimodel.md - title: AIOptions path: /docs/reference/js/ai.aioptions.md + - title: AnyOfSchema + path: /docs/reference/js/ai.anyofschema.md - title: ArraySchema path: /docs/reference/js/ai.arrayschema.md - title: Backend diff --git a/docs-devsite/ai.anyofschema.md b/docs-devsite/ai.anyofschema.md new file mode 100644 index 00000000000..6fc0fbc60a1 --- /dev/null +++ b/docs-devsite/ai.anyofschema.md @@ -0,0 +1,58 @@ +Project: /docs/reference/js/_project.yaml +Book: /docs/reference/_book.yaml +page_type: reference + +{% comment %} +DO NOT EDIT THIS FILE! +This is generated by the JS SDK team, and any local changes will be +overwritten. Changes should be made in the source code at +https://github.com/firebase/firebase-js-sdk +{% endcomment %} + +# AnyOfSchema class +Schema class representing a value that can conform to any of the provided sub-schemas. This is useful when a field can accept multiple distinct types or structures. + +Signature: + +```typescript +export declare class AnyOfSchema extends Schema +``` +Extends: [Schema](./ai.schema.md#schema_class) + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(schemaParams)](./ai.anyofschema.md#anyofschemaconstructor) | | Constructs a new instance of the AnyOfSchema class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [anyOf](./ai.anyofschema.md#anyofschemaanyof) | | [TypedSchema](./ai.md#typedschema)\[\] | | + +## AnyOfSchema.(constructor) + +Constructs a new instance of the `AnyOfSchema` class + +Signature: + +```typescript +constructor(schemaParams: SchemaParams & { + anyOf: TypedSchema[]; + }); +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| schemaParams | [SchemaParams](./ai.schemaparams.md#schemaparams_interface) & { anyOf: [TypedSchema](./ai.md#typedschema)\[\]; } | | + +## AnyOfSchema.anyOf + +Signature: + +```typescript +anyOf: TypedSchema[]; +``` diff --git a/docs-devsite/ai.generationconfig.md b/docs-devsite/ai.generationconfig.md index f9697a07454..3810fdb3621 100644 --- a/docs-devsite/ai.generationconfig.md +++ b/docs-devsite/ai.generationconfig.md @@ -28,7 +28,7 @@ export interface GenerationConfig | [presencePenalty](./ai.generationconfig.md#generationconfigpresencepenalty) | number | | | [responseMimeType](./ai.generationconfig.md#generationconfigresponsemimetype) | string | Output response MIME type of the generated candidate text. Supported MIME types are text/plain (default, text output), application/json (JSON response in the candidates), and text/x.enum. | | [responseModalities](./ai.generationconfig.md#generationconfigresponsemodalities) | [ResponseModality](./ai.md#responsemodality)\[\] | (Public Preview) Generation modalities to be returned in generation responses. | -| [responseSchema](./ai.generationconfig.md#generationconfigresponseschema) | [TypedSchema](./ai.md#typedschema) \| [SchemaRequest](./ai.schemarequest.md#schemarequest_interface) | Output response schema of the generated candidate text. This value can be a class generated with a [Schema](./ai.schema.md#schema_class) static method like Schema.string() or Schema.object() or it can be a plain JS object matching the [SchemaRequest](./ai.schemarequest.md#schemarequest_interface) interface.
Note: This only applies when the specified responseMIMEType supports a schema; currently this is limited to application/json and text/x.enum. | +| [responseSchema](./ai.generationconfig.md#generationconfigresponseschema) | [TypedSchema](./ai.md#typedschema) \| [SchemaRequest](./ai.schemarequest.md#schemarequest_interface) | Output response schema of the generated candidate text. This value can be a class generated with a [Schema](./ai.schema.md#schema_class) static method like Schema.string() or Schema.object() or it can be a plain JS object matching the [SchemaRequest](./ai.schemarequest.md#schemarequest_interface) interface.
Note: This only applies when the specified responseMimeType supports a schema; currently this is limited to application/json and text/x.enum. | | [stopSequences](./ai.generationconfig.md#generationconfigstopsequences) | string\[\] | | | [temperature](./ai.generationconfig.md#generationconfigtemperature) | number | | | [topK](./ai.generationconfig.md#generationconfigtopk) | number | | @@ -93,7 +93,7 @@ responseModalities?: ResponseModality[]; ## GenerationConfig.responseSchema -Output response schema of the generated candidate text. This value can be a class generated with a [Schema](./ai.schema.md#schema_class) static method like `Schema.string()` or `Schema.object()` or it can be a plain JS object matching the [SchemaRequest](./ai.schemarequest.md#schemarequest_interface) interface.
Note: This only applies when the specified `responseMIMEType` supports a schema; currently this is limited to `application/json` and `text/x.enum`. +Output response schema of the generated candidate text. This value can be a class generated with a [Schema](./ai.schema.md#schema_class) static method like `Schema.string()` or `Schema.object()` or it can be a plain JS object matching the [SchemaRequest](./ai.schemarequest.md#schemarequest_interface) interface.
Note: This only applies when the specified `responseMimeType` supports a schema; currently this is limited to `application/json` and `text/x.enum`. Signature: diff --git a/docs-devsite/ai.md b/docs-devsite/ai.md index c43c0391ba4..8223c745575 100644 --- a/docs-devsite/ai.md +++ b/docs-devsite/ai.md @@ -29,6 +29,7 @@ The Firebase AI Web SDK. | --- | --- | | [AIError](./ai.aierror.md#aierror_class) | Error class for the Firebase AI SDK. | | [AIModel](./ai.aimodel.md#aimodel_class) | Base class for Firebase AI model APIs.Instances of this class are associated with a specific Firebase AI [Backend](./ai.backend.md#backend_class) and provide methods for interacting with the configured generative model. | +| [AnyOfSchema](./ai.anyofschema.md#anyofschema_class) | Schema class representing a value that can conform to any of the provided sub-schemas. This is useful when a field can accept multiple distinct types or structures. | | [ArraySchema](./ai.arrayschema.md#arrayschema_class) | Schema class for "array" types. The items param should refer to the type of item that can be a member of the array. | | [Backend](./ai.backend.md#backend_class) | Abstract base class representing the configuration for an AI service backend. This class should not be instantiated directly. Use its subclasses; [GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class) for the Gemini Developer API (via [Google AI](https://ai.google/)), and [VertexAIBackend](./ai.vertexaibackend.md#vertexaibackend_class) for the Vertex AI Gemini API. | | [BooleanSchema](./ai.booleanschema.md#booleanschema_class) | Schema class for "boolean" types. | @@ -410,7 +411,7 @@ A type that includes all specific Schema types. Signature: ```typescript -export type TypedSchema = IntegerSchema | NumberSchema | StringSchema | BooleanSchema | ObjectSchema | ArraySchema; +export type TypedSchema = IntegerSchema | NumberSchema | StringSchema | BooleanSchema | ObjectSchema | ArraySchema | AnyOfSchema; ``` ## VertexAI diff --git a/docs-devsite/ai.schema.md b/docs-devsite/ai.schema.md index b0681b0cdf3..ce90cf9c805 100644 --- a/docs-devsite/ai.schema.md +++ b/docs-devsite/ai.schema.md @@ -33,12 +33,13 @@ export declare abstract class Schema implements SchemaInterface | [example](./ai.schema.md#schemaexample) | | unknown | Optional. The example of the property. | | [format](./ai.schema.md#schemaformat) | | string | Optional. The format of the property. Supported formats:
  • for NUMBER type: "float", "double"
  • for INTEGER type: "int32", "int64"
  • for STRING type: "email", "byte", etc
| | [nullable](./ai.schema.md#schemanullable) | | boolean | Optional. Whether the property is nullable. Defaults to false. | -| [type](./ai.schema.md#schematype) | | [SchemaType](./ai.md#schematype) | Optional. The type of the property. [SchemaType](./ai.md#schematype). | +| [type](./ai.schema.md#schematype) | | [SchemaType](./ai.md#schematype) | Optional. The type of the property. [SchemaType](./ai.md#schematype). This can only be undefined when using anyOf schemas, which do not have an explicit type in the . | ## Methods | Method | Modifiers | Description | | --- | --- | --- | +| [anyOf(anyOfParams)](./ai.schema.md#schemaanyof) | static | | | [array(arrayParams)](./ai.schema.md#schemaarray) | static | | | [boolean(booleanParams)](./ai.schema.md#schemaboolean) | static | | | [enumString(stringParams)](./ai.schema.md#schemaenumstring) | static | | @@ -105,14 +106,34 @@ nullable: boolean; ## Schema.type -Optional. The type of the property. [SchemaType](./ai.md#schematype). +Optional. The type of the property. [SchemaType](./ai.md#schematype). This can only be undefined when using `anyOf` schemas, which do not have an explicit type in the . Signature: ```typescript -type: SchemaType; +type?: SchemaType; ``` +## Schema.anyOf() + +Signature: + +```typescript +static anyOf(anyOfParams: SchemaParams & { + anyOf: TypedSchema[]; + }): AnyOfSchema; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| anyOfParams | [SchemaParams](./ai.schemaparams.md#schemaparams_interface) & { anyOf: [TypedSchema](./ai.md#typedschema)\[\]; } | | + +Returns: + +[AnyOfSchema](./ai.anyofschema.md#anyofschema_class) + ## Schema.array() Signature: diff --git a/docs-devsite/ai.schemainterface.md b/docs-devsite/ai.schemainterface.md index 6dd33e69e18..11e3542b357 100644 --- a/docs-devsite/ai.schemainterface.md +++ b/docs-devsite/ai.schemainterface.md @@ -23,14 +23,14 @@ export interface SchemaInterface extends SchemaShared | Property | Type | Description | | --- | --- | --- | -| [type](./ai.schemainterface.md#schemainterfacetype) | [SchemaType](./ai.md#schematype) | The type of the property. [SchemaType](./ai.md#schematype). | +| [type](./ai.schemainterface.md#schemainterfacetype) | [SchemaType](./ai.md#schematype) | The type of the property. this can only be undefined when using anyof schemas, which do not have an explicit type in the . | ## SchemaInterface.type -The type of the property. [SchemaType](./ai.md#schematype). +The type of the property. this can only be undefined when using `anyof` schemas, which do not have an explicit type in the . Signature: ```typescript -type: SchemaType; +type?: SchemaType; ``` diff --git a/docs-devsite/ai.schemarequest.md b/docs-devsite/ai.schemarequest.md index e71d24a6b1a..15423f967a4 100644 --- a/docs-devsite/ai.schemarequest.md +++ b/docs-devsite/ai.schemarequest.md @@ -24,7 +24,7 @@ export interface SchemaRequest extends SchemaShared | Property | Type | Description | | --- | --- | --- | | [required](./ai.schemarequest.md#schemarequestrequired) | string\[\] | Optional. Array of required property. | -| [type](./ai.schemarequest.md#schemarequesttype) | [SchemaType](./ai.md#schematype) | The type of the property. [SchemaType](./ai.md#schematype). | +| [type](./ai.schemarequest.md#schemarequesttype) | [SchemaType](./ai.md#schematype) | The type of the property. this can only be undefined when using anyof schemas, which do not have an explicit type in the . | ## SchemaRequest.required @@ -38,10 +38,10 @@ required?: string[]; ## SchemaRequest.type -The type of the property. [SchemaType](./ai.md#schematype). +The type of the property. this can only be undefined when using `anyof` schemas, which do not have an explicit type in the . Signature: ```typescript -type: SchemaType; +type?: SchemaType; ``` diff --git a/docs-devsite/ai.schemashared.md b/docs-devsite/ai.schemashared.md index eba57f82935..fd903b34395 100644 --- a/docs-devsite/ai.schemashared.md +++ b/docs-devsite/ai.schemashared.md @@ -22,6 +22,7 @@ export interface SchemaShared | Property | Type | Description | | --- | --- | --- | +| [anyOf](./ai.schemashared.md#schemasharedanyof) | T\[\] | An array of [Schema](./ai.schema.md#schema_class). The generated data must be valid against any of the schemas listed in this array. This allows specifying multiple possible structures or types for a single field. | | [description](./ai.schemashared.md#schemashareddescription) | string | Optional. The description of the property. | | [enum](./ai.schemashared.md#schemasharedenum) | string\[\] | Optional. The enum of the property. | | [example](./ai.schemashared.md#schemasharedexample) | unknown | Optional. The example of the property. | @@ -30,6 +31,16 @@ export interface SchemaShared | [nullable](./ai.schemashared.md#schemasharednullable) | boolean | Optional. Whether the property is nullable. | | [properties](./ai.schemashared.md#schemasharedproperties) | { \[k: string\]: T; } | Optional. Map of Schema objects. | +## SchemaShared.anyOf + +An array of [Schema](./ai.schema.md#schema_class). The generated data must be valid against any of the schemas listed in this array. This allows specifying multiple possible structures or types for a single field. + +Signature: + +```typescript +anyOf?: T[]; +``` + ## SchemaShared.description Optional. The description of the property. diff --git a/packages/ai/src/requests/schema-builder.test.ts b/packages/ai/src/requests/schema-builder.test.ts index d05b81381ea..451fdca8a54 100644 --- a/packages/ai/src/requests/schema-builder.test.ts +++ b/packages/ai/src/requests/schema-builder.test.ts @@ -15,10 +15,16 @@ * limitations under the License. */ +import { AIError } from '../errors'; import { expect, use } from 'chai'; import sinonChai from 'sinon-chai'; -import { Schema } from './schema-builder'; -import { AIErrorCode } from '../types'; +import { + AnyOfSchema, + NumberSchema, + Schema, + StringSchema +} from './schema-builder'; +import { AIErrorCode, SchemaType } from '../types'; use(sinonChai); @@ -242,9 +248,149 @@ describe('Schema builder', () => { population: Schema.integer({ nullable: true }) }, optionalProperties: ['cat'] - }); + }) as any; // Cast to any to bypass TypedSchema check for testing purposes + expect(() => schema.toJSON()).to.throw( + AIError, + /Property "cat" specified in "optionalProperties" does not exist./ + ); + // Check the error code as well expect(() => schema.toJSON()).to.throw(AIErrorCode.INVALID_SCHEMA); }); + + describe('AnyOfSchema', () => { + it('builds an anyOf schema with basic types using Schema.anyOf()', () => { + const schema: AnyOfSchema = Schema.anyOf({ + anyOf: [Schema.string(), Schema.number()] + }); + + expect(schema).to.be.instanceOf(AnyOfSchema); + expect(schema.type).to.be.undefined; + expect(schema.nullable).to.be.false; // Default from SchemaParams + expect(schema.anyOf).to.be.an('array').with.lengthOf(2); + expect(schema.anyOf[0]).to.be.instanceOf(StringSchema); + expect(schema.anyOf[1]).to.be.instanceOf(NumberSchema); + + expect(schema.toJSON()).to.eql({ + type: undefined, + anyOf: [ + { type: 'string', nullable: false }, + { type: 'number', nullable: false } + ], + nullable: false + }); + }); + + it('builds an anyOf schema with complex types and options', () => { + const schema = Schema.anyOf({ + description: 'Can be a string or a detailed object', + nullable: true, + anyOf: [ + Schema.string({ description: 'A simple string' }), + Schema.object({ + properties: { + id: Schema.integer(), + name: Schema.string() + }, + description: 'A detailed object', + nullable: false // Explicitly set for the object schema itself + }) + ] + }); + + expect(schema.description).to.equal( + 'Can be a string or a detailed object' + ); + expect(schema.nullable).to.be.true; + expect(schema.anyOf).to.be.an('array').with.lengthOf(2); + + expect(schema.toJSON()).to.eql({ + type: undefined, + description: 'Can be a string or a detailed object', + nullable: true, + anyOf: [ + { type: 'string', description: 'A simple string', nullable: false }, + { + type: 'object', + description: 'A detailed object', + properties: { + id: { type: 'integer', nullable: false }, + name: { type: 'string', nullable: false } + }, + required: ['id', 'name'], + nullable: false + } + ] + }); + }); + + it('correctly overrides type to undefined even if type is passed in params', () => { + const schema = Schema.anyOf({ + type: SchemaType.STRING, + anyOf: [Schema.string(), Schema.number()] + }); + expect(schema.toJSON().type).to.be.undefined; + expect(schema.toJSON()).to.eql({ + type: undefined, // Explicitly undefined for anyOf + anyOf: [ + { type: 'string', nullable: false }, + { type: 'number', nullable: false } + ], + nullable: false // Default from SchemaParams + }); + }); + + it('toJSON() correctly serializes nested complex schemas within anyOf', () => { + const schema = Schema.anyOf({ + anyOf: [ + Schema.object({ + properties: { name: Schema.string() }, + optionalProperties: ['name'] + }), + Schema.array({ items: Schema.integer() }) + ] + }); + expect(schema.toJSON()).to.eql({ + type: undefined, + anyOf: [ + { + type: 'object', + properties: { name: { type: 'string', nullable: false } }, + nullable: false + }, + { + type: 'array', + items: { type: 'integer', nullable: false }, + nullable: false + } + ], + nullable: false + }); + }); + + it('throws an error if the anyOf array is empty', () => { + expect(() => Schema.anyOf({ anyOf: [] })).to.throw( + AIErrorCode.INVALID_SCHEMA + ); + }); + }); + + describe('ObjectSchema toJSON() optionalProperties edge cases', () => { + it('handles empty optionalProperties array (all properties required)', () => { + const schema = Schema.object({ + properties: { a: Schema.string(), b: Schema.integer() }, + optionalProperties: [] + }); + expect(schema.toJSON().required).to.deep.equal(['a', 'b']); + }); + + it('handles all properties being optional (empty required array)', () => { + const schema = Schema.object({ + properties: { a: Schema.string(), b: Schema.integer() }, + optionalProperties: ['a', 'b'] + }); + expect(schema.toJSON().required).to.be.undefined; // or empty array, depending on implementation + }); + }); }); const layeredSchemaOutputPartial = { diff --git a/packages/ai/src/requests/schema-builder.ts b/packages/ai/src/requests/schema-builder.ts index 524cfdb1c20..0f46f0b098c 100644 --- a/packages/ai/src/requests/schema-builder.ts +++ b/packages/ai/src/requests/schema-builder.ts @@ -35,9 +35,10 @@ import { export abstract class Schema implements SchemaInterface { /** * Optional. The type of the property. {@link - * SchemaType}. + * SchemaType}. This can only be undefined when using `anyOf` schemas, which do not have an + * explicit type in the {@link OpenAPI specification | https://swagger.io/docs/specification/v3_0/data-models/data-types/#any-type}. */ - type: SchemaType; + type?: SchemaType; /** Optional. The format of the property. * Supported formats:
*
    @@ -60,12 +61,22 @@ export abstract class Schema implements SchemaInterface { [key: string]: unknown; constructor(schemaParams: SchemaInterface) { + // TODO(dlarocque): Enforce this with union types + if (!schemaParams.type && !schemaParams.anyOf) { + throw new AIError( + AIErrorCode.INVALID_SCHEMA, + "A schema must have either a 'type' or an 'anyOf' array of sub-schemas." + ); + } // eslint-disable-next-line guard-for-in for (const paramKey in schemaParams) { this[paramKey] = schemaParams[paramKey]; } // Ensure these are explicitly set to avoid TS errors. this.type = schemaParams.type; + this.format = schemaParams.hasOwnProperty('format') + ? schemaParams.format + : undefined; this.nullable = schemaParams.hasOwnProperty('nullable') ? !!schemaParams.nullable : false; @@ -77,7 +88,7 @@ export abstract class Schema implements SchemaInterface { * @internal */ toJSON(): SchemaRequest { - const obj: { type: SchemaType; [key: string]: unknown } = { + const obj: { type?: SchemaType; [key: string]: unknown } = { type: this.type }; for (const prop in this) { @@ -133,6 +144,12 @@ export abstract class Schema implements SchemaInterface { static boolean(booleanParams?: SchemaParams): BooleanSchema { return new BooleanSchema(booleanParams); } + + static anyOf( + anyOfParams: SchemaParams & { anyOf: TypedSchema[] } + ): AnyOfSchema { + return new AnyOfSchema(anyOfParams); + } } /** @@ -145,7 +162,8 @@ export type TypedSchema = | StringSchema | BooleanSchema | ObjectSchema - | ArraySchema; + | ArraySchema + | AnyOfSchema; /** * Schema class for "integer" types. @@ -290,3 +308,37 @@ export class ObjectSchema extends Schema { return obj as SchemaRequest; } } + +/** + * Schema class representing a value that can conform to any of the provided sub-schemas. This is + * useful when a field can accept multiple distinct types or structures. + * @public + */ +export class AnyOfSchema extends Schema { + anyOf: TypedSchema[]; // Re-define field to narrow to required type + constructor(schemaParams: SchemaParams & { anyOf: TypedSchema[] }) { + if (schemaParams.anyOf.length === 0) { + throw new AIError( + AIErrorCode.INVALID_SCHEMA, + "The 'anyOf' array must not be empty." + ); + } + super({ + ...schemaParams, + type: undefined // anyOf schemas do not have an explicit type + }); + this.anyOf = schemaParams.anyOf; + } + + /** + * @internal + */ + toJSON(): SchemaRequest { + const obj = super.toJSON(); + // Ensure the 'anyOf' property contains serialized SchemaRequest objects. + if (this.anyOf && Array.isArray(this.anyOf)) { + obj.anyOf = (this.anyOf as TypedSchema[]).map(s => s.toJSON()); + } + return obj; + } +} diff --git a/packages/ai/src/types/requests.ts b/packages/ai/src/types/requests.ts index 67f45095c2a..8abe74363f5 100644 --- a/packages/ai/src/types/requests.ts +++ b/packages/ai/src/types/requests.ts @@ -99,7 +99,7 @@ export interface GenerationConfig { * value can be a class generated with a {@link Schema} static method * like `Schema.string()` or `Schema.object()` or it can be a plain * JS object matching the {@link SchemaRequest} interface. - *
    Note: This only applies when the specified `responseMIMEType` supports a schema; currently + *
    Note: This only applies when the specified `responseMimeType` supports a schema; currently * this is limited to `application/json` and `text/x.enum`. */ responseSchema?: TypedSchema | SchemaRequest; diff --git a/packages/ai/src/types/schema.ts b/packages/ai/src/types/schema.ts index e9fe9286b61..314cb33a57e 100644 --- a/packages/ai/src/types/schema.ts +++ b/packages/ai/src/types/schema.ts @@ -42,6 +42,12 @@ export enum SchemaType { * @public */ export interface SchemaShared { + /** + * An array of {@link Schema}. The generated data must be valid against any of the schemas + * listed in this array. This allows specifying multiple possible structures or types for a + * single field. + */ + anyOf?: T[]; /** Optional. The format of the property. * When using the Gemini Developer API ({@link GoogleAIBackend}), this must be either `'enum'` or * `'date-time'`, otherwise requests will fail. @@ -77,10 +83,10 @@ export interface SchemaParams extends SchemaShared {} */ export interface SchemaRequest extends SchemaShared { /** - * The type of the property. {@link - * SchemaType}. + * The type of the property. this can only be undefined when using `anyof` schemas, + * which do not have an explicit type in the {@link OpenAPI specification | https://swagger.io/docs/specification/v3_0/data-models/data-types/#any-type}. */ - type: SchemaType; + type?: SchemaType; /** Optional. Array of required property. */ required?: string[]; } @@ -91,10 +97,10 @@ export interface SchemaRequest extends SchemaShared { */ export interface SchemaInterface extends SchemaShared { /** - * The type of the property. {@link - * SchemaType}. + * The type of the property. this can only be undefined when using `anyof` schemas, + * which do not have an explicit type in the {@link OpenAPI specification | https://swagger.io/docs/specification/v3_0/data-models/data-types/#any-type}. */ - type: SchemaType; + type?: SchemaType; } /**