From a8221b88df6dcb16a9f9597c6277612443f02049 Mon Sep 17 00:00:00 2001 From: Ayush Agrawal Date: Tue, 21 Apr 2026 08:24:46 -0700 Subject: [PATCH] feat: Support Prompt Management in the JS GenAI Modules FUTURE_COPYBARA_INTEGRATE_REVIEW=https://github.com/googleapis/nodejs-agentplatform/pull/650 from googleapis:release-please--branches--main--components--agentplatform 3fc0671db2f8a4c0059ce0b67424cdc4fd109d32 PiperOrigin-RevId: 903243258 --- README.md | 76 ++ src/client.ts | 3 + src/converters/_prompts_converters.ts | 362 ++++++++++ src/prompts.ts | 990 ++++++++++++++++++++++++++ src/types/common.ts | 673 ++++++++++++++++- system_test/prompts_e2e_test.ts | 144 ++++ system_test/prompts_sample_test.ts | 137 ++++ test/replays/prompts_test.ts | 64 ++ 8 files changed, 2429 insertions(+), 20 deletions(-) create mode 100644 src/converters/_prompts_converters.ts create mode 100644 src/prompts.ts create mode 100644 system_test/prompts_e2e_test.ts create mode 100644 system_test/prompts_sample_test.ts create mode 100644 test/replays/prompts_test.ts diff --git a/README.md b/README.md index b3f65db4..5c58bce6 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,82 @@ const client: Client = new Client({ }); ``` +## Prompts + +First define your prompt as a JS object or `Prompt` object. Then call `createVersion`. + +```typescript +const prompt = { + promptData: { + contents: [{ parts: [{ text: 'Hello, {name}! How are you?' }] }], + systemInstruction: { parts: [{ text: 'Please answer in a short sentence.' }] }, + variables: [ + { name: { text: 'Alice' } }, + ], + model: 'gemini-2.5-flash', + }, +}; + +const promptResource = await client.prompts.createVersion({ + prompt: prompt, +}); +``` + +Note that you can also use typed objects to define your prompt. Some of the types used to do this (`Content`, `Part`) are from the Gen AI SDK (`@google/genai`). + +```typescript +import { Prompt } from '@google-cloud/agentplatform'; +import { Part, Content } from '@google/genai'; + +const prompt: Prompt = { + promptData: { + contents: [{ parts: [{ text: 'Hello, {name}! How are you?' } as Part] } as Content], + systemInstruction: { parts: [{ text: 'Please answer in a short sentence.' } as Part] } as Content, + variables: [ + { name: { text: 'Alice' } as Part }, + ], + model: 'gemini-2.5-flash', + }, +}; + +const promptResource = await client.prompts.createVersion({ + prompt: prompt, +}); +``` + +Retrieve a prompt by calling `get()` with the `promptId`. + +```typescript +const dataset = (promptResource as any)._dataset; +const promptId = dataset?.name?.split('/').pop() || 'YOUR_PROMPT_ID'; + +const retrievedPrompt = await client.prompts.get({ + promptId: promptId, +}); +``` + +After creating or retrieving a prompt, you can call `models.generateContent()` with that prompt using the Gen AI SDK (`@google/genai`). + +```typescript +import { GoogleGenAI } from '@google/genai'; + +// Create a Client in the Gen AI SDK +const genaiClient = new GoogleGenAI({ vertexai: true, project: 'your-project', location: 'your-location' }); + +// Call generateContent() with the prompt +if (retrievedPrompt.promptData?.contents) { + const response = await genaiClient.models.generateContent({ + model: retrievedPrompt.promptData.model || 'gemini-2.5-flash', + contents: retrievedPrompt.promptData.contents, + config: { + systemInstruction: retrievedPrompt.promptData.systemInstruction, + ...(retrievedPrompt.promptData.generationConfig || {}), + }, + }); + console.log(response.text); +} +``` + ## License The contents of this repository are licensed under the diff --git a/src/client.ts b/src/client.ts index 6452dc14..67308ee6 100644 --- a/src/client.ts +++ b/src/client.ts @@ -7,6 +7,7 @@ import {ApiClient, NodeAuth, NodeDownloader, NodeUploader,} from '@google/genai/ import {AgentEngines} from './agentengines'; import {Skills} from './skills'; +import {Prompts} from './prompts'; export const SDK_VERSION = '0.9.0'; // x-release-please-version @@ -16,6 +17,7 @@ export class Client { protected readonly apiClient: ApiClient; public readonly _agentEnginesInternal: AgentEngines; public readonly skills: Skills; + public readonly prompts: Prompts; constructor( options: {project?: string; location?: string; apiEndpoint?: string;}) { @@ -61,6 +63,7 @@ export class Client { this._agentEnginesInternal = new AgentEngines(this.apiClient); this.skills = new Skills(this.apiClient); + this.prompts = new Prompts(this.apiClient); } /** diff --git a/src/converters/_prompts_converters.ts b/src/converters/_prompts_converters.ts new file mode 100644 index 00000000..1432129d --- /dev/null +++ b/src/converters/_prompts_converters.ts @@ -0,0 +1,362 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +// Code generated by the Google Gen AI SDK generator DO NOT EDIT. + +import * as common from '@google/genai/vertex_internal'; +import * as types from '../types.js'; + +export function createDatasetParametersToVertex( + fromObject: types.CreateDatasetParameters, +): Record { + const toObject: Record = {}; + + const fromName = common.getValueByPath(fromObject, ['name']); + if (fromName != null) { + common.setValueByPath(toObject, ['name'], fromName); + } + + const fromDisplayName = common.getValueByPath(fromObject, ['displayName']); + if (fromDisplayName != null) { + common.setValueByPath(toObject, ['displayName'], fromDisplayName); + } + + const fromMetadataSchemaUri = common.getValueByPath(fromObject, [ + 'metadataSchemaUri', + ]); + if (fromMetadataSchemaUri != null) { + common.setValueByPath( + toObject, + ['metadataSchemaUri'], + fromMetadataSchemaUri, + ); + } + + const fromMetadata = common.getValueByPath(fromObject, ['metadata']); + if (fromMetadata != null) { + common.setValueByPath(toObject, ['metadata'], fromMetadata); + } + + const fromDescription = common.getValueByPath(fromObject, ['description']); + if (fromDescription != null) { + common.setValueByPath(toObject, ['description'], fromDescription); + } + + const fromEncryptionSpec = common.getValueByPath(fromObject, [ + 'encryptionSpec', + ]); + if (fromEncryptionSpec != null) { + common.setValueByPath(toObject, ['encryptionSpec'], fromEncryptionSpec); + } + + const fromModelReference = common.getValueByPath(fromObject, [ + 'modelReference', + ]); + if (fromModelReference != null) { + common.setValueByPath(toObject, ['modelReference'], fromModelReference); + } + + const fromConfig = common.getValueByPath(fromObject, ['config']); + if (fromConfig != null) { + common.setValueByPath(toObject, ['config'], fromConfig); + } + + return toObject; +} + +export function createDatasetVersionParametersToVertex( + fromObject: types.CreateDatasetVersionParameters, +): Record { + const toObject: Record = {}; + + const fromDatasetName = common.getValueByPath(fromObject, ['datasetName']); + if (fromDatasetName != null) { + common.setValueByPath(toObject, ['_url', 'name'], fromDatasetName); + } + + const fromMetadata = common.getValueByPath(fromObject, ['metadata']); + if (fromMetadata != null) { + common.setValueByPath(toObject, ['metadata'], fromMetadata); + } + + const fromModelReference = common.getValueByPath(fromObject, [ + 'modelReference', + ]); + if (fromModelReference != null) { + common.setValueByPath(toObject, ['modelReference'], fromModelReference); + } + + const fromParent = common.getValueByPath(fromObject, ['parent']); + if (fromParent != null) { + common.setValueByPath(toObject, ['parent'], fromParent); + } + + const fromDisplayName = common.getValueByPath(fromObject, ['displayName']); + if (fromDisplayName != null) { + common.setValueByPath(toObject, ['displayName'], fromDisplayName); + } + + const fromConfig = common.getValueByPath(fromObject, ['config']); + if (fromConfig != null) { + common.setValueByPath(toObject, ['config'], fromConfig); + } + + return toObject; +} + +export function deleteDatasetRequestParametersToVertex( + fromObject: types.DeleteDatasetRequestParameters, +): Record { + const toObject: Record = {}; + + const fromPromptId = common.getValueByPath(fromObject, ['promptId']); + if (fromPromptId != null) { + common.setValueByPath(toObject, ['_url', 'dataset_id'], fromPromptId); + } + + const fromConfig = common.getValueByPath(fromObject, ['config']); + if (fromConfig != null) { + common.setValueByPath(toObject, ['config'], fromConfig); + } + + return toObject; +} + +export function deletePromptVersionRequestParametersToVertex( + fromObject: types.DeletePromptVersionRequestParameters, +): Record { + const toObject: Record = {}; + + const fromPromptId = common.getValueByPath(fromObject, ['promptId']); + if (fromPromptId != null) { + common.setValueByPath(toObject, ['_url', 'dataset_id'], fromPromptId); + } + + const fromVersionId = common.getValueByPath(fromObject, ['versionId']); + if (fromVersionId != null) { + common.setValueByPath(toObject, ['_url', 'version_id'], fromVersionId); + } + + const fromConfig = common.getValueByPath(fromObject, ['config']); + if (fromConfig != null) { + common.setValueByPath(toObject, ['config'], fromConfig); + } + + return toObject; +} + +export function getDatasetOperationParametersToVertex( + fromObject: types.GetDatasetOperationParameters, +): Record { + const toObject: Record = {}; + + const fromDatasetId = common.getValueByPath(fromObject, ['datasetId']); + if (fromDatasetId != null) { + common.setValueByPath(toObject, ['_url', 'dataset_id'], fromDatasetId); + } + + const fromOperationId = common.getValueByPath(fromObject, ['operationId']); + if (fromOperationId != null) { + common.setValueByPath(toObject, ['_url', 'operation_id'], fromOperationId); + } + + const fromConfig = common.getValueByPath(fromObject, ['config']); + if (fromConfig != null) { + common.setValueByPath(toObject, ['config'], fromConfig); + } + + return toObject; +} + +export function getDatasetParametersToVertex( + fromObject: types.GetDatasetParameters, +): Record { + const toObject: Record = {}; + + const fromName = common.getValueByPath(fromObject, ['name']); + if (fromName != null) { + common.setValueByPath(toObject, ['_url', 'name'], fromName); + } + + const fromConfig = common.getValueByPath(fromObject, ['config']); + if (fromConfig != null) { + common.setValueByPath(toObject, ['config'], fromConfig); + } + + return toObject; +} + +export function getDatasetVersionParametersToVertex( + fromObject: types.GetDatasetVersionParameters, +): Record { + const toObject: Record = {}; + + const fromDatasetId = common.getValueByPath(fromObject, ['datasetId']); + if (fromDatasetId != null) { + common.setValueByPath(toObject, ['_url', 'dataset_id'], fromDatasetId); + } + + const fromDatasetVersionId = common.getValueByPath(fromObject, [ + 'datasetVersionId', + ]); + if (fromDatasetVersionId != null) { + common.setValueByPath( + toObject, + ['_url', 'dataset_version_id'], + fromDatasetVersionId, + ); + } + + const fromConfig = common.getValueByPath(fromObject, ['config']); + if (fromConfig != null) { + common.setValueByPath(toObject, ['config'], fromConfig); + } + + return toObject; +} + +export function listDatasetVersionsRequestParametersToVertex( + fromObject: types.ListDatasetVersionsRequestParameters, +): Record { + const toObject: Record = {}; + + const fromReadMask = common.getValueByPath(fromObject, ['readMask']); + if (fromReadMask != null) { + common.setValueByPath(toObject, ['_url', 'read_mask'], fromReadMask); + } + + const fromDatasetId = common.getValueByPath(fromObject, ['datasetId']); + if (fromDatasetId != null) { + common.setValueByPath(toObject, ['_url', 'dataset_id'], fromDatasetId); + } + + const fromConfig = common.getValueByPath(fromObject, ['config']); + if (fromConfig != null) { + common.setValueByPath( + toObject, + ['config'], + listPromptsConfigToVertex(fromConfig, toObject), + ); + } + + return toObject; +} + +export function listDatasetsRequestParametersToVertex( + fromObject: types.ListDatasetsRequestParameters, +): Record { + const toObject: Record = {}; + + const fromConfig = common.getValueByPath(fromObject, ['config']); + if (fromConfig != null) { + common.setValueByPath( + toObject, + ['config'], + listPromptsConfigToVertex(fromConfig, toObject), + ); + } + + return toObject; +} + +export function listPromptsConfigToVertex( + fromObject: types.ListPromptsConfig, + parentObject: Record, +): Record { + const toObject: Record = {}; + + const fromPageSize = common.getValueByPath(fromObject, ['pageSize']); + if (parentObject !== undefined && fromPageSize != null) { + common.setValueByPath(parentObject, ['_query', 'pageSize'], fromPageSize); + } + + const fromPageToken = common.getValueByPath(fromObject, ['pageToken']); + if (parentObject !== undefined && fromPageToken != null) { + common.setValueByPath(parentObject, ['_query', 'pageToken'], fromPageToken); + } + + const fromFilter = common.getValueByPath(fromObject, ['filter']); + if (parentObject !== undefined && fromFilter != null) { + common.setValueByPath(parentObject, ['_query', 'filter'], fromFilter); + } + + return toObject; +} + +export function restoreVersionRequestParametersToVertex( + fromObject: types.RestoreVersionRequestParameters, +): Record { + const toObject: Record = {}; + + const fromDatasetId = common.getValueByPath(fromObject, ['datasetId']); + if (fromDatasetId != null) { + common.setValueByPath(toObject, ['_url', 'dataset_id'], fromDatasetId); + } + + const fromVersionId = common.getValueByPath(fromObject, ['versionId']); + if (fromVersionId != null) { + common.setValueByPath(toObject, ['_url', 'version_id'], fromVersionId); + } + + const fromConfig = common.getValueByPath(fromObject, ['config']); + if (fromConfig != null) { + common.setValueByPath(toObject, ['config'], fromConfig); + } + + return toObject; +} + +export function updateDatasetParametersToVertex( + fromObject: types.UpdateDatasetParameters, +): Record { + const toObject: Record = {}; + + const fromName = common.getValueByPath(fromObject, ['name']); + if (fromName != null) { + common.setValueByPath(toObject, ['name'], fromName); + } + + const fromDatasetId = common.getValueByPath(fromObject, ['datasetId']); + if (fromDatasetId != null) { + common.setValueByPath(toObject, ['_url', 'dataset_id'], fromDatasetId); + } + + const fromDisplayName = common.getValueByPath(fromObject, ['displayName']); + if (fromDisplayName != null) { + common.setValueByPath(toObject, ['displayName'], fromDisplayName); + } + + const fromMetadata = common.getValueByPath(fromObject, ['metadata']); + if (fromMetadata != null) { + common.setValueByPath(toObject, ['metadata'], fromMetadata); + } + + const fromDescription = common.getValueByPath(fromObject, ['description']); + if (fromDescription != null) { + common.setValueByPath(toObject, ['description'], fromDescription); + } + + const fromEncryptionSpec = common.getValueByPath(fromObject, [ + 'encryptionSpec', + ]); + if (fromEncryptionSpec != null) { + common.setValueByPath(toObject, ['encryptionSpec'], fromEncryptionSpec); + } + + const fromModelReference = common.getValueByPath(fromObject, [ + 'modelReference', + ]); + if (fromModelReference != null) { + common.setValueByPath(toObject, ['modelReference'], fromModelReference); + } + + const fromConfig = common.getValueByPath(fromObject, ['config']); + if (fromConfig != null) { + common.setValueByPath(toObject, ['config'], fromConfig); + } + + return toObject; +} diff --git a/src/prompts.ts b/src/prompts.ts new file mode 100644 index 00000000..0599beb5 --- /dev/null +++ b/src/prompts.ts @@ -0,0 +1,990 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +// Code generated by the Google Gen AI SDK generator DO NOT EDIT. + +import * as common from '@google/genai/vertex_internal'; +import {ApiClient, BaseModule} from '@google/genai/vertex_internal'; +import * as converters from './converters/_prompts_converters.js'; +import * as types from './types.js'; + +export class Prompts extends BaseModule { + constructor(private readonly apiClient: ApiClient) { + super(); + } + + /* eslint-disable @typescript-eslint/no-explicit-any */ + private async _waitForOperation( + operation: any, + timeout = 90, + ): Promise { + let done = false; + let promptDatasetOperation: any; + + const responseOperationName = operation?.name; + if (!responseOperationName) { + throw new Error('Invalid operation name.'); + } + + const parts = responseOperationName.split('/datasets/'); + if (parts.length < 2) { + throw new Error( + `Unexpected operation name format: ${responseOperationName}`, + ); + } + const datasetId = parts[1].split('/')[0]; + const operationId = responseOperationName.split('/').pop(); + + if (!datasetId || !operationId) { + throw new Error('Invalid operation name.'); + } + + const startTime = Date.now(); + let sleepDuration = 5000; // ms + const maxWaitTime = 60000; // ms + + while (!done) { + if (Date.now() - startTime > timeout * 1000) { + throw new Error( + `Operation did not complete within the specified timeout of ${timeout} seconds.`, + ); + } + await new Promise((resolve) => setTimeout(resolve, sleepDuration)); + sleepDuration = Math.min(sleepDuration * 2, maxWaitTime); + + promptDatasetOperation = await this.getDatasetOperationInternal({ + datasetId, + operationId, + }); + + done = promptDatasetOperation?.done || false; + } + + if (promptDatasetOperation?.error) { + throw new Error( + `Error in dataset operation: ${JSON.stringify(promptDatasetOperation.error)}`, + ); + } + if (!promptDatasetOperation?.response?.['name']) { + throw new Error('Error in dataset operation: response name not found.'); + } + return promptDatasetOperation.response['name'] as string; + } + + private async _waitForProjectOperation( + operation: any, + timeout = 90, + ): Promise { + let done = false; + const startTime = Date.now(); + let sleepDuration = 5000; // ms + const maxWaitTime = 60000; // ms + + if (!operation?.name) { + throw new Error('Invalid operation name.'); + } + + while (!done) { + if (Date.now() - startTime > timeout * 1000) { + throw new Error( + `Operation did not complete within the specified timeout of ${timeout} seconds.`, + ); + } + await new Promise((resolve) => setTimeout(resolve, sleepDuration)); + sleepDuration = Math.min(sleepDuration * 2, maxWaitTime); + + const operationId = operation.name.split('/').pop(); + if (!operationId) { + throw new Error('Invalid operation name.'); + } + const response = await this.apiClient.request({ + path: `operations/${operationId}`, + httpMethod: 'GET', + }); + const opStatus = (await response.json()) as any; + done = opStatus?.done || false; + + if (opStatus?.error) { + throw new Error( + `Error in operation: ${JSON.stringify(opStatus.error)}`, + ); + } + } + } + + private _raiseForInvalidPrompt(prompt: any): void { + if (!prompt || !prompt.promptData) { + throw new Error('Prompt data must be provided.'); + } + if ( + !prompt.promptData.contents || + prompt.promptData.contents.length === 0 + ) { + throw new Error('Prompt contents must be provided.'); + } + if (!prompt.promptData.model) { + throw new Error('Model name must be provided.'); + } + if (prompt.promptData.contents && prompt.promptData.contents.length > 1) { + throw new Error('Multi-turn prompts are not currently supported.'); + } + } + + private _createDatasetMetadataFromPrompt( + prompt: any, + variables?: Record[], + ): any { + const promptMetadata: any = {}; + const promptApiSchema: any = {}; + + promptApiSchema.multimodalPrompt = { + promptMessage: prompt.promptData, + }; + promptApiSchema.apiSchemaVersion = '1.0.0'; + promptMetadata.hasPromptVariable = Boolean( + variables && variables.length > 0, + ); + + if (variables && variables.length > 0) { + const promptExecutionList: any[] = []; + for (const promptVar of variables) { + const promptInstanceExecution: any = {arguments: {}}; + for (const [key, val] of Object.entries(promptVar)) { + promptInstanceExecution.arguments[key] = { + partList: {parts: [val]}, + }; + } + promptExecutionList.push(promptInstanceExecution); + } + promptApiSchema.executions = promptExecutionList; + } + + if (promptApiSchema.multimodalPrompt.promptMessage) { + const promptMessage = {...promptApiSchema.multimodalPrompt.promptMessage}; + delete promptMessage.variables; + promptApiSchema.multimodalPrompt.promptMessage = promptMessage; + } + + promptMetadata.promptApiSchema = promptApiSchema; + promptMetadata.promptType = 'multimodal_freeform'; + + return promptMetadata; + } + + private _createPromptFromDatasetMetadata(dataset: any): any { + const prompt: any = {}; + if (!dataset?.metadata?.promptApiSchema) { + return prompt; + } + const apiSchema = dataset.metadata.promptApiSchema; + + if (apiSchema.multimodalPrompt?.promptMessage) { + prompt.promptData = {...apiSchema.multimodalPrompt.promptMessage}; + } + + if (apiSchema.executions) { + const variables: Record[] = []; + for (const execution of apiSchema.executions) { + const promptVar: Record = {}; + if (execution.arguments) { + for (const [key, val] of Object.entries(execution.arguments)) { + if (val && (val as any).partList?.parts) { + promptVar[key] = (val as any).partList.parts[0]; + } + } + } + variables.push(promptVar); + } + if (prompt.promptData) { + prompt.promptData.variables = variables; + } + } + + if (dataset.modelReference) { + if (!prompt.promptData) { + prompt.promptData = {}; + } + prompt.promptData.model = dataset.modelReference; + } + + return prompt; + } + + async createVersion(params: { + prompt: types.Prompt; + config?: types.CreatePromptVersionConfig; + }): Promise { + const {prompt, config} = params; + this._raiseForInvalidPrompt(prompt); + + const promptMetadata = this._createDatasetMetadataFromPrompt( + prompt, + prompt.promptData?.variables, + ); + + const project = this.apiClient.clientOptions.project; + const location = this.apiClient.clientOptions.location; + if (!project || !location) { + throw new Error( + 'Project and location must be specified in client options.', + ); + } + + const createPromptDatasetOperation = + await this.createDatasetResourceInternal({ + name: `projects/${project}/locations/${location}`, + displayName: config?.promptDisplayName || `prompt_${Date.now()}`, + metadataSchemaUri: + 'gs://google-cloud-aiplatform/schema/dataset/metadata/text_prompt_1.0.0.yaml', + metadata: promptMetadata, + modelReference: prompt.promptData?.model, + encryptionSpec: config?.encryptionSpec, + config, + }); + + const datasetResourceName = await this._waitForOperation( + createPromptDatasetOperation, + config?.timeout || 90, + ); + + const datasetId = datasetResourceName.split('/').pop(); + if (!datasetId) { + throw new Error('Invalid dataset resource name.'); + } + + const datasetResource = await this.getDatasetResourceInternal({ + name: datasetId, + }); + + const createDatasetVersionOperation = + await this.createDatasetVersionResourceInternal({ + datasetName: datasetId, + displayName: + config?.versionDisplayName || `prompt_version_${Date.now()}`, + config, + }); + + const datasetVersionResourceName = await this._waitForOperation( + createDatasetVersionOperation, + config?.timeout || 90, + ); + + const versionId = datasetVersionResourceName.split('/').pop(); + if (!versionId) { + throw new Error('Invalid dataset version resource name.'); + } + + const datasetVersionResource = await this.getDatasetVersionResourceInternal( + { + datasetId, + datasetVersionId: versionId, + }, + ); + + const updatedPrompt = this._createPromptFromDatasetMetadata( + datasetVersionResource, + ); + (updatedPrompt as any)._dataset = datasetResource; + (updatedPrompt as any)._dataset_version = datasetVersionResource; + + return updatedPrompt; + } + + async get(params: { + promptId: string; + config?: types.GetPromptConfig; + }): Promise { + const {promptId, config} = params; + const promptDatasetResource = await this.getDatasetResourceInternal({ + name: promptId, + config, + }); + const prompt = this._createPromptFromDatasetMetadata(promptDatasetResource); + (prompt as any)._dataset = promptDatasetResource; + return prompt; + } + + async getVersion(params: { + promptId: string; + versionId: string; + config?: types.GetPromptConfig; + }): Promise { + const {promptId, versionId, config} = params; + const promptDatasetResource = await this.getDatasetResourceInternal({ + name: promptId, + config, + }); + const prompt = this._createPromptFromDatasetMetadata(promptDatasetResource); + (prompt as any)._dataset = promptDatasetResource; + + const promptVersionResource = await this.getDatasetVersionResourceInternal({ + datasetId: promptId, + datasetVersionId: versionId, + config, + }); + (prompt as any)._dataset_version = promptVersionResource; + return prompt; + } + + async list( + params: {config?: types.ListPromptsConfig} = {}, + ): Promise { + let config: any = params.config ? {...params.config} : {}; + let response = await this.listPromptsInternal({config}); + const promptRefs: types.PromptRef[] = []; + let hasNextPage = true; + while (hasNextPage) { + if (response.datasets) { + for (const dataset of response.datasets) { + if (dataset.name) { + promptRefs.push({ + model: dataset.modelReference, + promptId: dataset.name.split('/').pop(), + }); + } + } + } + if (!response.nextPageToken) { + hasNextPage = false; + break; + } + config = {...config, pageToken: response.nextPageToken}; + response = await this.listPromptsInternal({config}); + } + return promptRefs; + } + + async listVersions(params: { + promptId: string; + config?: types.ListPromptsConfig; + }): Promise { + const {promptId} = params; + let config: any = params.config ? {...params.config} : {}; + let response = await this.listVersionsInternal({ + datasetId: promptId, + config, + }); + const versionRefs: types.PromptVersionRef[] = []; + let hasNextPage = true; + while (hasNextPage) { + if (response.datasetVersions) { + for (const datasetVersion of response.datasetVersions) { + if (datasetVersion.name) { + versionRefs.push({ + model: datasetVersion.modelReference, + versionId: datasetVersion.name.split('/').pop(), + promptId, + }); + } + } + } + if (!response.nextPageToken) { + hasNextPage = false; + break; + } + config = {...config, pageToken: response.nextPageToken}; + response = await this.listVersionsInternal({ + datasetId: promptId, + config, + }); + } + return versionRefs; + } + + async delete(params: { + promptId: string; + config?: types.DeletePromptConfig; + }): Promise { + const {promptId, config} = params; + const deletePromptOperation = await this.deleteDatasetInternal({ + promptId, + config, + }); + await this._waitForProjectOperation( + deletePromptOperation, + config?.timeout || 90, + ); + } + + async deleteVersion(params: { + promptId: string; + versionId: string; + config?: types.DeletePromptConfig; + }): Promise { + const {promptId, versionId, config} = params; + const deleteVersionOperation = await this.deleteDatasetVersionInternal({ + promptId, + versionId, + config, + }); + await this._waitForProjectOperation( + deleteVersionOperation, + config?.timeout || 90, + ); + } + + async restoreVersion(params: { + promptId: string; + versionId: string; + config?: types.RestoreVersionConfig; + }): Promise { + const {promptId, versionId, config} = params; + const restorePromptOperation = await this.restoreVersionInternal({ + datasetId: promptId, + versionId, + config, + }); + await this._waitForProjectOperation(restorePromptOperation, 90); + const datasetVersionResource = await this.getDatasetVersionResourceInternal( + { + datasetId: promptId, + datasetVersionId: versionId, + }, + ); + const updatedPrompt = this._createPromptFromDatasetMetadata( + datasetVersionResource, + ); + (updatedPrompt as any)._dataset_version = datasetVersionResource; + return updatedPrompt; + } + + async update(params: { + promptId: string; + prompt: types.Prompt; + config?: types.UpdatePromptConfig; + }): Promise { + const {promptId, prompt, config} = params; + this._raiseForInvalidPrompt(prompt); + + if (!prompt.promptData) { + throw new Error('Prompt data is required to update a prompt.'); + } + + const promptMetadata = this._createDatasetMetadataFromPrompt( + prompt, + prompt.promptData.variables, + ); + + const project = this.apiClient.clientOptions.project; + const location = this.apiClient.clientOptions.location; + if (!project || !location) { + throw new Error( + 'Project and location must be specified in client options.', + ); + } + + const updatedDatasetResource = await this.updateDatasetResourceInternal({ + name: `projects/${project}/locations/${location}`, + datasetId: promptId, + displayName: config?.promptDisplayName, + metadata: promptMetadata, + modelReference: prompt.promptData.model, + encryptionSpec: config?.encryptionSpec, + config, + }); + + if (!updatedDatasetResource.name) { + throw new Error( + 'Failed to update dataset resource: resource name missing.', + ); + } + + const datasetId = updatedDatasetResource.name.split('/').pop(); + if (!datasetId) { + throw new Error('Failed to parse dataset ID from updated resource.'); + } + + const createDatasetVersionOperation = + await this.createDatasetVersionResourceInternal({ + datasetName: datasetId, + displayName: + config?.versionDisplayName || `prompt_version_${Date.now()}`, + config, + }); + + const datasetVersionResourceName = await this._waitForOperation( + createDatasetVersionOperation, + config?.timeout || 90, + ); + + const versionId = datasetVersionResourceName.split('/').pop(); + if (!versionId) { + throw new Error('Failed to parse version ID created during update.'); + } + + const datasetVersionResource = await this.getDatasetVersionResourceInternal( + { + datasetId, + datasetVersionId: versionId, + }, + ); + + const updatedPrompt = this._createPromptFromDatasetMetadata( + datasetVersionResource, + ); + (updatedPrompt as any)._dataset = updatedDatasetResource; + (updatedPrompt as any)._dataset_version = datasetVersionResource; + + return updatedPrompt; + } + + private async createDatasetResourceInternal( + params: types.CreateDatasetParameters, + ): Promise { + let response: Promise; + + let path: string = ''; + let queryParams: Record = {}; + if (this.apiClient.isVertexAI()) { + const body = converters.createDatasetParametersToVertex(params); + path = common.formatMap( + 'datasets', + body['_url'] as Record, + ); + queryParams = body['_query'] as Record; + delete body['_url']; + delete body['_query']; + delete body['config']; + + response = this.apiClient + .request({ + path: path, + queryParams: queryParams, + body: JSON.stringify(body), + httpMethod: 'POST', + httpOptions: params.config?.httpOptions, + abortSignal: params.config?.abortSignal, + }) + .then((httpResponse) => { + return httpResponse.json(); + }) as Promise; + + return response.then((resp) => { + return resp as types.DatasetOperation; + }); + } else { + throw new Error( + 'This method is only supported by the Gemini Enterprise Agent Platform (previously known as Vertex AI).', + ); + } + } + + private async createDatasetVersionResourceInternal( + params: types.CreateDatasetVersionParameters, + ): Promise { + let response: Promise; + + let path: string = ''; + let queryParams: Record = {}; + if (this.apiClient.isVertexAI()) { + const body = converters.createDatasetVersionParametersToVertex(params); + path = common.formatMap( + 'datasets/{name}/datasetVersions', + body['_url'] as Record, + ); + queryParams = body['_query'] as Record; + delete body['_url']; + delete body['_query']; + delete body['config']; + + response = this.apiClient + .request({ + path: path, + queryParams: queryParams, + body: JSON.stringify(body), + httpMethod: 'POST', + httpOptions: params.config?.httpOptions, + abortSignal: params.config?.abortSignal, + }) + .then((httpResponse) => { + return httpResponse.json(); + }) as Promise; + + return response.then((resp) => { + return resp as types.DatasetOperation; + }); + } else { + throw new Error( + 'This method is only supported by the Gemini Enterprise Agent Platform (previously known as Vertex AI).', + ); + } + } + + private async getDatasetResourceInternal( + params: types.GetDatasetParameters, + ): Promise { + let response: Promise; + + let path: string = ''; + let queryParams: Record = {}; + if (this.apiClient.isVertexAI()) { + const body = converters.getDatasetParametersToVertex(params); + path = common.formatMap( + 'datasets/{name}', + body['_url'] as Record, + ); + queryParams = body['_query'] as Record; + delete body['_url']; + delete body['_query']; + delete body['config']; + + response = this.apiClient + .request({ + path: path, + queryParams: queryParams, + body: JSON.stringify(body), + httpMethod: 'GET', + httpOptions: params.config?.httpOptions, + abortSignal: params.config?.abortSignal, + }) + .then((httpResponse) => { + return httpResponse.json(); + }) as Promise; + + return response.then((resp) => { + return resp as types.Dataset; + }); + } else { + throw new Error( + 'This method is only supported by the Gemini Enterprise Agent Platform (previously known as Vertex AI).', + ); + } + } + + private async getDatasetVersionResourceInternal( + params: types.GetDatasetVersionParameters, + ): Promise { + let response: Promise; + + let path: string = ''; + let queryParams: Record = {}; + if (this.apiClient.isVertexAI()) { + const body = converters.getDatasetVersionParametersToVertex(params); + path = common.formatMap( + 'datasets/{dataset_id}/datasetVersions/{dataset_version_id}', + body['_url'] as Record, + ); + queryParams = body['_query'] as Record; + delete body['_url']; + delete body['_query']; + delete body['config']; + + response = this.apiClient + .request({ + path: path, + queryParams: queryParams, + body: JSON.stringify(body), + httpMethod: 'GET', + httpOptions: params.config?.httpOptions, + abortSignal: params.config?.abortSignal, + }) + .then((httpResponse) => { + return httpResponse.json(); + }) as Promise; + + return response.then((resp) => { + return resp as types.DatasetVersion; + }); + } else { + throw new Error( + 'This method is only supported by the Gemini Enterprise Agent Platform (previously known as Vertex AI).', + ); + } + } + + private async getDatasetOperationInternal( + params: types.GetDatasetOperationParameters, + ): Promise { + let response: Promise; + + let path: string = ''; + let queryParams: Record = {}; + if (this.apiClient.isVertexAI()) { + const body = converters.getDatasetOperationParametersToVertex(params); + path = common.formatMap( + 'datasets/{dataset_id}/operations/{operation_id}', + body['_url'] as Record, + ); + queryParams = body['_query'] as Record; + delete body['_url']; + delete body['_query']; + delete body['config']; + + response = this.apiClient + .request({ + path: path, + queryParams: queryParams, + body: JSON.stringify(body), + httpMethod: 'GET', + httpOptions: params.config?.httpOptions, + abortSignal: params.config?.abortSignal, + }) + .then((httpResponse) => { + return httpResponse.json(); + }) as Promise; + + return response.then((resp) => { + return resp as types.DatasetOperation; + }); + } else { + throw new Error( + 'This method is only supported by the Gemini Enterprise Agent Platform (previously known as Vertex AI).', + ); + } + } + + private async listPromptsInternal( + params: types.ListDatasetsRequestParameters, + ): Promise { + let response: Promise; + + let path: string = ''; + let queryParams: Record = {}; + if (this.apiClient.isVertexAI()) { + const body = converters.listDatasetsRequestParametersToVertex(params); + path = common.formatMap( + 'datasets', + body['_url'] as Record, + ); + queryParams = body['_query'] as Record; + delete body['_url']; + delete body['_query']; + delete body['config']; + + response = this.apiClient + .request({ + path: path, + queryParams: queryParams, + body: JSON.stringify(body), + httpMethod: 'GET', + httpOptions: params.config?.httpOptions, + abortSignal: params.config?.abortSignal, + }) + .then((httpResponse) => { + return httpResponse.json(); + }) as Promise; + + return response.then((resp) => { + const typedResp = new types.ListDatasetsResponse(); + Object.assign(typedResp, resp); + return typedResp; + }); + } else { + throw new Error( + 'This method is only supported by the Gemini Enterprise Agent Platform (previously known as Vertex AI).', + ); + } + } + + private async listVersionsInternal( + params: types.ListDatasetVersionsRequestParameters, + ): Promise { + let response: Promise; + + let path: string = ''; + let queryParams: Record = {}; + if (this.apiClient.isVertexAI()) { + const body = + converters.listDatasetVersionsRequestParametersToVertex(params); + path = common.formatMap( + 'datasets/{dataset_id}/datasetVersions', + body['_url'] as Record, + ); + queryParams = body['_query'] as Record; + delete body['_url']; + delete body['_query']; + delete body['config']; + + response = this.apiClient + .request({ + path: path, + queryParams: queryParams, + body: JSON.stringify(body), + httpMethod: 'GET', + httpOptions: params.config?.httpOptions, + abortSignal: params.config?.abortSignal, + }) + .then((httpResponse) => { + return httpResponse.json(); + }) as Promise; + + return response.then((resp) => { + const typedResp = new types.ListDatasetVersionsResponse(); + Object.assign(typedResp, resp); + return typedResp; + }); + } else { + throw new Error( + 'This method is only supported by the Gemini Enterprise Agent Platform (previously known as Vertex AI).', + ); + } + } + + private async deleteDatasetInternal( + params: types.DeleteDatasetRequestParameters, + ): Promise { + let response: Promise; + + let path: string = ''; + let queryParams: Record = {}; + if (this.apiClient.isVertexAI()) { + const body = converters.deleteDatasetRequestParametersToVertex(params); + path = common.formatMap( + 'datasets/{dataset_id}', + body['_url'] as Record, + ); + queryParams = body['_query'] as Record; + delete body['_url']; + delete body['_query']; + delete body['config']; + + response = this.apiClient + .request({ + path: path, + queryParams: queryParams, + body: JSON.stringify(body), + httpMethod: 'DELETE', + httpOptions: params.config?.httpOptions, + abortSignal: params.config?.abortSignal, + }) + .then((httpResponse) => { + return httpResponse.json(); + }) as Promise; + + return response.then((resp) => { + return resp as types.DeletePromptOperation; + }); + } else { + throw new Error( + 'This method is only supported by the Gemini Enterprise Agent Platform (previously known as Vertex AI).', + ); + } + } + + private async deleteDatasetVersionInternal( + params: types.DeletePromptVersionRequestParameters, + ): Promise { + let response: Promise; + + let path: string = ''; + let queryParams: Record = {}; + if (this.apiClient.isVertexAI()) { + const body = + converters.deletePromptVersionRequestParametersToVertex(params); + path = common.formatMap( + 'datasets/{dataset_id}/datasetVersions/{version_id}', + body['_url'] as Record, + ); + queryParams = body['_query'] as Record; + delete body['_url']; + delete body['_query']; + delete body['config']; + + response = this.apiClient + .request({ + path: path, + queryParams: queryParams, + body: JSON.stringify(body), + httpMethod: 'DELETE', + httpOptions: params.config?.httpOptions, + abortSignal: params.config?.abortSignal, + }) + .then((httpResponse) => { + return httpResponse.json(); + }) as Promise; + + return response.then((resp) => { + return resp as types.DeletePromptVersionOperation; + }); + } else { + throw new Error( + 'This method is only supported by the Gemini Enterprise Agent Platform (previously known as Vertex AI).', + ); + } + } + + private async restoreVersionInternal( + params: types.RestoreVersionRequestParameters, + ): Promise { + let response: Promise; + + let path: string = ''; + let queryParams: Record = {}; + if (this.apiClient.isVertexAI()) { + const body = converters.restoreVersionRequestParametersToVertex(params); + path = common.formatMap( + 'datasets/{dataset_id}/datasetVersions/{version_id}:restore', + body['_url'] as Record, + ); + queryParams = body['_query'] as Record; + delete body['_url']; + delete body['_query']; + delete body['config']; + + response = this.apiClient + .request({ + path: path, + queryParams: queryParams, + body: JSON.stringify(body), + httpMethod: 'GET', + httpOptions: params.config?.httpOptions, + abortSignal: params.config?.abortSignal, + }) + .then((httpResponse) => { + return httpResponse.json(); + }) as Promise; + + return response.then((resp) => { + return resp as types.RestoreVersionOperation; + }); + } else { + throw new Error( + 'This method is only supported by the Gemini Enterprise Agent Platform (previously known as Vertex AI).', + ); + } + } + + private async updateDatasetResourceInternal( + params: types.UpdateDatasetParameters, + ): Promise { + let response: Promise; + + let path: string = ''; + let queryParams: Record = {}; + if (this.apiClient.isVertexAI()) { + const body = converters.updateDatasetParametersToVertex(params); + path = common.formatMap( + 'datasets/{dataset_id}', + body['_url'] as Record, + ); + queryParams = body['_query'] as Record; + delete body['_url']; + delete body['_query']; + delete body['config']; + + response = this.apiClient + .request({ + path: path, + queryParams: queryParams, + body: JSON.stringify(body), + httpMethod: 'PATCH', + httpOptions: params.config?.httpOptions, + abortSignal: params.config?.abortSignal, + }) + .then((httpResponse) => { + return httpResponse.json(); + }) as Promise; + + return response.then((resp) => { + return resp as types.Dataset; + }); + } else { + throw new Error( + 'This method is only supported by the Gemini Enterprise Agent Platform (previously known as Vertex AI).', + ); + } + } +} diff --git a/src/types/common.ts b/src/types/common.ts index 0eecdf05..1ae2e68b 100644 --- a/src/types/common.ts +++ b/src/types/common.ts @@ -2873,6 +2873,581 @@ export class ListSkillRevisionsResponse { skillRevisions?: SkillRevision[]; } +/** Single source entry for the grounding checking. */ +export declare interface SchemaPredictParamsGroundingConfigSourceEntry { + /** The uri of the Vertex AI Search data source. Deprecated. Use vertex_ai_search_datastore instead. */ + enterpriseDatastore?: string; + /** The grounding text passed inline with the Predict API. It can support up to 1 million bytes. */ + inlineContext?: string; + /** The type of the grounding checking source. */ + type?: 'UNSPECIFIED' | 'WEB' | 'ENTERPRISE' | 'VERTEX_AI_SEARCH' | 'INLINE'; + /** The uri of the Vertex AI Search data source. */ + vertexAiSearchDatastore?: string; +} + +/** The configuration for grounding checking. */ +export declare interface SchemaPredictParamsGroundingConfig { + /** If set, skip finding claim attributions (i.e not generate grounding citation). */ + disableAttribution?: boolean; + /** The sources for the grounding checking. */ + sources?: SchemaPredictParamsGroundingConfigSourceEntry[]; +} + +/** A prompt instance's parameters set that contains a set of variable values. */ +export declare interface SchemaPromptInstancePromptExecution { + /** Maps variable names to their value. */ + arguments?: Record; +} + +/** Represents a prompt message. */ +export declare interface SchemaPromptSpecPromptMessage { + /** Generation config. */ + generationConfig?: genaiTypes.GenerationConfig; + /** Tool config. This config is shared for all tools provided in the request. */ + toolConfig?: genaiTypes.FunctionCallingConfig; + /** A list of `Tools` the model may use to generate the next response. A `Tool` is a piece of code that enables the system to interact with external systems to perform an action, or set of actions, outside of knowledge and scope of the model. */ + tools?: genaiTypes.Tool[]; + /** Per request settings for blocking unsafe content. Enforced on GenerateContentResponse.candidates. */ + safetySettings?: genaiTypes.SafetySetting[]; + /** The content of the current conversation with the model. For single-turn queries, this is a single instance. For multi-turn queries, this is a repeated field that contains conversation history + latest request. */ + contents?: genaiTypes.Content[]; + /** The user provided system instructions for the model. Note: only text should be used in parts and content in each part will be in a separate paragraph. */ + systemInstruction?: genaiTypes.Content; + variables?: Record[]; + /** The model name. */ + model?: string; +} + +/** Prompt variation that embeds preambles to prompt string. */ +export declare interface SchemaPromptSpecMultimodalPrompt { + /** The prompt message. */ + promptMessage?: SchemaPromptSpecPromptMessage; +} + +/** A linked resource attached to the application by the user. */ +export declare interface SchemaPromptSpecAppBuilderDataLinkedResource { + /** A user-friendly name for the data source shown in the UI. */ + displayName?: string; + /** The unique resource name of the data source. The format is determined by the 'type' field. For type "SAVED_PROMPT": projects/{project}/locations/{location}/datasets/{dataset} For type "AI_AGENT": projects/{project}/locations/{location}/agents/{agent} */ + name?: string; + /** The type of the linked resource. e.g., "SAVED_PROMPT", "AI_AGENT" This string corresponds to the name of the LinkedResourceType enum member. See: google3/cloud/console/web/ai/platform/llm/prompts/build/services/specs_repository_service/linked_resources/linked_resource.ts */ + type?: string; +} + +/** Defines data for an application builder. */ +export declare interface SchemaPromptSpecAppBuilderData { + /** Serialized state of the code repository. This string will typically contain a JSON representation of the UI's CodeRepositoryService state (files, folders, content, and any metadata). The UI is responsible for serialization and deserialization. */ + codeRepositoryState?: string; + /** Optional. Framework used to build the application. */ + framework?: Framework; + /** Linked resources attached to the application by the user. */ + linkedResources?: SchemaPromptSpecAppBuilderDataLinkedResource[]; +} + +/** Represents a prompt spec part list. */ +export declare interface SchemaPromptSpecPartList { + /** A list of elements that can be part of a prompt. */ + parts?: genaiTypes.Part[]; +} + +/** Defines data for an interaction prompt. */ +export declare interface SchemaPromptSpecInteractionData { + /** Optional. Lists interaction IDs associated with the prompt. This maps 1:1 to PromptMessage.contents. If InteractionData is present, every prompt message has an interaction ID. */ + interactionIds?: string[]; +} + +/** Represents a structured prompt. */ +export declare interface SchemaPromptSpecStructuredPrompt { + /** Preamble: The context of the prompt. */ + context?: genaiTypes.Content; + /** Data for app builder use case. */ + appBuilderData?: SchemaPromptSpecAppBuilderData; + /** Preamble: A set of examples for expected model response. */ + examples?: SchemaPromptSpecPartList[]; + /** Preamble: For infill prompt, the prefix before expected model response. */ + infillPrefix?: string; + /** Preamble: For infill prompt, the suffix after expected model response. */ + infillSuffix?: string; + /** Preamble: The input prefixes before each example input. */ + inputPrefixes?: string[]; + /** Preamble: The output prefixes before each example output. */ + outputPrefixes?: string[]; + /** Preamble: The input test data for prediction. Each PartList in this field represents one text-only input set for a single model request. */ + predictionInputs?: SchemaPromptSpecPartList[]; + /** The prompt message. */ + promptMessage?: SchemaPromptSpecPromptMessage; + /** Data for interaction use case. */ + interactionData?: SchemaPromptSpecInteractionData; +} + +/** A pair of sentences used as reference in source and target languages. */ +export declare interface SchemaPromptSpecReferenceSentencePair { + /** Source sentence in the sentence pair. */ + sourceSentence?: string; + /** Target sentence in the sentence pair. */ + targetSentence?: string; +} + +/** A list of reference sentence pairs. */ +export declare interface SchemaPromptSpecReferenceSentencePairList { + /** Reference sentence pairs. */ + referenceSentencePairs?: SchemaPromptSpecReferenceSentencePair[]; +} + +export declare interface SchemaPromptSpecTranslationFileInputSource { + /** The file's contents. */ + content?: string; + /** The file's display name. */ + displayName?: string; + /** The file's mime type. */ + mimeType?: string; +} + +export declare interface SchemaPromptSpecTranslationGcsInputSource { + /** Source data URI. For example, `gs://my_bucket/my_object`. */ + inputUri?: string; +} + +export declare interface SchemaPromptSpecTranslationSentenceFileInput { + /** Inlined file source. */ + fileInputSource?: SchemaPromptSpecTranslationFileInputSource; + /** Cloud Storage file source. */ + gcsInputSource?: SchemaPromptSpecTranslationGcsInputSource; +} + +/** The translation example that contains reference sentences from various sources. */ +export declare interface SchemaPromptSpecTranslationExample { + /** The reference sentences from inline text. */ + referenceSentencePairLists?: SchemaPromptSpecReferenceSentencePairList[]; + /** The reference sentences from file. */ + referenceSentencesFileInputs?: SchemaPromptSpecTranslationSentenceFileInput[]; +} + +/** Optional settings for translation prompt. */ +export declare interface SchemaPromptSpecTranslationOption { + /** How many shots to use. */ + numberOfShots?: number; +} + +/** Prompt variation for Translation use case. */ +export declare interface SchemaPromptSpecTranslationPrompt { + /** The translation example. */ + example?: SchemaPromptSpecTranslationExample; + /** The translation option. */ + option?: SchemaPromptSpecTranslationOption; + /** The prompt message. */ + promptMessage?: SchemaPromptSpecPromptMessage; + /** The source language code. */ + sourceLanguageCode?: string; + /** The target language code. */ + targetLanguageCode?: string; +} + +/** The A2 schema of a prompt. */ +export declare interface SchemaPromptApiSchema { + /** The Schema version that represents changes to the API behavior. */ + apiSchemaVersion?: string; + /** A list of execution instances for constructing a ready-to-use prompt. */ + executions?: SchemaPromptInstancePromptExecution[]; + /** Multimodal prompt which embeds preambles to prompt string. */ + multimodalPrompt?: SchemaPromptSpecMultimodalPrompt; + /** The prompt variation that stores preambles in separate fields. */ + structuredPrompt?: SchemaPromptSpecStructuredPrompt; + /** The prompt variation for Translation use case. */ + translationPrompt?: SchemaPromptSpecTranslationPrompt; +} + +/** Represents the text prompt dataset metadata. */ +export declare interface SchemaTextPromptDatasetMetadata { + /** Number of candidates. */ + candidateCount?: string; + /** The Google Cloud Storage URI that stores the prompt data. */ + gcsUri?: string; + /** Grounding checking configuration. */ + groundingConfig?: SchemaPredictParamsGroundingConfig; + /** Whether the prompt dataset has prompt variable. */ + hasPromptVariable?: boolean; + /** Whether or not the user has enabled logit probabilities in the model parameters. */ + logprobs?: boolean; + /** Value of the maximum number of tokens generated set when the dataset was saved. */ + maxOutputTokens?: string; + /** User-created prompt note. Note size limit is 2KB. */ + note?: string; + /** The API schema of the prompt to support both UI and SDK usages. */ + promptApiSchema?: SchemaPromptApiSchema; + /** Type of the prompt dataset. */ + promptType?: string; + /** Seeding enables model to return a deterministic response on a best effort basis. Determinism isn't guaranteed. This field determines whether or not seeding is enabled. */ + seedEnabled?: boolean; + /** The actual value of the seed. */ + seedValue?: string; + /** Customized stop sequences. */ + stopSequences?: string[]; + /** The content of the prompt dataset system instruction. */ + systemInstruction?: string; + /** The Google Cloud Storage URI that stores the system instruction, starting with gs://. */ + systemInstructionGcsUri?: string; + /** Temperature value used for sampling set when the dataset was saved. This value is used to tune the degree of randomness. */ + temperature?: number; + /** The content of the prompt dataset. */ + text?: string; + /** Top K value set when the dataset was saved. This value determines how many candidates with highest probability from the vocab would be selected for each decoding step. */ + topK?: string; + /** Top P value set when the dataset was saved. Given topK tokens for decoding, top candidates will be selected until the sum of their probabilities is topP. */ + topP?: number; +} + +/** Config for creating a dataset resource to store prompts. */ +export declare interface CreateDatasetConfig { + /** Used to override HTTP request options. */ + httpOptions?: genaiTypes.HttpOptions; + /** Abort signal which can be used to cancel the request. + + NOTE: AbortSignal is a client-only operation. Using it to cancel an + operation will not cancel the request in the service. You will still + be charged usage for any applicable operations. + */ + abortSignal?: AbortSignal; +} + +/** Parameters for creating a dataset resource to store prompts. */ +export declare interface CreateDatasetParameters { + name?: string; + displayName?: string; + metadataSchemaUri?: string; + metadata?: SchemaTextPromptDatasetMetadata; + description?: string; + encryptionSpec?: genaiTypes.EncryptionSpec; + modelReference?: string; + config?: CreateDatasetConfig; +} + +/** Represents the create dataset operation. */ +export declare interface DatasetOperation { + /** The server-assigned name, which is only unique within the same service that originally returns it. If you use the default HTTP mapping, the `name` should be a resource name ending with `operations/{unique_id}`. */ + name?: string; + /** Service-specific metadata associated with the operation. It typically contains progress information and common metadata such as create time. Some services might not provide such metadata. Any method that returns a long-running operation should document the metadata type, if any. */ + metadata?: Record; + /** If the value is `false`, it means the operation is still in progress. If `true`, the operation is completed, and either `error` or `response` is available. */ + done?: boolean; + /** The error result of the operation in case of failure or cancellation. */ + error?: Record; + /** The result of the dataset operation. */ + response?: Record; +} + +/** Config for creating a dataset version resource to store prompts. */ +export declare interface CreateDatasetVersionConfig { + /** Used to override HTTP request options. */ + httpOptions?: genaiTypes.HttpOptions; + /** Abort signal which can be used to cancel the request. + + NOTE: AbortSignal is a client-only operation. Using it to cancel an + operation will not cancel the request in the service. You will still + be charged usage for any applicable operations. + */ + abortSignal?: AbortSignal; +} + +/** Represents the create dataset version parameters. */ +export declare interface CreateDatasetVersionParameters { + datasetName?: string; + metadata?: SchemaTextPromptDatasetMetadata; + modelReference?: string; + parent?: string; + displayName?: string; + config?: CreateDatasetVersionConfig; +} + +/** Parameters for getting a dataset resource to store prompts. */ +export declare interface GetDatasetParameters { + name?: string; + config?: VertexBaseConfig; +} + +/** A SavedQuery is a view of the dataset. It references a subset of annotations by problem type and filters. */ +export declare interface SavedQuery { + /** Output only. Filters on the Annotations in the dataset. */ + annotationFilter?: string; + /** Output only. Number of AnnotationSpecs in the context of the SavedQuery. */ + annotationSpecCount?: number; + /** Output only. Timestamp when this SavedQuery was created. */ + createTime?: string; + /** Required. The user-defined name of the SavedQuery. The name can be up to 128 characters long and can consist of any UTF-8 characters. */ + displayName?: string; + /** Used to perform a consistent read-modify-write update. If not set, a blind "overwrite" update happens. */ + etag?: string; + /** Some additional information about the SavedQuery. */ + metadata?: unknown; + /** Output only. Resource name of the SavedQuery. */ + name?: string; + /** Required. Problem type of the SavedQuery. Allowed values: * IMAGE_CLASSIFICATION_SINGLE_LABEL * IMAGE_CLASSIFICATION_MULTI_LABEL * IMAGE_BOUNDING_POLY * IMAGE_BOUNDING_BOX * TEXT_CLASSIFICATION_SINGLE_LABEL * TEXT_CLASSIFICATION_MULTI_LABEL * TEXT_EXTRACTION * TEXT_SENTIMENT * VIDEO_CLASSIFICATION * VIDEO_OBJECT_TRACKING */ + problemType?: string; + /** Output only. If the Annotations belonging to the SavedQuery can be used for AutoML training. */ + supportAutomlTraining?: boolean; + /** Output only. Timestamp when SavedQuery was last updated. */ + updateTime?: string; +} + +/** Represents a dataset resource to store prompts. */ +export declare interface Dataset { + /** Required. Additional information about the Dataset. */ + metadata?: SchemaTextPromptDatasetMetadata; + /** Customer-managed encryption key spec for a Dataset. If set, this Dataset and all sub-resources of this Dataset will be secured by this key. */ + encryptionSpec?: genaiTypes.EncryptionSpec; + /** Output only. Timestamp when this Dataset was created. */ + createTime?: string; + /** Output only. The number of DataItems in this Dataset. Only apply for non-structured Dataset. */ + dataItemCount?: string; + /** The description of the Dataset. */ + description?: string; + /** Required. The user-defined name of the Dataset. The name can be up to 128 characters long and can consist of any UTF-8 characters. */ + displayName?: string; + /** Used to perform consistent read-modify-write updates. If not set, a blind "overwrite" update happens. */ + etag?: string; + /** The labels with user-defined metadata to organize your Datasets. Label keys and values can be no longer than 64 characters (Unicode codepoints), can only contain lowercase letters, numeric characters, underscores and dashes. International characters are allowed. No more than 64 user labels can be associated with one Dataset (System labels are excluded). See https://goo.gl/xmQnxf for more information and examples of labels. System reserved label keys are prefixed with "aiplatform.googleapis.com/" and are immutable. Following system labels exist for each Dataset: * "aiplatform.googleapis.com/dataset_metadata_schema": output only, its value is the metadata_schema's title. */ + labels?: Record; + /** Output only. The resource name of the Artifact that was created in MetadataStore when creating the Dataset. The Artifact resource name pattern is `projects/{project}/locations/{location}/metadataStores/{metadata_store}/artifacts/{artifact}`. */ + metadataArtifact?: string; + /** Required. Points to a YAML file stored on Google Cloud Storage describing additional information about the Dataset. The schema is defined as an OpenAPI 3.0.2 Schema Object. The schema files that can be used here are found in gs://google-cloud-aiplatform/schema/dataset/metadata/. */ + metadataSchemaUri?: string; + /** Optional. Reference to the public base model last used by the dataset. Only set for prompt datasets. */ + modelReference?: string; + /** Output only. Identifier. The resource name of the Dataset. Format: `projects/{project}/locations/{location}/datasets/{dataset}` */ + name?: string; + /** Output only. Reserved for future use. */ + satisfiesPzi?: boolean; + /** Output only. Reserved for future use. */ + satisfiesPzs?: boolean; + /** All SavedQueries belong to the Dataset will be returned in List/Get Dataset response. The annotation_specs field will not be populated except for UI cases which will only use annotation_spec_count. In CreateDataset request, a SavedQuery is created together if this field is set, up to one SavedQuery can be set in CreateDatasetRequest. The SavedQuery should not contain any AnnotationSpec. */ + savedQueries?: SavedQuery[]; + /** Output only. Timestamp when this Dataset was last updated. */ + updateTime?: string; +} + +/** Parameters for getting a dataset resource to store prompts. */ +export declare interface GetDatasetVersionParameters { + datasetId?: string; + datasetVersionId?: string; + config?: VertexBaseConfig; +} + +/** Represents a dataset version resource to store prompts. */ +export declare interface DatasetVersion { + /** Required. Output only. Additional information about the DatasetVersion. */ + metadata?: SchemaTextPromptDatasetMetadata; + /** Output only. Name of the associated BigQuery dataset. */ + bigQueryDatasetName?: string; + /** Output only. Timestamp when this DatasetVersion was created. */ + createTime?: string; + /** The user-defined name of the DatasetVersion. The name can be up to 128 characters long and can consist of any UTF-8 characters. */ + displayName?: string; + /** Used to perform consistent read-modify-write updates. If not set, a blind "overwrite" update happens. */ + etag?: string; + /** Output only. Reference to the public base model last used by the dataset version. Only set for prompt dataset versions. */ + modelReference?: string; + /** Output only. Identifier. The resource name of the DatasetVersion. Format: `projects/{project}/locations/{location}/datasets/{dataset}/datasetVersions/{dataset_version}` */ + name?: string; + /** Output only. Reserved for future use. */ + satisfiesPzi?: boolean; + /** Output only. Reserved for future use. */ + satisfiesPzs?: boolean; + /** Output only. Timestamp when this DatasetVersion was last updated. */ + updateTime?: string; +} + +/** Config for getting a dataset version operation. */ +export declare interface GetDatasetOperationConfig { + /** Used to override HTTP request options. */ + httpOptions?: genaiTypes.HttpOptions; + /** Abort signal which can be used to cancel the request. + + NOTE: AbortSignal is a client-only operation. Using it to cancel an + operation will not cancel the request in the service. You will still + be charged usage for any applicable operations. + */ + abortSignal?: AbortSignal; +} + +/** Parameters for getting a dataset operation. */ +export declare interface GetDatasetOperationParameters { + datasetId?: string; + operationId?: string; + config?: GetDatasetOperationConfig; +} + +/** Config for listing prompt datasets and dataset versions. */ +export declare interface ListPromptsConfig { + /** Used to override HTTP request options. */ + httpOptions?: genaiTypes.HttpOptions; + /** Abort signal which can be used to cancel the request. + + NOTE: AbortSignal is a client-only operation. Using it to cancel an + operation will not cancel the request in the service. You will still + be charged usage for any applicable operations. + */ + abortSignal?: AbortSignal; + pageSize?: number; + pageToken?: string; + /** An expression for filtering the results of the request. + For field names both snake_case and camelCase are supported. */ + filter?: string; +} + +/** Parameters for listing prompt datasets. */ +export declare interface ListDatasetsRequestParameters { + config?: ListPromptsConfig; +} + +/** Response for listing prompt datasets. */ +export class ListDatasetsResponse { + /** Used to retain the full HTTP response. */ + sdkHttpResponse?: genaiTypes.HttpResponse; + nextPageToken?: string; + /** List of datasets for the project. + */ + datasets?: Dataset[]; +} + +/** Parameters for listing dataset versions. */ +export declare interface ListDatasetVersionsRequestParameters { + readMask?: string; + datasetId?: string; + config?: ListPromptsConfig; +} + +/** Response for listing prompt datasets. */ +export class ListDatasetVersionsResponse { + /** Used to retain the full HTTP response. */ + sdkHttpResponse?: genaiTypes.HttpResponse; + nextPageToken?: string; + /** List of datasets for the project. + */ + datasetVersions?: DatasetVersion[]; +} + +/** Config for deleting a prompt. */ +export declare interface DeletePromptConfig { + /** Used to override HTTP request options. */ + httpOptions?: genaiTypes.HttpOptions; + /** Abort signal which can be used to cancel the request. + + NOTE: AbortSignal is a client-only operation. Using it to cancel an + operation will not cancel the request in the service. You will still + be charged usage for any applicable operations. + */ + abortSignal?: AbortSignal; + /** Timeout for the delete prompt operation in seconds. Defaults to 90. */ + timeout?: number; +} + +/** Parameters for deleting a prompt dataset. */ +export declare interface DeleteDatasetRequestParameters { + /** ID of the prompt dataset to be deleted. */ + promptId: string; + config?: DeletePromptConfig; +} + +/** Operation for deleting prompts. */ +export declare interface DeletePromptOperation { + /** The server-assigned name, which is only unique within the same service that originally returns it. If you use the default HTTP mapping, the `name` should be a resource name ending with `operations/{unique_id}`. */ + name?: string; + /** Service-specific metadata associated with the operation. It typically contains progress information and common metadata such as create time. Some services might not provide such metadata. Any method that returns a long-running operation should document the metadata type, if any. */ + metadata?: Record; + /** If the value is `false`, it means the operation is still in progress. If `true`, the operation is completed, and either `error` or `response` is available. */ + done?: boolean; + /** The error result of the operation in case of failure or cancellation. */ + error?: Record; +} + +/** Parameters for deleting a prompt version. */ +export declare interface DeletePromptVersionRequestParameters { + /** ID of the prompt to be deleted. */ + promptId: string; + /** ID of the prompt version to be deleted within the provided prompt_id. */ + versionId: string; + config?: DeletePromptConfig; +} + +/** Operation for deleting prompt versions. */ +export declare interface DeletePromptVersionOperation { + /** The server-assigned name, which is only unique within the same service that originally returns it. If you use the default HTTP mapping, the `name` should be a resource name ending with `operations/{unique_id}`. */ + name?: string; + /** Service-specific metadata associated with the operation. It typically contains progress information and common metadata such as create time. Some services might not provide such metadata. Any method that returns a long-running operation should document the metadata type, if any. */ + metadata?: Record; + /** If the value is `false`, it means the operation is still in progress. If `true`, the operation is completed, and either `error` or `response` is available. */ + done?: boolean; + /** The error result of the operation in case of failure or cancellation. */ + error?: Record; +} + +/** Config for restoring a prompt version. */ +export declare interface RestoreVersionConfig { + /** Used to override HTTP request options. */ + httpOptions?: genaiTypes.HttpOptions; + /** Abort signal which can be used to cancel the request. + + NOTE: AbortSignal is a client-only operation. Using it to cancel an + operation will not cancel the request in the service. You will still + be charged usage for any applicable operations. + */ + abortSignal?: AbortSignal; +} + +/** Parameters for restoring a prompt version. */ +export declare interface RestoreVersionRequestParameters { + /** ID of the prompt dataset to be restored. */ + datasetId: string; + /** ID of the prompt dataset version to be restored. */ + versionId: string; + config?: RestoreVersionConfig; +} + +/** Represents the restore version operation. */ +export declare interface RestoreVersionOperation { + /** The server-assigned name, which is only unique within the same service that originally returns it. If you use the default HTTP mapping, the `name` should be a resource name ending with `operations/{unique_id}`. */ + name?: string; + /** Service-specific metadata associated with the operation. It typically contains progress information and common metadata such as create time. Some services might not provide such metadata. Any method that returns a long-running operation should document the metadata type, if any. */ + metadata?: Record; + /** If the value is `false`, it means the operation is still in progress. If `true`, the operation is completed, and either `error` or `response` is available. */ + done?: boolean; + /** The error result of the operation in case of failure or cancellation. */ + error?: Record; +} + +/** Config for creating a dataset resource to store prompts. */ +export declare interface UpdatePromptConfig { + /** Used to override HTTP request options. */ + httpOptions?: genaiTypes.HttpOptions; + /** Abort signal which can be used to cancel the request. + + NOTE: AbortSignal is a client-only operation. Using it to cancel an + operation will not cancel the request in the service. You will still + be charged usage for any applicable operations. + */ + abortSignal?: AbortSignal; + /** The updated display name for the prompt. */ + promptDisplayName?: string; + /** The updated display name for the prompt version. If not set, a default name with a timestamp will be used. */ + versionDisplayName?: string; + /** The timeout for the update_dataset_resource request in seconds. If not set, the default timeout is 90 seconds. */ + timeout?: number; + /** Customer-managed encryption key spec for a prompt dataset. If set, this prompt dataset and all sub-resources of this prompt dataset will be secured by this key. */ + encryptionSpec?: genaiTypes.EncryptionSpec; +} + +/** Parameters for creating a dataset resource to store prompts. */ +export declare interface UpdateDatasetParameters { + name?: string; + datasetId?: string; + displayName?: string; + metadata?: SchemaTextPromptDatasetMetadata; + description?: string; + encryptionSpec?: genaiTypes.EncryptionSpec; + modelReference?: string; + config?: UpdatePromptConfig; +} + /** An agent engine instance. */ export declare interface AgentEngine { /** The underlying API client. */ @@ -3156,28 +3731,86 @@ export class CheckQueryJobResponse { outputGcsUri?: string; } -/** A linked resource attached to the application by the user. */ -export declare interface SchemaPromptSpecAppBuilderDataLinkedResource { - /** A user-friendly name for the data source shown in the UI. */ - displayName?: string; - /** The unique resource name of the data source. The format is determined by the 'type' field. For type "SAVED_PROMPT": projects/{project}/locations/{location}/datasets/{dataset} For type "AI_AGENT": projects/{project}/locations/{location}/agents/{agent} */ - name?: string; - /** The type of the linked resource. e.g., "SAVED_PROMPT", "AI_AGENT" This string corresponds to the name of the LinkedResourceType enum member. See: google3/cloud/console/web/ai/platform/llm/prompts/build/services/specs_repository_service/linked_resources/linked_resource.ts */ - type?: string; +/** Represents a prompt. */ +export declare interface Prompt { + promptData?: PromptData; } -/** Defines data for an application builder. */ -export declare interface SchemaPromptSpecAppBuilderData { - /** Serialized state of the code repository. This string will typically contain a JSON representation of the UI's CodeRepositoryService state (files, folders, content, and any metadata). The UI is responsible for serialization and deserialization. */ - codeRepositoryState?: string; - /** Optional. Framework used to build the application. */ - framework?: Framework; - /** Linked resources attached to the application by the user. */ - linkedResources?: SchemaPromptSpecAppBuilderDataLinkedResource[]; +export type PromptData = SchemaPromptSpecPromptMessage; +/* eslint-disable @typescript-eslint/no-explicit-any */ +export type VertexBaseConfig = any; +export type ParsedResponseUnion = any; + +/** Represents a prompt instance variable. */ +export declare interface SchemaPromptInstanceVariableValue { + /** The parts of the variable value. */ + partList?: SchemaPromptSpecPartList; } -/** Defines data for an interaction prompt. */ -export declare interface SchemaPromptSpecInteractionData { - /** Optional. Lists interaction IDs associated with the prompt. This maps 1:1 to PromptMessage.contents. If InteractionData is present, every prompt message has an interaction ID. */ - interactionIds?: string[]; +/** Config for creating a prompt. */ +export declare interface CreatePromptConfig { + /** Used to override HTTP request options. */ + httpOptions?: genaiTypes.HttpOptions; + /** Abort signal which can be used to cancel the request. + + NOTE: AbortSignal is a client-only operation. Using it to cancel an + operation will not cancel the request in the service. You will still + be charged usage for any applicable operations. + */ + abortSignal?: AbortSignal; + /** The display name for the prompt. If not set, a default name with a timestamp will be used. */ + promptDisplayName?: string; + /** The timeout for the create_version request in seconds. If not set, the default timeout is 90 seconds. */ + timeout?: number; + /** Customer-managed encryption key spec for a prompt dataset. If set, this prompt dataset and all sub-resources of this prompt dataset will be secured by this key. */ + encryptionSpec?: genaiTypes.EncryptionSpec; + /** The display name for the prompt version. If not set, a default name with a timestamp will be used. */ + versionDisplayName?: string; +} + +/** Config for creating a prompt version. */ +export declare interface CreatePromptVersionConfig { + /** Used to override HTTP request options. */ + httpOptions?: genaiTypes.HttpOptions; + /** Abort signal which can be used to cancel the request. + + NOTE: AbortSignal is a client-only operation. Using it to cancel an + operation will not cancel the request in the service. You will still + be charged usage for any applicable operations. + */ + abortSignal?: AbortSignal; + /** The display name for the prompt version. If not set, a default name with a timestamp will be used. */ + versionDisplayName?: string; + /** The timeout for the create_version request in seconds. If not set, the default timeout is 90 seconds. */ + timeout?: number; + /** The display name for the prompt. If not set, a default name with a timestamp will be used. */ + promptDisplayName?: string; + /** Customer-managed encryption key spec for a prompt dataset. If set, this prompt dataset and all sub-resources of this prompt dataset will be secured by this key. */ + encryptionSpec?: genaiTypes.EncryptionSpec; +} + +/** Config for getting a prompt. */ +export declare interface GetPromptConfig { + /** Used to override HTTP request options. */ + httpOptions?: genaiTypes.HttpOptions; + /** Abort signal which can be used to cancel the request. + + NOTE: AbortSignal is a client-only operation. Using it to cancel an + operation will not cancel the request in the service. You will still + be charged usage for any applicable operations. + */ + abortSignal?: AbortSignal; +} + +/** Reference to a prompt. */ +export declare interface PromptRef { + promptId?: string; + model?: string; +} + +/** Reference to a prompt version. */ +export declare interface PromptVersionRef { + promptId?: string; + versionId?: string; + model?: string; } diff --git a/system_test/prompts_e2e_test.ts b/system_test/prompts_e2e_test.ts new file mode 100644 index 00000000..5cb70292 --- /dev/null +++ b/system_test/prompts_e2e_test.ts @@ -0,0 +1,144 @@ +/** + * @license + * Copyright 2026 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import {Client} from '../src/client'; + +const PROJECT = process.env['GCLOUD_PROJECT'] || process.env['GOOGLE_CLOUD_PROJECT']; +const LOCATION = process.env['GCLOUD_LOCATION'] || 'us-central1'; +const MODEL = 'gemini-2.0-flash'; + +describe('Prompts E2E System Tests', () => { + let client: Client; + let createdPromptId: string | undefined; + let versionIdV1: string | undefined; + let versionIdV2: string | undefined; + + beforeAll(() => { + jasmine.DEFAULT_TIMEOUT_INTERVAL = 600000; + if (!PROJECT) { + throw new Error( + 'GCLOUD_PROJECT environment variable must be set to run E2E system tests.' + ); + } + client = new Client({ + project: PROJECT, + location: LOCATION, + }); + }, 600000); + + afterAll(async () => { + if (createdPromptId) { + try { + await client.prompts.delete({promptId: createdPromptId}); + } catch (err) { + console.error(`Failed to cleanup prompt ${createdPromptId}:`, err); + } + } + }, 600000); + + it('runs strict end-to-end lifecycle for Prompts API', async () => { + const promptV1 = await client.prompts.createVersion({ + prompt: { + promptData: { + contents: [ + { + role: 'user', + parts: [{text: 'You are a helpful GenAI assistant v1.'}], + }, + ], + model: MODEL, + }, + }, + config: { + promptDisplayName: `e2e_test_prompt_${Date.now()}`, + versionDisplayName: 'v1', + }, + }); + + expect(promptV1).toBeDefined(); + expect(promptV1.promptData).toBeDefined(); + + const datasetResource = (promptV1 as any)._dataset; + const versionResource = (promptV1 as any)._dataset_version; + expect(datasetResource?.name).toBeDefined(); + expect(versionResource?.name).toBeDefined(); + + createdPromptId = datasetResource.name.split('/').pop(); + versionIdV1 = versionResource.name.split('/').pop(); + + expect(createdPromptId).toBeDefined(); + expect(versionIdV1).toBeDefined(); + + const fetchedPrompt = await client.prompts.get({ + promptId: createdPromptId!, + }); + expect(fetchedPrompt.promptData).toBeDefined(); + + const fetchedVersion = await client.prompts.getVersion({ + promptId: createdPromptId!, + versionId: versionIdV1!, + }); + expect(fetchedVersion.promptData).toBeDefined(); + + const promptRefs = await client.prompts.list(); + expect(promptRefs.length).toBeGreaterThan(0); + const foundRef = promptRefs.find((r) => r.promptId === createdPromptId); + expect(foundRef).toBeDefined(); + + const promptV2 = await client.prompts.update({ + promptId: createdPromptId!, + prompt: { + promptData: { + contents: [ + { + role: 'user', + parts: [{text: 'You are an updated GenAI assistant v2.'}], + }, + ], + model: MODEL, + }, + }, + config: { + versionDisplayName: 'v2', + }, + }); + + expect(promptV2.promptData).toBeDefined(); + const versionResourceV2 = (promptV2 as any)._dataset_version; + versionIdV2 = versionResourceV2?.name.split('/').pop(); + expect(versionIdV2).toBeDefined(); + expect(versionIdV2).not.toEqual(versionIdV1); + + const versionRefs = await client.prompts.listVersions({ + promptId: createdPromptId!, + }); + expect(versionRefs.length).toBeGreaterThanOrEqual(2); + expect(versionRefs.some((v) => v.versionId === versionIdV1)).toBeTrue(); + expect(versionRefs.some((v) => v.versionId === versionIdV2)).toBeTrue(); + + const restoredPrompt = await client.prompts.restoreVersion({ + promptId: createdPromptId!, + versionId: versionIdV1!, + }); + expect(restoredPrompt.promptData).toBeDefined(); + expect((restoredPrompt as any)._dataset_version).toBeDefined(); + + await client.prompts.deleteVersion({ + promptId: createdPromptId!, + versionId: versionIdV2!, + }); + + const afterDeleteVersionRefs = await client.prompts.listVersions({ + promptId: createdPromptId!, + }); + expect(afterDeleteVersionRefs.some((v) => v.versionId === versionIdV2)).toBeFalse(); + + await client.prompts.delete({ + promptId: createdPromptId!, + }); + createdPromptId = undefined; + }, 600000); +}); diff --git a/system_test/prompts_sample_test.ts b/system_test/prompts_sample_test.ts new file mode 100644 index 00000000..b191e5d7 --- /dev/null +++ b/system_test/prompts_sample_test.ts @@ -0,0 +1,137 @@ +/** + * @license + * Copyright 2026 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +// Standalone executable sample for Prompts API +// Run with: npx ts-node third_party/javascript/node_modules/vertexai/system_test/prompts_sample_test.ts +import {Client} from '../src/client'; +import * as assert from 'assert'; + +const PROJECT = process.env['GCLOUD_PROJECT'] || process.env['GOOGLE_CLOUD_PROJECT']; +const LOCATION = process.env['GCLOUD_LOCATION'] || 'us-central1'; +const MODEL = 'gemini-2.0-flash'; + +async function runPromptsSample() { + if (!PROJECT) { + console.error('ERROR: GCLOUD_PROJECT environment variable must be set.'); + process.exit(1); + } + + const client = new Client({ + project: PROJECT, + location: LOCATION, + }); + + let promptId: string | undefined; + let versionIdV1: string | undefined; + let versionIdV2: string | undefined; + + try { + const promptV1 = await client.prompts.createVersion({ + prompt: { + promptData: { + contents: [ + { + role: 'user', + parts: [{text: 'You are a helpful GenAI assistant v1.'}], + }, + ], + model: MODEL, + }, + }, + config: { + promptDisplayName: `standalone_sample_prompt_${Date.now()}`, + versionDisplayName: 'v1', + }, + }); + + const datasetResource = (promptV1 as any)._dataset; + const versionResource = (promptV1 as any)._dataset_version; + promptId = datasetResource?.name?.split('/').pop(); + versionIdV1 = versionResource?.name?.split('/').pop(); + + assert.ok(promptId, 'Prompt ID should be present'); + assert.ok(versionIdV1, 'Version ID should be present'); + + const fetchedPrompt = await client.prompts.get({ + promptId: promptId!, + }); + assert.ok(fetchedPrompt.promptData, 'Fetched prompt should have promptData'); + + const fetchedVersion = await client.prompts.getVersion({ + promptId: promptId!, + versionId: versionIdV1!, + }); + assert.ok(fetchedVersion.promptData, 'Fetched version should have promptData'); + + const promptRefs = await client.prompts.list(); + const foundRef = promptRefs.find((r) => r.promptId === promptId); + assert.ok(foundRef, 'Created prompt ID should be found in listed prompts'); + + const promptV2 = await client.prompts.update({ + promptId: promptId!, + prompt: { + promptData: { + contents: [ + { + role: 'user', + parts: [{text: 'You are an updated GenAI assistant v2.'}], + }, + ], + model: MODEL, + }, + }, + config: { + versionDisplayName: 'v2', + }, + }); + const versionResourceV2 = (promptV2 as any)._dataset_version; + versionIdV2 = versionResourceV2?.name?.split('/').pop(); + assert.ok(versionIdV2, 'Version ID 2 should be present'); + assert.notStrictEqual(versionIdV1, versionIdV2, 'New version ID should differ from v1'); + + const versionRefs = await client.prompts.listVersions({ + promptId: promptId!, + }); + assert.ok(versionRefs.some((v) => v.versionId === versionIdV1), 'v1 should be present'); + assert.ok(versionRefs.some((v) => v.versionId === versionIdV2), 'v2 should be present'); + + const restoredPrompt = await client.prompts.restoreVersion({ + promptId: promptId!, + versionId: versionIdV1!, + }); + assert.ok(restoredPrompt.promptData, 'Restored prompt should have promptData'); + + await client.prompts.deleteVersion({ + promptId: promptId!, + versionId: versionIdV2!, + }); + const afterDeleteVersionRefs = await client.prompts.listVersions({ + promptId: promptId!, + }); + assert.ok( + !afterDeleteVersionRefs.some((v) => v.versionId === versionIdV2), + 'v2 should no longer be listed' + ); + + await client.prompts.delete({ + promptId: promptId!, + }); + promptId = undefined; + } catch (err) { + console.error('\n❌ Error during Prompts E2E execution:', err); + process.exit(1); + } finally { + if (promptId) { + try { + await client.prompts.delete({promptId}); + } catch (e) { + console.error(`[Cleanup] Failed to delete prompt ${promptId}:`, e); + } + } + } +} + +runPromptsSample(); diff --git a/test/replays/prompts_test.ts b/test/replays/prompts_test.ts new file mode 100644 index 00000000..30e42f34 --- /dev/null +++ b/test/replays/prompts_test.ts @@ -0,0 +1,64 @@ +/** + * @license + * Copyright 2026 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +import 'jasmine'; +import {ReplayClient} from './_replay_client.js'; + +describe('Prompts', () => { + let client: ReplayClient; + + beforeEach(() => { + client = new ReplayClient({ + project: 'test-project', + location: 'us-central1', + }); + }); + + it('gets a prompt resource', async () => { + const fetchSpy = client.setupReplay( + 'get_prompt_resource/test_get_prompt.vertex.json' + ); + const prompt = await client.prompts.get({ + promptId: '6550997480673116160', + }); + client.verifyInteraction(0, fetchSpy.calls.argsFor(0)); + expect(prompt).toBeDefined(); + expect(prompt.promptData).toBeDefined(); + client.verifyAllInteractions(); + }); + + it('gets a prompt version resource', async () => { + const fetchSpy = client.setupReplay( + 'get_prompt_resource/test_get_prompt_version.vertex.json' + ); + const prompt = await client.prompts.getVersion({ + promptId: '6550997480673116160', + versionId: '2', + }); + client.verifyInteraction(0, fetchSpy.calls.argsFor(0)); + client.verifyInteraction(1, fetchSpy.calls.argsFor(1)); + expect(prompt).toBeDefined(); + expect(prompt.promptData).toBeDefined(); + client.verifyAllInteractions(); + }); + + it('lists prompt resources', async () => { + const fetchSpy = client.setupReplay( + 'list_prompts/test_list_returns_prompts.vertex.json' + ); + const prompts1 = await client.prompts.list(); + client.verifyInteraction(0, fetchSpy.calls.argsFor(0)); + expect(prompts1).toBeDefined(); + expect(prompts1.length).toBeGreaterThan(0); + + const prompts2 = await client.prompts.list(); + expect(prompts2).toBeDefined(); + expect(prompts2.length).toBeGreaterThan(0); + + const prompts3 = await client.prompts.list(); + expect(prompts3).toBeDefined(); + client.verifyAllInteractions(); + }); +});