diff --git a/.changeset/plenty-bulldogs-complain.md b/.changeset/plenty-bulldogs-complain.md new file mode 100644 index 0000000000..d8583106e6 --- /dev/null +++ b/.changeset/plenty-bulldogs-complain.md @@ -0,0 +1,6 @@ +--- +'@firebase/ai': minor +'firebase': minor +--- + +Added ability to specify thinking levels in `thinkingConfig`. diff --git a/common/api-review/ai.api.md b/common/api-review/ai.api.md index 2bf194fbaf..4d28570404 100644 --- a/common/api-review/ai.api.md +++ b/common/api-review/ai.api.md @@ -1371,8 +1371,20 @@ export interface TextPart { export interface ThinkingConfig { includeThoughts?: boolean; thinkingBudget?: number; + thinkingLevel?: ThinkingLevel; } +// @public +export const ThinkingLevel: { + MINIMAL: string; + LOW: string; + MEDIUM: string; + HIGH: string; +}; + +// @public +export type ThinkingLevel = (typeof ThinkingLevel)[keyof typeof ThinkingLevel]; + // Warning: (ae-incompatible-release-tags) The symbol "Tool" is marked as @public, but its signature references "CodeExecutionTool" which is marked as @beta // Warning: (ae-incompatible-release-tags) The symbol "Tool" is marked as @public, but its signature references "URLContextTool" which is marked as @beta // diff --git a/docs-devsite/ai.md b/docs-devsite/ai.md index 53e4057cad..172f060f14 100644 --- a/docs-devsite/ai.md +++ b/docs-devsite/ai.md @@ -176,6 +176,7 @@ The Firebase AI Web SDK. | [POSSIBLE\_ROLES](./ai.md#possible_roles) | Possible roles. | | [ResponseModality](./ai.md#responsemodality) | (Public Preview) Generation modalities to be returned in generation responses. | | [SchemaType](./ai.md#schematype) | Contains the list of OpenAPI data types as defined by the [OpenAPI specification](https://swagger.io/docs/specification/data-models/data-types/) | +| [ThinkingLevel](./ai.md#thinkinglevel) | A preset that controls the model's "thinking" process. Use ThinkingLevel.LOW for faster responses on less complex tasks, and ThinkingLevel.HIGH for better reasoning on more complex tasks. | | [URLRetrievalStatus](./ai.md#urlretrievalstatus) | (Public Preview) The status of a URL retrieval. | ## Type Aliases @@ -208,6 +209,7 @@ The Firebase AI Web SDK. | [ResponseModality](./ai.md#responsemodality) | (Public Preview) Generation modalities to be returned in generation responses. | | [Role](./ai.md#role) | Role is the producer of the content. | | [SchemaType](./ai.md#schematype) | Contains the list of OpenAPI data types as defined by the [OpenAPI specification](https://swagger.io/docs/specification/data-models/data-types/) | +| [ThinkingLevel](./ai.md#thinkinglevel) | A preset that controls the model's "thinking" process. Use ThinkingLevel.LOW for faster responses on less complex tasks, and ThinkingLevel.HIGH for better reasoning on more complex tasks. | | [Tool](./ai.md#tool) | Defines a tool that model can call to access external knowledge. | | [TypedSchema](./ai.md#typedschema) | A type that includes all specific Schema types. | | [URLRetrievalStatus](./ai.md#urlretrievalstatus) | (Public Preview) The status of a URL retrieval. | @@ -827,6 +829,21 @@ SchemaType: { } ``` +## ThinkingLevel + +A preset that controls the model's "thinking" process. Use `ThinkingLevel.LOW` for faster responses on less complex tasks, and `ThinkingLevel.HIGH` for better reasoning on more complex tasks. + +Signature: + +```typescript +ThinkingLevel: { + MINIMAL: string; + LOW: string; + MEDIUM: string; + HIGH: string; +} +``` + ## URLRetrievalStatus > This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. @@ -1142,6 +1159,16 @@ Contains the list of OpenAPI data types as defined by the [OpenAPI specification export type SchemaType = (typeof SchemaType)[keyof typeof SchemaType]; ``` +## ThinkingLevel + +A preset that controls the model's "thinking" process. Use `ThinkingLevel.LOW` for faster responses on less complex tasks, and `ThinkingLevel.HIGH` for better reasoning on more complex tasks. + +Signature: + +```typescript +export type ThinkingLevel = (typeof ThinkingLevel)[keyof typeof ThinkingLevel]; +``` + ## Tool Defines a tool that model can call to access external knowledge. diff --git a/docs-devsite/ai.thinkingconfig.md b/docs-devsite/ai.thinkingconfig.md index 1ddc1626f4..118aa0d3e3 100644 --- a/docs-devsite/ai.thinkingconfig.md +++ b/docs-devsite/ai.thinkingconfig.md @@ -25,7 +25,8 @@ export interface ThinkingConfig | Property | Type | Description | | --- | --- | --- | | [includeThoughts](./ai.thinkingconfig.md#thinkingconfigincludethoughts) | boolean | Whether to include "thought summaries" in the model's response. | -| [thinkingBudget](./ai.thinkingconfig.md#thinkingconfigthinkingbudget) | number | The thinking budget, in tokens.This parameter sets an upper limit on the number of tokens the model can use for its internal "thinking" process. A higher budget may result in higher quality responses for complex tasks but can also increase latency and cost.If you don't specify a budget, the model will determine the appropriate amount of thinking based on the complexity of the prompt.An error will be thrown if you set a thinking budget for a model that does not support this feature or if the specified budget is not within the model's supported range. | +| [thinkingBudget](./ai.thinkingconfig.md#thinkingconfigthinkingbudget) | number | The thinking budget, in tokens. | +| [thinkingLevel](./ai.thinkingconfig.md#thinkingconfigthinkinglevel) | [ThinkingLevel](./ai.md#thinkinglevel) | If not specified, Gemini will use the model's default dynamic thinking level. | ## ThinkingConfig.includeThoughts @@ -45,12 +46,34 @@ The thinking budget, in tokens. This parameter sets an upper limit on the number of tokens the model can use for its internal "thinking" process. A higher budget may result in higher quality responses for complex tasks but can also increase latency and cost. -If you don't specify a budget, the model will determine the appropriate amount of thinking based on the complexity of the prompt. +The range of supported thinking budget values depends on the model. + + An error will be thrown if you set a thinking budget for a model that does not support this feature or if the specified budget is not within the model's supported range. +The model will also error if `thinkingLevel` and `thinkingBudget` are both set. + Signature: ```typescript thinkingBudget?: number; ``` + +## ThinkingConfig.thinkingLevel + +If not specified, Gemini will use the model's default dynamic thinking level. + +Note: The model will error if `thinkingLevel` and `thinkingBudget` are both set. + +Important: Gemini 2.5 series models do not support thinking levels; use `thinkingBudget` to set a thinking budget instead. + +Signature: + +```typescript +thinkingLevel?: ThinkingLevel; +``` diff --git a/packages/ai/src/models/generative-model.test.ts b/packages/ai/src/models/generative-model.test.ts index 45430cb5f5..794508e6e7 100644 --- a/packages/ai/src/models/generative-model.test.ts +++ b/packages/ai/src/models/generative-model.test.ts @@ -21,7 +21,8 @@ import { AI, InferenceMode, AIErrorCode, - ChromeAdapter + ChromeAdapter, + ThinkingLevel } from '../public-types'; import * as request from '../requests/request'; import { SinonStub, match, restore, stub } from 'sinon'; @@ -53,6 +54,20 @@ const fakeAI: AI = { }; describe('GenerativeModel', () => { + it('throws if generationConfig is invalid', () => { + expect( + () => + new GenerativeModel(fakeAI, { + model: 'my-model', + generationConfig: { + thinkingConfig: { + thinkingBudget: 1000, + thinkingLevel: ThinkingLevel.LOW + } + } + }) + ).to.throw(AIErrorCode.UNSUPPORTED); + }); it('passes params through to generateContent', async () => { const genModel = new GenerativeModel( fakeAI, diff --git a/packages/ai/src/models/generative-model.ts b/packages/ai/src/models/generative-model.ts index ffce645eeb..959e21d9e2 100644 --- a/packages/ai/src/models/generative-model.ts +++ b/packages/ai/src/models/generative-model.ts @@ -41,9 +41,10 @@ import { formatGenerateContentInput, formatSystemInstruction } from '../requests/request-helpers'; -import { AI } from '../public-types'; +import { AI, AIErrorCode } from '../public-types'; import { AIModel } from './ai-model'; import { ChromeAdapter } from '../types/chrome-adapter'; +import { AIError } from '../errors'; /** * Class for generative model APIs. @@ -65,6 +66,7 @@ export class GenerativeModel extends AIModel { ) { super(ai, modelParams.model); this.generationConfig = modelParams.generationConfig || {}; + validateGenerationConfig(this.generationConfig); this.safetySettings = modelParams.safetySettings || []; this.tools = modelParams.tools; this.toolConfig = modelParams.toolConfig; @@ -165,3 +167,20 @@ export class GenerativeModel extends AIModel { ); } } + +/** + * Client-side validation of some common `GenerationConfig` pitfalls, in order + * to save the developer a wasted request. + */ +function validateGenerationConfig(generationConfig: GenerationConfig): void { + if ( + // != allows for null and undefined. 0 is considered "set" by the model + generationConfig.thinkingConfig?.thinkingBudget != null && + generationConfig.thinkingConfig?.thinkingLevel + ) { + throw new AIError( + AIErrorCode.UNSUPPORTED, + `Cannot set both thinkingBudget and thinkingLevel in a config.` + ); + } +} diff --git a/packages/ai/src/types/enums.ts b/packages/ai/src/types/enums.ts index f7c55d5e4c..4e4a45805d 100644 --- a/packages/ai/src/types/enums.ts +++ b/packages/ai/src/types/enums.ts @@ -432,3 +432,26 @@ export const Language = { * @beta */ export type Language = (typeof Language)[keyof typeof Language]; + +/** + * A preset that controls the model's "thinking" process. Use + * `ThinkingLevel.LOW` for faster responses on less complex tasks, and + * `ThinkingLevel.HIGH` for better reasoning on more complex tasks. + * + * @public + */ +export const ThinkingLevel = { + MINIMAL: 'MINIMAL', + LOW: 'LOW', + MEDIUM: 'MEDIUM', + HIGH: 'HIGH' +}; + +/** + * A preset that controls the model's "thinking" process. Use + * `ThinkingLevel.LOW` for faster responses on less complex tasks, and + * `ThinkingLevel.HIGH` for better reasoning on more complex tasks. + * + * @public + */ +export type ThinkingLevel = (typeof ThinkingLevel)[keyof typeof ThinkingLevel]; diff --git a/packages/ai/src/types/requests.ts b/packages/ai/src/types/requests.ts index 6e5d214768..f9b7818e5a 100644 --- a/packages/ai/src/types/requests.ts +++ b/packages/ai/src/types/requests.ts @@ -27,7 +27,8 @@ import { HarmBlockThreshold, HarmCategory, InferenceMode, - ResponseModality + ResponseModality, + ThinkingLevel } from './enums'; import { ObjectSchemaRequest, SchemaRequest } from './schema'; @@ -436,18 +437,44 @@ export interface ThinkingConfig { /** * The thinking budget, in tokens. * + * @remarks * This parameter sets an upper limit on the number of tokens the model can use for its internal * "thinking" process. A higher budget may result in higher quality responses for complex tasks * but can also increase latency and cost. * - * If you don't specify a budget, the model will determine the appropriate amount - * of thinking based on the complexity of the prompt. + * The range of supported thinking budget values depends on the model. + * + * * * An error will be thrown if you set a thinking budget for a model that does not support this * feature or if the specified budget is not within the model's supported range. + * + * The model will also error if `thinkingLevel` and `thinkingBudget` are + * both set. */ thinkingBudget?: number; + /** + * If not specified, Gemini will use the model's default dynamic thinking level. + * + * @remarks + * Note: The model will error if `thinkingLevel` and `thinkingBudget` are + * both set. + * + * Important: Gemini 2.5 series models do not support thinking levels; use + * `thinkingBudget` to set a thinking budget instead. + */ + thinkingLevel?: ThinkingLevel; + /** * Whether to include "thought summaries" in the model's response. *