@@ -58,6 +58,8 @@ export type Model = typeof ResponseModelIds.Encoded | typeof SharedModelIds.Enco
5858 */
5959type ImageDetail = "auto" | "low" | "high"
6060
61+ type PromptCacheBreakpoint = { readonly mode : "explicit" }
62+
6163// =============================================================================
6264// Configuration
6365// =============================================================================
@@ -127,6 +129,24 @@ export class Config extends Context.Service<
127129// =============================================================================
128130
129131declare module "effect/unstable/ai/Prompt" {
132+ /**
133+ * OpenAI-specific options for system messages.
134+ *
135+ * @category models
136+ * @since 4.0.0
137+ */
138+ export interface SystemMessageOptions extends ProviderOptions {
139+ /**
140+ * Provider-specific system message options for the OpenAI Responses API.
141+ */
142+ readonly openai ?: {
143+ /**
144+ * Marks the system input text as the end of a reusable prompt prefix.
145+ */
146+ readonly promptCacheBreakpoint ?: PromptCacheBreakpoint | null
147+ } | null
148+ }
149+
130150 /**
131151 * OpenAI-specific options for file prompt parts.
132152 *
@@ -244,6 +264,10 @@ declare module "effect/unstable/ai/Prompt" {
244264 * A list of annotations that apply to the output text.
245265 */
246266 readonly annotations ?: ReadonlyArray < typeof OpenAiSchema . Annotation . Encoded > | null
267+ /**
268+ * Marks the input text as the end of a reusable prompt prefix.
269+ */
270+ readonly promptCacheBreakpoint ?: PromptCacheBreakpoint | null
247271 } | null
248272 }
249273}
@@ -817,9 +841,16 @@ const prepareMessages = Effect.fnUntraced(
817841 for ( const message of prompt . content ) {
818842 switch ( message . role ) {
819843 case "system" : {
844+ const promptCacheBreakpoint = getPromptCacheBreakpoint ( message )
820845 messages . push ( {
821846 role : getSystemMessageMode ( config . model as string ) ,
822- content : [ { type : "input_text" , text : message . content } ]
847+ content : [ {
848+ type : "input_text" ,
849+ text : message . content ,
850+ ...( Predicate . isNotNull ( promptCacheBreakpoint )
851+ ? { prompt_cache_breakpoint : promptCacheBreakpoint }
852+ : undefined )
853+ } ]
823854 } )
824855 break
825856 }
@@ -832,7 +863,14 @@ const prepareMessages = Effect.fnUntraced(
832863
833864 switch ( part . type ) {
834865 case "text" : {
835- content . push ( { type : "input_text" , text : part . text } )
866+ const promptCacheBreakpoint = getPromptCacheBreakpoint ( part )
867+ content . push ( {
868+ type : "input_text" ,
869+ text : part . text ,
870+ ...( Predicate . isNotNull ( promptCacheBreakpoint )
871+ ? { prompt_cache_breakpoint : promptCacheBreakpoint }
872+ : undefined )
873+ } )
836874 break
837875 }
838876
@@ -2910,6 +2948,10 @@ const getEncryptedContent = (
29102948
29112949const getImageDetail = ( part : Prompt . FilePart ) : ImageDetail => part . options . openai ?. imageDetail ?? "auto"
29122950
2951+ const getPromptCacheBreakpoint = (
2952+ input : Prompt . SystemMessage | Prompt . TextPart
2953+ ) : PromptCacheBreakpoint | null => input . options . openai ?. promptCacheBreakpoint ?? null
2954+
29132955const makeItemIdMetadata = ( itemId : string | undefined ) => Predicate . isNotUndefined ( itemId ) ? { itemId } : { }
29142956
29152957const makeEncryptedContentMetadata = ( encryptedContent : string | null | undefined ) =>
0 commit comments