Skip to content

Commit 5865db8

Browse files
committed
feat: typesafe prompts.resolve<typeof myPrompt>() with PromptIdentifier and PromptVariables inference
1 parent ad226d4 commit 5865db8

File tree

4 files changed

+53
-16
lines changed

4 files changed

+53
-16
lines changed

packages/trigger-sdk/src/v3/prompt.ts

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@ import {
1010
} from "@trigger.dev/core/v3";
1111
import { tracer } from "./tracer.js";
1212

13-
type PromptOptions<TVariables extends TaskSchema | undefined = undefined> = {
14-
id: string;
13+
type PromptOptions<
14+
TIdentifier extends string = string,
15+
TVariables extends TaskSchema | undefined = undefined,
16+
> = {
17+
id: TIdentifier;
1518
description?: string;
1619
model?: string;
1720
config?: Record<string, unknown>;
@@ -37,14 +40,30 @@ type ResolvedPrompt = {
3740

3841
export type { PromptOptions, ResolvedPrompt };
3942

40-
export type PromptHandle<TVariables extends TaskSchema | undefined = undefined> = {
41-
id: string;
43+
export type PromptHandle<
44+
TIdentifier extends string = string,
45+
TVariables extends TaskSchema | undefined = undefined,
46+
> = {
47+
id: TIdentifier;
4248
resolve(
4349
variables: inferSchemaIn<TVariables>,
4450
options?: { label?: string; version?: number }
4551
): Promise<ResolvedPrompt>;
4652
};
4753

54+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
55+
export type AnyPromptHandle = PromptHandle<string, any>;
56+
57+
/** Extract the identifier (id literal type) from a PromptHandle */
58+
export type PromptIdentifier<T extends AnyPromptHandle> = T extends PromptHandle<infer TId, any>
59+
? TId
60+
: string;
61+
62+
/** Extract the variables input type from a PromptHandle */
63+
export type PromptVariables<T extends AnyPromptHandle> = T extends PromptHandle<any, infer TVariables>
64+
? inferSchemaIn<TVariables>
65+
: Record<string, unknown>;
66+
4867
/**
4968
* Compile a Mustache-style template by substituting `{{variable}}` placeholders.
5069
*/
@@ -101,7 +120,7 @@ function makeToAISDKTelemetry(
101120
}
102121

103122
function resolveLocally(
104-
options: PromptOptions<any>,
123+
options: PromptOptions<any, any>,
105124
variables: Record<string, unknown>
106125
): ResolvedPrompt {
107126
const inputJson = Object.keys(variables).length > 0 ? JSON.stringify(variables) : undefined;
@@ -118,9 +137,12 @@ function resolveLocally(
118137
};
119138
}
120139

121-
export function definePrompt<TVariables extends TaskSchema | undefined = undefined>(
122-
options: PromptOptions<TVariables>
123-
): PromptHandle<TVariables> {
140+
export function definePrompt<
141+
TIdentifier extends string,
142+
TVariables extends TaskSchema | undefined = undefined,
143+
>(
144+
options: PromptOptions<TIdentifier, TVariables>
145+
): PromptHandle<TIdentifier, TVariables> {
124146
const parseVariables = options.variables
125147
? getSchemaParseFn(options.variables)
126148
: undefined;

packages/trigger-sdk/src/v3/promptManagement.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
type PromptOverrideCreatedResponseBody,
1111
type UpdatePromptOverrideRequestBody,
1212
} from "@trigger.dev/core/v3";
13-
import type { ResolvedPrompt } from "./prompt.js";
13+
import type { AnyPromptHandle, PromptIdentifier, PromptVariables, ResolvedPrompt } from "./prompt.js";
1414
import { tracer } from "./tracer.js";
1515

1616
function promptSpanOptions(name: string, slug: string) {
@@ -59,9 +59,9 @@ function makeToAISDKTelemetry(
5959
* compiled text. Works both inside and outside of a task context — requires
6060
* an API client to be configured (via `configure()` or task runtime).
6161
*/
62-
export async function resolvePrompt(
63-
slug: string,
64-
variables?: Record<string, unknown>,
62+
export async function resolvePrompt<TPromptHandle extends AnyPromptHandle = AnyPromptHandle>(
63+
slug: PromptIdentifier<TPromptHandle>,
64+
variables?: PromptVariables<TPromptHandle>,
6565
options?: { label?: string; version?: number; requestOptions?: ApiRequestOptions }
6666
): Promise<ResolvedPrompt> {
6767
const apiClient = apiClientManager.clientOrThrow();

packages/trigger-sdk/src/v3/prompts.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
export { definePrompt as define } from "./prompt.js";
2-
export type { PromptHandle, PromptOptions, ResolvedPrompt } from "./prompt.js";
2+
export type {
3+
AnyPromptHandle,
4+
PromptHandle,
5+
PromptIdentifier,
6+
PromptOptions,
7+
PromptVariables,
8+
ResolvedPrompt,
9+
} from "./prompt.js";
310

411
export {
512
resolvePrompt as resolve,

references/hello-world/src/trigger/prompts.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -302,8 +302,12 @@ export const testPromptManagement = task({
302302
const versions = await prompts.versions(slug);
303303
logger.info("Listed versions", { slug, count: versions.data.length });
304304

305-
// Resolve the prompt (standalone, not via PromptHandle)
306-
const resolved = await prompts.resolve(slug, { customerName: "SDK Test", plan: "Enterprise", issue: "Testing management API" });
305+
// Resolve the prompt (standalone, typesafe via generic)
306+
const resolved = await prompts.resolve<typeof supportPrompt>("customer-support", {
307+
customerName: "SDK Test",
308+
plan: "Enterprise",
309+
issue: "Testing management API",
310+
});
307311
logger.info("Resolved prompt standalone", { version: resolved.version, textLength: resolved.text.length });
308312

309313
// Create an override
@@ -315,7 +319,11 @@ export const testPromptManagement = task({
315319
logger.info("Created override", { version: override.version });
316320

317321
// Resolve again — should get the override
318-
const resolvedOverride = await prompts.resolve(slug, { customerName: "SDK Test", plan: "Enterprise", issue: "Testing override" });
322+
const resolvedOverride = await prompts.resolve<typeof supportPrompt>("customer-support", {
323+
customerName: "SDK Test",
324+
plan: "Enterprise",
325+
issue: "Testing override",
326+
});
319327
logger.info("Resolved with override", { version: resolvedOverride.version, text: resolvedOverride.text });
320328

321329
// Update the override

0 commit comments

Comments
 (0)