responses_options.go

  1// Package openai provides an implementation of the fantasy AI SDK for OpenAI's language models.
  2package openai
  3
  4import (
  5	"encoding/json"
  6	"slices"
  7
  8	"charm.land/fantasy"
  9)
 10
 11// Global type identifiers for OpenAI Responses API-specific data.
 12const (
 13	TypeResponsesProviderOptions   = Name + ".responses.options"
 14	TypeResponsesReasoningMetadata = Name + ".responses.reasoning_metadata"
 15	TypeWebSearchCallMetadata      = Name + ".responses.web_search_call_metadata"
 16)
 17
 18// Register OpenAI Responses API-specific types with the global registry.
 19func init() {
 20	fantasy.RegisterProviderType(TypeResponsesProviderOptions, func(data []byte) (fantasy.ProviderOptionsData, error) {
 21		var v ResponsesProviderOptions
 22		if err := json.Unmarshal(data, &v); err != nil {
 23			return nil, err
 24		}
 25		return &v, nil
 26	})
 27	fantasy.RegisterProviderType(TypeResponsesReasoningMetadata, func(data []byte) (fantasy.ProviderOptionsData, error) {
 28		var v ResponsesReasoningMetadata
 29		if err := json.Unmarshal(data, &v); err != nil {
 30			return nil, err
 31		}
 32		return &v, nil
 33	})
 34	fantasy.RegisterProviderType(TypeWebSearchCallMetadata, func(data []byte) (fantasy.ProviderOptionsData, error) {
 35		var v WebSearchCallMetadata
 36		if err := json.Unmarshal(data, &v); err != nil {
 37			return nil, err
 38		}
 39		return &v, nil
 40	})
 41}
 42
 43// ResponsesReasoningMetadata represents reasoning metadata for OpenAI Responses API.
 44type ResponsesReasoningMetadata struct {
 45	ItemID           string   `json:"item_id"`
 46	EncryptedContent *string  `json:"encrypted_content"`
 47	Summary          []string `json:"summary"`
 48}
 49
 50// Options implements the ProviderOptions interface.
 51func (*ResponsesReasoningMetadata) Options() {}
 52
 53// MarshalJSON implements custom JSON marshaling with type info for ResponsesReasoningMetadata.
 54func (m ResponsesReasoningMetadata) MarshalJSON() ([]byte, error) {
 55	type plain ResponsesReasoningMetadata
 56	return fantasy.MarshalProviderType(TypeResponsesReasoningMetadata, plain(m))
 57}
 58
 59// UnmarshalJSON implements custom JSON unmarshaling with type info for ResponsesReasoningMetadata.
 60func (m *ResponsesReasoningMetadata) UnmarshalJSON(data []byte) error {
 61	type plain ResponsesReasoningMetadata
 62	var p plain
 63	if err := fantasy.UnmarshalProviderType(data, &p); err != nil {
 64		return err
 65	}
 66	*m = ResponsesReasoningMetadata(p)
 67	return nil
 68}
 69
 70// IncludeType represents the type of content to include for OpenAI Responses API.
 71type IncludeType string
 72
 73const (
 74	// IncludeReasoningEncryptedContent includes encrypted reasoning content.
 75	IncludeReasoningEncryptedContent IncludeType = "reasoning.encrypted_content"
 76	// IncludeFileSearchCallResults includes file search call results.
 77	IncludeFileSearchCallResults IncludeType = "file_search_call.results"
 78	// IncludeMessageOutputTextLogprobs includes message output text log probabilities.
 79	IncludeMessageOutputTextLogprobs IncludeType = "message.output_text.logprobs"
 80)
 81
 82// ServiceTier represents the service tier for OpenAI Responses API.
 83type ServiceTier string
 84
 85const (
 86	// ServiceTierAuto represents the auto service tier.
 87	ServiceTierAuto ServiceTier = "auto"
 88	// ServiceTierFlex represents the flex service tier.
 89	ServiceTierFlex ServiceTier = "flex"
 90	// ServiceTierPriority represents the priority service tier.
 91	ServiceTierPriority ServiceTier = "priority"
 92)
 93
 94// TextVerbosity represents the text verbosity level for OpenAI Responses API.
 95type TextVerbosity string
 96
 97const (
 98	// TextVerbosityLow represents low text verbosity.
 99	TextVerbosityLow TextVerbosity = "low"
100	// TextVerbosityMedium represents medium text verbosity.
101	TextVerbosityMedium TextVerbosity = "medium"
102	// TextVerbosityHigh represents high text verbosity.
103	TextVerbosityHigh TextVerbosity = "high"
104)
105
106// ResponsesProviderOptions represents additional options for OpenAI Responses API.
107type ResponsesProviderOptions struct {
108	Include           []IncludeType    `json:"include"`
109	Instructions      *string          `json:"instructions"`
110	Logprobs          any              `json:"logprobs"`
111	MaxToolCalls      *int64           `json:"max_tool_calls"`
112	Metadata          map[string]any   `json:"metadata"`
113	ParallelToolCalls *bool            `json:"parallel_tool_calls"`
114	PromptCacheKey    *string          `json:"prompt_cache_key"`
115	ReasoningEffort   *ReasoningEffort `json:"reasoning_effort"`
116	ReasoningSummary  *string          `json:"reasoning_summary"`
117	SafetyIdentifier  *string          `json:"safety_identifier"`
118	ServiceTier       *ServiceTier     `json:"service_tier"`
119	StrictJSONSchema  *bool            `json:"strict_json_schema"`
120	TextVerbosity     *TextVerbosity   `json:"text_verbosity"`
121	User              *string          `json:"user"`
122}
123
124// Options implements the ProviderOptions interface.
125func (*ResponsesProviderOptions) Options() {}
126
127// MarshalJSON implements custom JSON marshaling with type info for ResponsesProviderOptions.
128func (o ResponsesProviderOptions) MarshalJSON() ([]byte, error) {
129	type plain ResponsesProviderOptions
130	return fantasy.MarshalProviderType(TypeResponsesProviderOptions, plain(o))
131}
132
133// UnmarshalJSON implements custom JSON unmarshaling with type info for ResponsesProviderOptions.
134func (o *ResponsesProviderOptions) UnmarshalJSON(data []byte) error {
135	type plain ResponsesProviderOptions
136	var p plain
137	if err := fantasy.UnmarshalProviderType(data, &p); err != nil {
138		return err
139	}
140	*o = ResponsesProviderOptions(p)
141	return nil
142}
143
144// responsesReasoningModelIds lists the model IDs that support reasoning for OpenAI Responses API.
145var responsesReasoningModelIDs = []string{
146	"o1",
147	"o1-2024-12-17",
148	"o3-mini",
149	"o3-mini-2025-01-31",
150	"o3",
151	"o3-2025-04-16",
152	"o4-mini",
153	"o4-mini-2025-04-16",
154	"codex-mini-latest",
155	"gpt-5",
156	"gpt-5-2025-08-07",
157	"gpt-5-mini",
158	"gpt-5-mini-2025-08-07",
159	"gpt-5-nano",
160	"gpt-5-nano-2025-08-07",
161	"gpt-5-codex",
162	"gpt-5-chat",
163	"gpt-5-pro",
164	"gpt-5.1",
165	"gpt-5.1-codex",
166	"gpt-5.1-codex-max",
167	"gpt-5.1-codex-mini",
168	"gpt-5.1-chat",
169	"gpt-5.2",
170	"gpt-5.2-codex",
171	"gpt-5.3",
172	"gpt-5.3-codex",
173	"gpt-5.4",
174	"gpt-5.4-pro",
175	"gpt-5.4-mini",
176	"gpt-5.4-nano",
177	"gpt-5.4-codex",
178	"gpt-oss-120b",
179}
180
181// responsesModelIds lists all model IDs for OpenAI Responses API.
182var responsesModelIDs = append([]string{
183	"gpt-4.1",
184	"gpt-4.1-2025-04-14",
185	"gpt-4.1-mini",
186	"gpt-4.1-mini-2025-04-14",
187	"gpt-4.1-nano",
188	"gpt-4.1-nano-2025-04-14",
189	"gpt-4o",
190	"gpt-4o-2024-05-13",
191	"gpt-4o-2024-08-06",
192	"gpt-4o-2024-11-20",
193	"gpt-4o-mini",
194	"gpt-4o-mini-2024-07-18",
195	"gpt-4-turbo",
196	"gpt-4-turbo-2024-04-09",
197	"gpt-4-turbo-preview",
198	"gpt-4-0125-preview",
199	"gpt-4-1106-preview",
200	"gpt-4",
201	"gpt-4-0613",
202	"gpt-4.5-preview",
203	"gpt-4.5-preview-2025-02-27",
204	"gpt-3.5-turbo-0125",
205	"gpt-3.5-turbo",
206	"gpt-3.5-turbo-1106",
207	"chatgpt-4o-latest",
208	"gpt-5-chat-latest",
209}, responsesReasoningModelIDs...)
210
211// NewResponsesProviderOptions creates new provider options for OpenAI Responses API.
212func NewResponsesProviderOptions(opts *ResponsesProviderOptions) fantasy.ProviderOptions {
213	return fantasy.ProviderOptions{
214		Name: opts,
215	}
216}
217
218// ParseResponsesOptions parses provider options from a map for OpenAI Responses API.
219func ParseResponsesOptions(data map[string]any) (*ResponsesProviderOptions, error) {
220	var options ResponsesProviderOptions
221	if err := fantasy.ParseOptions(data, &options); err != nil {
222		return nil, err
223	}
224	return &options, nil
225}
226
227// IsResponsesModel checks if a model ID is a Responses API model for OpenAI.
228func IsResponsesModel(modelID string) bool {
229	return slices.Contains(responsesModelIDs, modelID)
230}
231
232// IsResponsesReasoningModel checks if a model ID is a Responses API reasoning model for OpenAI.
233func IsResponsesReasoningModel(modelID string) bool {
234	return slices.Contains(responsesReasoningModelIDs, modelID)
235}
236
237// SearchContextSize controls how much context window space the
238// web search tool uses. Maps to the OpenAI API's
239// search_context_size parameter.
240type SearchContextSize string
241
242const (
243	// SearchContextSizeLow uses minimal context for search results.
244	SearchContextSizeLow SearchContextSize = "low"
245	// SearchContextSizeMedium is the default context size.
246	SearchContextSizeMedium SearchContextSize = "medium"
247	// SearchContextSizeHigh uses maximal context for search results.
248	SearchContextSizeHigh SearchContextSize = "high"
249)
250
251// WebSearchUserLocation provides geographic context for more
252// relevant web search results.
253type WebSearchUserLocation struct {
254	City     string `json:"city,omitempty"`
255	Region   string `json:"region,omitempty"`
256	Country  string `json:"country,omitempty"`
257	Timezone string `json:"timezone,omitempty"`
258}
259
260// WebSearchToolOptions configures the OpenAI web search tool.
261type WebSearchToolOptions struct {
262	// SearchContextSize controls the amount of context window
263	// space used for search results. Defaults to medium.
264	SearchContextSize SearchContextSize
265	// AllowedDomains restricts search results to these domains.
266	// Subdomains are included automatically.
267	AllowedDomains []string
268	// UserLocation provides geographic context for more
269	// relevant search results.
270	UserLocation *WebSearchUserLocation
271}
272
273// WebSearchTool creates a provider-defined web search tool for
274// OpenAI models. Pass nil for default options.
275func WebSearchTool(opts *WebSearchToolOptions) fantasy.ProviderDefinedTool {
276	tool := fantasy.ProviderDefinedTool{
277		ID:   "web_search",
278		Name: "web_search",
279	}
280	if opts == nil {
281		return tool
282	}
283	args := map[string]any{}
284	if opts.SearchContextSize != "" {
285		args["search_context_size"] = opts.SearchContextSize
286	}
287	if len(opts.AllowedDomains) > 0 {
288		args["allowed_domains"] = opts.AllowedDomains
289	}
290	if opts.UserLocation != nil {
291		args["user_location"] = opts.UserLocation
292	}
293	if len(args) > 0 {
294		tool.Args = args
295	}
296	return tool
297}
298
299// WebSearchSource represents a single source from a web search action.
300type WebSearchSource struct {
301	Type string `json:"type"`
302	URL  string `json:"url"`
303}
304
305// WebSearchAction represents the action taken during a web search call.
306type WebSearchAction struct {
307	// Type is the kind of action: "search", "open_page", or "find".
308	Type string `json:"type"`
309	// Query is the search query (present when Type is "search").
310	Query string `json:"query,omitempty"`
311	// Sources are the results returned by the search.
312	Sources []WebSearchSource `json:"sources,omitempty"`
313}
314
315// WebSearchCallMetadata stores structured data from a web_search_call
316// output item for round-tripping through multi-turn conversations.
317// The ItemID is used with item_reference for efficient round-tripping
318// when response storage is enabled.
319type WebSearchCallMetadata struct {
320	// ItemID is the server-side ID of the web_search_call output item.
321	ItemID string `json:"item_id"`
322	// Action contains the structured action data from the search.
323	Action *WebSearchAction `json:"action,omitempty"`
324}
325
326// Options implements the ProviderOptionsData interface.
327func (*WebSearchCallMetadata) Options() {}
328
329// MarshalJSON implements custom JSON marshaling with type info.
330func (m WebSearchCallMetadata) MarshalJSON() ([]byte, error) {
331	type plain WebSearchCallMetadata
332	return fantasy.MarshalProviderType(TypeWebSearchCallMetadata, plain(m))
333}
334
335// UnmarshalJSON implements custom JSON unmarshaling with type info.
336func (m *WebSearchCallMetadata) UnmarshalJSON(data []byte) error {
337	type plain WebSearchCallMetadata
338	var p plain
339	if err := fantasy.UnmarshalProviderType(data, &p); err != nil {
340		return err
341	}
342	*m = WebSearchCallMetadata(p)
343	return nil
344}