Skip to content

Commit a2bf8a3

Browse files
authored
Add prompt metadata to registry for function-based prompts (#1484)
1 parent 8e86d6b commit a2bf8a3

File tree

3 files changed

+89
-17
lines changed

3 files changed

+89
-17
lines changed

js/genkit/src/genkit.ts

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ import {
130130
PromptMetadata as DotpromptPromptMetadata,
131131
loadPromptFolder,
132132
prompt,
133+
toFrontmatter,
133134
} from '@genkit-ai/dotprompt';
134135
import { v4 as uuidv4 } from 'uuid';
135136
import { BaseEvalDataPointSchema } from './evaluator.js';
@@ -360,7 +361,16 @@ export class Genkit implements HasRegistry {
360361
}
361362

362363
/**
363-
* Defines and registers a function-based prompt.
364+
* Defines and registers a prompt based on a function.
365+
*
366+
* This is an alternative to defining and importing a .prompt file, providing
367+
* the most advanced control over how the final request to the model is made.
368+
*
369+
* @param options - Prompt metadata including model, model params,
370+
* input/output schemas, etc
371+
* @param fn - A function that returns a {@link GenerateRequest}. Any config
372+
* parameters specified by the {@link GenerateRequest} will take precedence
373+
* over any parameters specified by `options`.
364374
*
365375
* ```ts
366376
* const hi = ai.definePrompt(
@@ -394,9 +404,13 @@ export class Genkit implements HasRegistry {
394404
): ExecutablePrompt<z.infer<I>, O, CustomOptions>;
395405

396406
/**
397-
* Defines and registers a dotprompt.
407+
* Defines and registers a prompt based on a template.
398408
*
399-
* This is an alternative to defining and importing a .prompt file.
409+
* This is an alternative to defining and importing a .prompt file, in
410+
* situations where a static definition will not suffice.
411+
*
412+
* @param options - The first input number
413+
* @param fn - The second input number
400414
*
401415
* ```ts
402416
* const hi = ai.definePrompt(
@@ -427,10 +441,7 @@ export class Genkit implements HasRegistry {
427441
O extends z.ZodTypeAny = z.ZodTypeAny,
428442
CustomOptions extends z.ZodTypeAny = z.ZodTypeAny,
429443
>(
430-
options: PromptMetadata<I, CustomOptions> & {
431-
/** The name of the prompt. */
432-
name: string;
433-
},
444+
options: PromptMetadata<I, CustomOptions>,
434445
templateOrFn: string | PromptFn<I>
435446
): ExecutablePrompt<z.infer<I>, O, CustomOptions> {
436447
if (!options.name) {
@@ -458,9 +469,17 @@ export class Genkit implements HasRegistry {
458469
this.registry,
459470
{
460471
name: options.name!,
472+
description: options.description,
461473
inputJsonSchema: options.input?.jsonSchema,
462474
inputSchema: options.input?.schema,
463-
description: options.description,
475+
metadata: {
476+
type: 'prompt',
477+
// TODO: As a stop-gap, we are using the dotprompt interpretation of
478+
// the "prompt metadata", which is roughly the same as the dotprompt
479+
// frontmatter schema. This should be inverted, such that Genkit
480+
// defines the metadata spec and registered dotprompts conform.
481+
prompt: toFrontmatter(options),
482+
},
464483
},
465484
async (input: z.infer<I>) => {
466485
const response = await (templateOrFn as PromptFn<I>)(input);

js/genkit/tests/prompts_test.ts

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { modelRef } from '@genkit-ai/ai/model';
1818
import assert from 'node:assert';
1919
import { beforeEach, describe, it } from 'node:test';
2020
import { Genkit, genkit } from '../src/genkit';
21-
import { z } from '../src/index';
21+
import { PromptAction, z } from '../src/index';
2222
import {
2323
ProgrammableModel,
2424
defineEchoModel,
@@ -509,7 +509,7 @@ describe('definePrompt', () => {
509509
defineEchoModel(ai);
510510
});
511511

512-
it('calls dotprompt with default model', async () => {
512+
it('calls prompt with default model', async () => {
513513
const hi = ai.definePrompt(
514514
{
515515
name: 'hi',
@@ -532,7 +532,7 @@ describe('definePrompt', () => {
532532
assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}');
533533
});
534534

535-
it('calls dotprompt with default model with config', async () => {
535+
it('calls prompt with default model with config', async () => {
536536
const hi = ai.definePrompt(
537537
{
538538
name: 'hi',
@@ -561,7 +561,7 @@ describe('definePrompt', () => {
561561
);
562562
});
563563

564-
it('calls dotprompt with default model via retrieved prompt', async () => {
564+
it('calls prompt with default model via retrieved prompt', async () => {
565565
ai.definePrompt(
566566
{
567567
name: 'hi',
@@ -599,7 +599,7 @@ describe('definePrompt', () => {
599599
defineEchoModel(ai);
600600
});
601601

602-
it('calls dotprompt with default model', async () => {
602+
it('calls prompt with default model', async () => {
603603
const hi = ai.definePrompt(
604604
{
605605
name: 'hi',
@@ -622,7 +622,7 @@ describe('definePrompt', () => {
622622
assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}');
623623
});
624624

625-
it('streams dotprompt with default model', async () => {
625+
it('streams prompt with default model', async () => {
626626
const hi = ai.definePrompt(
627627
{
628628
name: 'hi',
@@ -667,7 +667,7 @@ describe('definePrompt', () => {
667667
defineEchoModel(ai);
668668
});
669669

670-
it('calls dotprompt with default model', async () => {
670+
it('calls prompt with default model', async () => {
671671
const hi = ai.definePrompt(
672672
{
673673
name: 'hi',
@@ -691,7 +691,7 @@ describe('definePrompt', () => {
691691
assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}');
692692
});
693693

694-
it('calls dotprompt with default model with config', async () => {
694+
it('calls prompt with default model with config', async () => {
695695
const hi = ai.definePrompt(
696696
{
697697
name: 'hi',
@@ -721,7 +721,7 @@ describe('definePrompt', () => {
721721
);
722722
});
723723

724-
it('calls dotprompt with default model with call site config', async () => {
724+
it('calls prompt with default model with call site config', async () => {
725725
const hi = ai.definePrompt(
726726
{
727727
name: 'hi',
@@ -870,6 +870,57 @@ describe('prompt', () => {
870870
assert.strictEqual(text, 'Echo: hi banana; config: {"temperature":11}');
871871
});
872872

873+
it('includes metadata for functional prompts', async () => {
874+
ai.definePrompt(
875+
{
876+
name: 'hi',
877+
model: 'echoModel',
878+
input: {
879+
schema: z.object({
880+
name: z.string(),
881+
}),
882+
},
883+
config: {
884+
temperature: 0.13,
885+
},
886+
},
887+
async (input) => {
888+
return {
889+
messages: [],
890+
config: {
891+
temperature: 11,
892+
},
893+
};
894+
}
895+
);
896+
const testPrompt: PromptAction =
897+
await ai.registry.lookupAction('/prompt/hi');
898+
899+
assert.deepStrictEqual(testPrompt.__action.metadata, {
900+
type: 'prompt',
901+
prompt: {
902+
name: 'hi',
903+
model: 'echoModel',
904+
config: {
905+
temperature: 0.13,
906+
},
907+
input: {
908+
schema: {
909+
type: 'object',
910+
properties: {
911+
name: {
912+
type: 'string',
913+
},
914+
},
915+
required: ['name'],
916+
additionalProperties: true,
917+
$schema: 'http://json-schema.org/draft-07/schema#',
918+
},
919+
},
920+
},
921+
});
922+
});
923+
873924
it('passes in output options to the model', async () => {
874925
const hi = ai.definePrompt(
875926
{

js/plugins/dotprompt/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import { Registry } from '@genkit-ai/core/registry';
1818
import { readFileSync } from 'fs';
1919
import { basename } from 'path';
20+
import { toFrontmatter } from './metadata.js';
2021
import {
2122
defineDotprompt,
2223
Dotprompt,
@@ -31,6 +32,7 @@ export {
3132
defineDotprompt,
3233
Dotprompt,
3334
loadPromptFolder,
35+
toFrontmatter,
3436
type PromptGenerateOptions,
3537
};
3638

0 commit comments

Comments
 (0)