UNPKG

@genkit-ai/dotprompt

Version:

Genkit AI framework `.prompt` file format and management library.

1 lines 18.7 kB
{"version":3,"sources":["../src/prompt.ts"],"sourcesContent":["/**\n * Copyright 2024 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n definePrompt,\n generate,\n GenerateOptions,\n GenerateResponse,\n generateStream,\n GenerateStreamResponse,\n PromptAction,\n toGenerateRequest,\n} from '@genkit-ai/ai';\nimport { MessageData, ModelArgument } from '@genkit-ai/ai/model';\nimport { DocumentData } from '@genkit-ai/ai/retriever';\nimport { getCurrentSession } from '@genkit-ai/ai/session';\nimport { GenkitError, z } from '@genkit-ai/core';\nimport { Registry } from '@genkit-ai/core/registry';\nimport { parseSchema } from '@genkit-ai/core/schema';\nimport {\n runInNewSpan,\n setCustomMetadataAttribute,\n SPAN_TYPE_ATTR,\n} from '@genkit-ai/core/tracing';\nimport { createHash } from 'crypto';\nimport fm, { FrontMatterResult } from 'front-matter';\nimport {\n PromptFrontmatter,\n PromptMetadata,\n toFrontmatter,\n toMetadata,\n} from './metadata.js';\nimport { lookupPrompt, registryDefinitionKey } from './registry.js';\nimport { compile } from './template.js';\n\nexport type PromptData = PromptFrontmatter & { template: string };\n\nexport type PromptGenerateOptions<\n V = unknown,\n CustomOptions extends z.ZodTypeAny = z.ZodTypeAny,\n> = Omit<\n GenerateOptions<z.ZodTypeAny, CustomOptions>,\n 'prompt' | 'input' | 'model'\n> & {\n model?: ModelArgument<CustomOptions>;\n input?: V;\n};\n\ninterface RenderMetadata {\n docs?: DocumentData[];\n messages?: MessageData[];\n}\n\nexport class Dotprompt<I = unknown> implements PromptMetadata<z.ZodTypeAny> {\n name: string;\n description?: string;\n variant?: string;\n hash: string;\n\n template: string;\n\n model?: PromptMetadata['model'];\n metadata: PromptMetadata['metadata'];\n input?: PromptMetadata['input'];\n output?: PromptMetadata['output'];\n tools?: PromptMetadata['tools'];\n config?: PromptMetadata['config'];\n\n private _promptAction?: PromptAction;\n\n private _render: (\n input: I,\n options?: RenderMetadata,\n data?: Record<string, any>\n ) => MessageData[];\n\n static parse(registry: Registry, name: string, source: string) {\n try {\n const fmResult = (fm as any)(source.trimStart(), {\n allowUnsafe: false,\n }) as FrontMatterResult<unknown>;\n\n return new Dotprompt(\n registry,\n {\n ...toMetadata(registry, fmResult.attributes),\n name,\n } as PromptMetadata,\n fmResult.body\n );\n } catch (e: any) {\n throw new GenkitError({\n source: 'Dotprompt',\n status: 'INVALID_ARGUMENT',\n message: `Error parsing YAML frontmatter of '${name}' prompt: ${e.stack}`,\n });\n }\n }\n\n static fromAction(registry: Registry, action: PromptAction): Dotprompt {\n const { template, ...options } = action.__action.metadata!.prompt;\n const pm = options as PromptMetadata;\n if (pm.input?.schema) {\n pm.input.jsonSchema = options.input?.schema;\n delete pm.input.schema;\n }\n if (pm.output?.schema) {\n pm.output.jsonSchema = options.output?.schema;\n }\n const prompt = new Dotprompt(\n registry,\n options as PromptMetadata,\n template,\n action\n );\n return prompt;\n }\n\n constructor(\n private registry: Registry,\n options: PromptMetadata,\n template: string,\n action?: PromptAction\n ) {\n this.name = options.name || 'untitledPrompt';\n this.description = options.description;\n this.variant = options.variant;\n this.model = options.model;\n this.input = options.input || { schema: z.any() };\n this.output = options.output;\n this.tools = options.tools;\n this.config = options.config;\n this.template = template;\n this.hash = createHash('sha256').update(JSON.stringify(this)).digest('hex');\n\n this._render = compile(this.template, options);\n this._promptAction = action;\n }\n\n /**\n * Renders all of the prompt's text parts into a raw string.\n *\n * @param input User input to the prompt template.\n * @param options Optional context and/or history for the prompt template.\n * @returns all of the text parts concatenated into a string.\n */\n\n renderText(input: I, options?: RenderMetadata): string {\n const result = this.renderMessages(input, options);\n if (result.length !== 1) {\n throw new Error(\"Multi-message prompt can't be rendered as text.\");\n }\n let out = '';\n for (const part of result[0].content) {\n if (!part.text) {\n throw new Error(\"Multimodal prompt can't be rendered as text.\");\n }\n out += part.text;\n }\n return out;\n }\n\n /**\n * Renders the prompt template into an array of messages.\n *\n * @param input User input to the prompt template\n * @param options optional context and/or history for the prompt template.\n * @returns an array of messages representing an exchange between a user and a model.\n */\n renderMessages(input?: I, options?: RenderMetadata): MessageData[] {\n let sessionStateData: Record<string, any> | undefined = undefined;\n if (getCurrentSession()) {\n sessionStateData = { state: getCurrentSession()?.state };\n }\n input = parseSchema(input, {\n schema: this.input?.schema,\n jsonSchema: this.input?.jsonSchema,\n });\n return this._render(\n { ...this.input?.default, ...input },\n options,\n sessionStateData\n );\n }\n\n toJSON(): PromptData {\n return { ...toFrontmatter(this), template: this.template };\n }\n\n define(options?: { ns?: string; description?: string }): void {\n this._promptAction = definePrompt(\n this.registry,\n {\n name: registryDefinitionKey(this.name, this.variant, options?.ns),\n description: options?.description ?? this.description,\n inputSchema: this.input?.schema,\n inputJsonSchema: this.input?.jsonSchema,\n metadata: {\n type: 'prompt',\n prompt: this.toJSON(),\n },\n },\n async (input?: I) =>\n toGenerateRequest(this.registry, this.render({ input }))\n );\n }\n\n get promptAction(): PromptAction | undefined {\n return this._promptAction;\n }\n\n private _generateOptions<\n O extends z.ZodTypeAny = z.ZodTypeAny,\n CustomOptions extends z.ZodTypeAny = z.ZodTypeAny,\n >(options: PromptGenerateOptions<I>): GenerateOptions<O, CustomOptions> {\n const messages = this.renderMessages(options.input, {\n messages: options.messages,\n docs: options.docs,\n });\n let renderedPrompt;\n let renderedMessages;\n if (messages.length > 0 && messages[messages.length - 1].role === 'user') {\n renderedPrompt = messages[messages.length - 1].content;\n renderedMessages = messages.slice(0, messages.length - 1);\n } else {\n renderedPrompt = undefined;\n renderedMessages = messages;\n }\n return {\n model: options.model || this.model!,\n config: { ...this.config, ...options.config },\n messages: renderedMessages,\n prompt: renderedPrompt,\n docs: options.docs,\n output: {\n format: options.output?.format || this.output?.format || undefined,\n schema: options.output?.schema || this.output?.schema,\n jsonSchema: options.output?.jsonSchema || this.output?.jsonSchema,\n },\n tools: (options.tools || []).concat(this.tools || []),\n streamingCallback: options.streamingCallback,\n returnToolRequests: options.returnToolRequests,\n use: options.use,\n } as GenerateOptions<O, CustomOptions>;\n }\n\n /**\n * Renders the prompt template based on user input.\n *\n * @param opt Options for the prompt template, including user input variables and custom model configuration options.\n * @returns a `GenerateOptions` object to be used with the `generate()` function from @genkit-ai/ai.\n */\n render<\n CustomOptions extends z.ZodTypeAny = z.ZodTypeAny,\n O extends z.ZodTypeAny = z.ZodTypeAny,\n >(\n opt: PromptGenerateOptions<I, CustomOptions>\n ): GenerateOptions<CustomOptions, O> {\n return this._generateOptions(opt);\n }\n\n async renderInNewSpan<\n CustomOptions extends z.ZodTypeAny = z.ZodTypeAny,\n O extends z.ZodTypeAny = z.ZodTypeAny,\n >(opt: PromptGenerateOptions<I>): Promise<GenerateOptions<CustomOptions, O>> {\n const spanName = this.variant ? `${this.name}.${this.variant}` : this.name;\n return runInNewSpan(\n {\n metadata: {\n name: spanName,\n input: opt,\n },\n labels: {\n [SPAN_TYPE_ATTR]: 'dotprompt',\n },\n },\n async (metadata) => {\n setCustomMetadataAttribute('prompt_fingerprint', this.hash);\n const generateOptions = this._generateOptions<CustomOptions, O>(opt);\n metadata.output = generateOptions;\n return generateOptions;\n }\n );\n }\n\n /**\n * Generates a response by rendering the prompt template with given user input and then calling the model.\n *\n * @param opt Options for the prompt template, including user input variables and custom model configuration options.\n * @returns the model response as a promise of `GenerateResponse`.\n */\n async generate<\n CustomOptions extends z.ZodTypeAny = z.ZodTypeAny,\n O extends z.ZodTypeAny = z.ZodTypeAny,\n >(\n opt: PromptGenerateOptions<I, CustomOptions>\n ): Promise<GenerateResponse<z.infer<O>>> {\n const renderedOpts = this.renderInNewSpan<CustomOptions, O>(opt);\n return generate<CustomOptions, O>(this.registry, renderedOpts);\n }\n\n /**\n * Generates a streaming response by rendering the prompt template with given user input and then calling the model.\n *\n * @param opt Options for the prompt template, including user input variables and custom model configuration options.\n * @returns the model response as a promise of `GenerateStreamResponse`.\n */\n async generateStream<CustomOptions extends z.ZodTypeAny = z.ZodTypeAny>(\n opt: PromptGenerateOptions<I, CustomOptions>\n ): Promise<GenerateStreamResponse> {\n const renderedOpts = await this.renderInNewSpan<CustomOptions>(opt);\n return generateStream(this.registry, renderedOpts);\n }\n}\n\nexport class DotpromptRef<Variables = unknown> {\n name: string;\n variant?: string;\n dir?: string;\n private _prompt?: Dotprompt<Variables>;\n\n constructor(\n name: string,\n options?: {\n variant?: string;\n dir?: string;\n }\n ) {\n this.name = name;\n this.variant = options?.variant;\n this.dir = options?.dir;\n }\n\n /** Loads the prompt which is referenced. */\n async loadPrompt(registry: Registry): Promise<Dotprompt<Variables>> {\n if (this._prompt) return this._prompt;\n this._prompt = (await lookupPrompt(\n registry,\n this.name,\n this.variant,\n this.dir\n )) as Dotprompt<Variables>;\n return this._prompt;\n }\n\n /**\n * Generates a response by rendering the prompt template with given user input and then calling the model.\n *\n * @param opt Options for the prompt template, including user input variables and custom model configuration options.\n * @returns the model response as a promise of `GenerateResponse`.\n */\n\n async generate<\n CustomOptions extends z.ZodTypeAny = z.ZodTypeAny,\n O extends z.ZodTypeAny = z.ZodTypeAny,\n >(\n registry: Registry,\n opt: PromptGenerateOptions<Variables, CustomOptions>\n ): Promise<GenerateResponse<z.infer<O>>> {\n const prompt = await this.loadPrompt(registry);\n return prompt.generate<CustomOptions, O>(opt);\n }\n\n /**\n * Renders the prompt template based on user input.\n *\n * @param opt Options for the prompt template, including user input variables and custom model configuration options.\n * @returns a `GenerateOptions` object to be used with the `generate()` function from @genkit-ai/ai.\n */\n async render<\n CustomOptions extends z.ZodTypeAny = z.ZodTypeAny,\n O extends z.ZodTypeAny = z.ZodTypeAny,\n >(\n registry: Registry,\n\n opt: PromptGenerateOptions<Variables, CustomOptions>\n ): Promise<GenerateOptions<z.ZodTypeAny, O>> {\n const prompt = await this.loadPrompt(registry);\n return prompt.render<CustomOptions, O>(opt);\n }\n}\n\n/**\n * Define a dotprompt in code. This function is offered as an alternative to definitions in .prompt files.\n *\n * @param options the prompt definition, including its name, variant and model. Any options from .prompt file front matter are accepted.\n * @param template a string template, comparable to the main body of a prompt file.\n * @returns the newly defined prompt.\n */\nexport function defineDotprompt<\n I extends z.ZodTypeAny = z.ZodTypeAny,\n CustomOptions extends z.ZodTypeAny = z.ZodTypeAny,\n>(\n registry: Registry,\n options: PromptMetadata<I, CustomOptions>,\n template: string\n): Dotprompt<z.infer<I>> {\n const prompt = new Dotprompt(registry, options, template);\n prompt.define({ description: options.description });\n return prompt;\n}\n"],"mappings":"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","names":[]}