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)
 16
 17// ResponsesReasoningMetadata represents reasoning metadata for OpenAI Responses API.
 18type ResponsesReasoningMetadata struct {
 19	ItemID           string   `json:"item_id"`
 20	EncryptedContent *string  `json:"encrypted_content"`
 21	Summary          []string `json:"summary"`
 22}
 23
 24// Options implements the ProviderOptions interface.
 25func (*ResponsesReasoningMetadata) Options() {}
 26
 27// MarshalJSON implements custom JSON marshaling with type info for ResponsesReasoningMetadata.
 28func (m ResponsesReasoningMetadata) MarshalJSON() ([]byte, error) {
 29	type plain ResponsesReasoningMetadata
 30	raw, err := json.Marshal(plain(m))
 31	if err != nil {
 32		return nil, err
 33	}
 34	return json.Marshal(struct {
 35		Type string          `json:"type"`
 36		Data json.RawMessage `json:"data"`
 37	}{
 38		Type: TypeResponsesReasoningMetadata,
 39		Data: raw,
 40	})
 41}
 42
 43// UnmarshalJSON implements custom JSON unmarshaling with type info for ResponsesReasoningMetadata.
 44func (m *ResponsesReasoningMetadata) UnmarshalJSON(data []byte) error {
 45	type plain ResponsesReasoningMetadata
 46	var rm plain
 47	err := json.Unmarshal(data, &rm)
 48	if err != nil {
 49		return err
 50	}
 51	*m = ResponsesReasoningMetadata(rm)
 52	return nil
 53}
 54
 55// IncludeType represents the type of content to include for OpenAI Responses API.
 56type IncludeType string
 57
 58const (
 59	// IncludeReasoningEncryptedContent includes encrypted reasoning content.
 60	IncludeReasoningEncryptedContent IncludeType = "reasoning.encrypted_content"
 61	// IncludeFileSearchCallResults includes file search call results.
 62	IncludeFileSearchCallResults IncludeType = "file_search_call.results"
 63	// IncludeMessageOutputTextLogprobs includes message output text log probabilities.
 64	IncludeMessageOutputTextLogprobs IncludeType = "message.output_text.logprobs"
 65)
 66
 67// ServiceTier represents the service tier for OpenAI Responses API.
 68type ServiceTier string
 69
 70const (
 71	// ServiceTierAuto represents the auto service tier.
 72	ServiceTierAuto ServiceTier = "auto"
 73	// ServiceTierFlex represents the flex service tier.
 74	ServiceTierFlex ServiceTier = "flex"
 75	// ServiceTierPriority represents the priority service tier.
 76	ServiceTierPriority ServiceTier = "priority"
 77)
 78
 79// TextVerbosity represents the text verbosity level for OpenAI Responses API.
 80type TextVerbosity string
 81
 82const (
 83	// TextVerbosityLow represents low text verbosity.
 84	TextVerbosityLow TextVerbosity = "low"
 85	// TextVerbosityMedium represents medium text verbosity.
 86	TextVerbosityMedium TextVerbosity = "medium"
 87	// TextVerbosityHigh represents high text verbosity.
 88	TextVerbosityHigh TextVerbosity = "high"
 89)
 90
 91// ResponsesProviderOptions represents additional options for OpenAI Responses API.
 92type ResponsesProviderOptions struct {
 93	Include           []IncludeType    `json:"include"`
 94	Instructions      *string          `json:"instructions"`
 95	Logprobs          any              `json:"logprobs"`
 96	MaxToolCalls      *int64           `json:"max_tool_calls"`
 97	Metadata          map[string]any   `json:"metadata"`
 98	ParallelToolCalls *bool            `json:"parallel_tool_calls"`
 99	PromptCacheKey    *string          `json:"prompt_cache_key"`
100	ReasoningEffort   *ReasoningEffort `json:"reasoning_effort"`
101	ReasoningSummary  *string          `json:"reasoning_summary"`
102	SafetyIdentifier  *string          `json:"safety_identifier"`
103	ServiceTier       *ServiceTier     `json:"service_tier"`
104	StrictJSONSchema  *bool            `json:"strict_json_schema"`
105	TextVerbosity     *TextVerbosity   `json:"text_verbosity"`
106	User              *string          `json:"user"`
107}
108
109// Options implements the ProviderOptions interface.
110func (*ResponsesProviderOptions) Options() {}
111
112// MarshalJSON implements custom JSON marshaling with type info for ResponsesProviderOptions.
113func (o ResponsesProviderOptions) MarshalJSON() ([]byte, error) {
114	type plain ResponsesProviderOptions
115	raw, err := json.Marshal(plain(o))
116	if err != nil {
117		return nil, err
118	}
119	return json.Marshal(struct {
120		Type string          `json:"type"`
121		Data json.RawMessage `json:"data"`
122	}{
123		Type: TypeResponsesProviderOptions,
124		Data: raw,
125	})
126}
127
128// UnmarshalJSON implements custom JSON unmarshaling with type info for ResponsesProviderOptions.
129func (o *ResponsesProviderOptions) UnmarshalJSON(data []byte) error {
130	type plain ResponsesProviderOptions
131	var ro plain
132	err := json.Unmarshal(data, &ro)
133	if err != nil {
134		return err
135	}
136	*o = ResponsesProviderOptions(ro)
137	return nil
138}
139
140// responsesReasoningModelIds lists the model IDs that support reasoning for OpenAI Responses API.
141var responsesReasoningModelIDs = []string{
142	"o1",
143	"o1-2024-12-17",
144	"o3-mini",
145	"o3-mini-2025-01-31",
146	"o3",
147	"o3-2025-04-16",
148	"o4-mini",
149	"o4-mini-2025-04-16",
150	"codex-mini-latest",
151	"gpt-5",
152	"gpt-5-2025-08-07",
153	"gpt-5-mini",
154	"gpt-5-mini-2025-08-07",
155	"gpt-5-nano",
156	"gpt-5-nano-2025-08-07",
157	"gpt-5-codex",
158}
159
160// responsesModelIds lists all model IDs for OpenAI Responses API.
161var responsesModelIDs = append([]string{
162	"gpt-4.1",
163	"gpt-4.1-2025-04-14",
164	"gpt-4.1-mini",
165	"gpt-4.1-mini-2025-04-14",
166	"gpt-4.1-nano",
167	"gpt-4.1-nano-2025-04-14",
168	"gpt-4o",
169	"gpt-4o-2024-05-13",
170	"gpt-4o-2024-08-06",
171	"gpt-4o-2024-11-20",
172	"gpt-4o-mini",
173	"gpt-4o-mini-2024-07-18",
174	"gpt-4-turbo",
175	"gpt-4-turbo-2024-04-09",
176	"gpt-4-turbo-preview",
177	"gpt-4-0125-preview",
178	"gpt-4-1106-preview",
179	"gpt-4",
180	"gpt-4-0613",
181	"gpt-4.5-preview",
182	"gpt-4.5-preview-2025-02-27",
183	"gpt-3.5-turbo-0125",
184	"gpt-3.5-turbo",
185	"gpt-3.5-turbo-1106",
186	"chatgpt-4o-latest",
187	"gpt-5-chat-latest",
188}, responsesReasoningModelIDs...)
189
190// NewResponsesProviderOptions creates new provider options for OpenAI Responses API.
191func NewResponsesProviderOptions(opts *ResponsesProviderOptions) fantasy.ProviderOptions {
192	return fantasy.ProviderOptions{
193		Name: opts,
194	}
195}
196
197// ParseResponsesOptions parses provider options from a map for OpenAI Responses API.
198func ParseResponsesOptions(data map[string]any) (*ResponsesProviderOptions, error) {
199	var options ResponsesProviderOptions
200	if err := fantasy.ParseOptions(data, &options); err != nil {
201		return nil, err
202	}
203	return &options, nil
204}
205
206// IsResponsesModel checks if a model ID is a Responses API model for OpenAI.
207func IsResponsesModel(modelID string) bool {
208	return slices.Contains(responsesModelIDs, modelID)
209}
210
211// IsResponsesReasoningModel checks if a model ID is a Responses API reasoning model for OpenAI.
212func IsResponsesReasoningModel(modelID string) bool {
213	return slices.Contains(responsesReasoningModelIDs, modelID)
214}
215
216// Register OpenAI Responses API-specific types with the global registry.
217func init() {
218	fantasy.RegisterProviderType(TypeResponsesProviderOptions, func(data []byte) (fantasy.ProviderOptionsData, error) {
219		var v ResponsesProviderOptions
220		if err := json.Unmarshal(data, &v); err != nil {
221			return nil, err
222		}
223		return &v, nil
224	})
225	fantasy.RegisterProviderType(TypeResponsesReasoningMetadata, func(data []byte) (fantasy.ProviderOptionsData, error) {
226		var v ResponsesReasoningMetadata
227		if err := json.Unmarshal(data, &v); err != nil {
228			return nil, err
229		}
230		return &v, nil
231	})
232}