1// Package openrouter provides an implementation of the fantasy AI SDK for OpenRouter's language models.
2package openrouter
3
4import (
5 "encoding/json"
6
7 "charm.land/fantasy"
8)
9
10// ReasoningEffort represents the reasoning effort level for OpenRouter models.
11type ReasoningEffort string
12
13const (
14 // ReasoningEffortLow represents low reasoning effort.
15 ReasoningEffortLow ReasoningEffort = "low"
16 // ReasoningEffortMedium represents medium reasoning effort.
17 ReasoningEffortMedium ReasoningEffort = "medium"
18 // ReasoningEffortHigh represents high reasoning effort.
19 ReasoningEffortHigh ReasoningEffort = "high"
20)
21
22// Global type identifiers for OpenRouter-specific provider data.
23const (
24 TypeProviderOptions = Name + ".options"
25 TypeProviderMetadata = Name + ".metadata"
26)
27
28// PromptTokensDetails represents details about prompt tokens for OpenRouter.
29type PromptTokensDetails struct {
30 CachedTokens int64
31}
32
33// CompletionTokensDetails represents details about completion tokens for OpenRouter.
34type CompletionTokensDetails struct {
35 ReasoningTokens int64
36}
37
38// CostDetails represents cost details for OpenRouter.
39type CostDetails struct {
40 UpstreamInferenceCost float64 `json:"upstream_inference_cost"`
41 UpstreamInferencePromptCost float64 `json:"upstream_inference_prompt_cost"`
42 UpstreamInferenceCompletionsCost float64 `json:"upstream_inference_completions_cost"`
43}
44
45// UsageAccounting represents usage accounting details for OpenRouter.
46type UsageAccounting struct {
47 PromptTokens int64 `json:"prompt_tokens"`
48 PromptTokensDetails PromptTokensDetails `json:"prompt_tokens_details"`
49 CompletionTokens int64 `json:"completion_tokens"`
50 CompletionTokensDetails CompletionTokensDetails `json:"completion_tokens_details"`
51 TotalTokens int64 `json:"total_tokens"`
52 Cost float64 `json:"cost"`
53 CostDetails CostDetails `json:"cost_details"`
54}
55
56// ProviderMetadata represents metadata from OpenRouter provider.
57type ProviderMetadata struct {
58 Provider string `json:"provider"`
59 Usage UsageAccounting `json:"usage"`
60}
61
62// Options implements the ProviderOptionsData interface for ProviderMetadata.
63func (*ProviderMetadata) Options() {}
64
65// MarshalJSON implements custom JSON marshaling with type info for ProviderMetadata.
66func (m ProviderMetadata) MarshalJSON() ([]byte, error) {
67 type plain ProviderMetadata
68 raw, err := json.Marshal(plain(m))
69 if err != nil {
70 return nil, err
71 }
72 return json.Marshal(struct {
73 Type string `json:"type"`
74 Data json.RawMessage `json:"data"`
75 }{
76 Type: TypeProviderMetadata,
77 Data: raw,
78 })
79}
80
81// UnmarshalJSON implements custom JSON unmarshaling with type info for ProviderMetadata.
82func (m *ProviderMetadata) UnmarshalJSON(data []byte) error {
83 type plain ProviderMetadata
84 var pm plain
85 err := json.Unmarshal(data, &pm)
86 if err != nil {
87 return err
88 }
89 *m = ProviderMetadata(pm)
90 return nil
91}
92
93// ReasoningOptions represents reasoning options for OpenRouter.
94type ReasoningOptions struct {
95 // Whether reasoning is enabled
96 Enabled *bool `json:"enabled,omitempty"`
97 // Whether to exclude reasoning from the response
98 Exclude *bool `json:"exclude,omitempty"`
99 // Maximum number of tokens to use for reasoning
100 MaxTokens *int64 `json:"max_tokens,omitempty"`
101 // Reasoning effort level: "low" | "medium" | "high"
102 Effort *ReasoningEffort `json:"effort,omitempty"`
103}
104
105// Provider represents provider routing preferences for OpenRouter.
106type Provider struct {
107 // List of provider slugs to try in order (e.g. ["anthropic", "openai"])
108 Order []string `json:"order,omitempty"`
109 // Whether to allow backup providers when primary is unavailable (default: true)
110 AllowFallbacks *bool `json:"allow_fallbacks,omitempty"`
111 // Only use providers that support all parameters in your request (default: false)
112 RequireParameters *bool `json:"require_parameters,omitempty"`
113 // Control whether to use providers that may store data: "allow" | "deny"
114 DataCollection *string `json:"data_collection,omitempty"`
115 // List of provider slugs to allow for this request
116 Only []string `json:"only,omitempty"`
117 // List of provider slugs to skip for this request
118 Ignore []string `json:"ignore,omitempty"`
119 // List of quantization levels to filter by (e.g. ["int4", "int8"])
120 Quantizations []string `json:"quantizations,omitempty"`
121 // Sort providers by "price" | "throughput" | "latency"
122 Sort *string `json:"sort,omitempty"`
123}
124
125// ProviderOptions represents additional options for OpenRouter provider.
126type ProviderOptions struct {
127 Reasoning *ReasoningOptions `json:"reasoning,omitempty"`
128 ExtraBody map[string]any `json:"extra_body,omitempty"`
129 IncludeUsage *bool `json:"include_usage,omitempty"`
130 // Modify the likelihood of specified tokens appearing in the completion.
131 // Accepts a map that maps tokens (specified by their token ID) to an associated bias value from -100 to 100.
132 // The bias is added to the logits generated by the model prior to sampling.
133 LogitBias map[string]int64 `json:"logit_bias,omitempty"`
134 // Return the log probabilities of the tokens. Including logprobs will increase the response size.
135 // Setting to true will return the log probabilities of the tokens that were generated.
136 LogProbs *bool `json:"log_probs,omitempty"`
137 // Whether to enable parallel function calling during tool use. Default to true.
138 ParallelToolCalls *bool `json:"parallel_tool_calls,omitempty"`
139 // A unique identifier representing your end-user, which can help OpenRouter to monitor and detect abuse.
140 User *string `json:"user,omitempty"`
141 // Provider routing preferences to control request routing behavior
142 Provider *Provider `json:"provider,omitempty"`
143 // TODO: add the web search plugin config
144}
145
146// Options implements the ProviderOptionsData interface for ProviderOptions.
147func (*ProviderOptions) Options() {}
148
149// MarshalJSON implements custom JSON marshaling with type info for ProviderOptions.
150func (o ProviderOptions) MarshalJSON() ([]byte, error) {
151 type plain ProviderOptions
152 raw, err := json.Marshal(plain(o))
153 if err != nil {
154 return nil, err
155 }
156 return json.Marshal(struct {
157 Type string `json:"type"`
158 Data json.RawMessage `json:"data"`
159 }{
160 Type: TypeProviderOptions,
161 Data: raw,
162 })
163}
164
165// UnmarshalJSON implements custom JSON unmarshaling with type info for ProviderOptions.
166func (o *ProviderOptions) UnmarshalJSON(data []byte) error {
167 type plain ProviderOptions
168 var oo plain
169 err := json.Unmarshal(data, &oo)
170 if err != nil {
171 return err
172 }
173 *o = ProviderOptions(oo)
174 return nil
175}
176
177// ReasoningDetail represents a reasoning detail for OpenRouter.
178type ReasoningDetail struct {
179 ID string `json:"id,omitempty"`
180 Type string `json:"type,omitempty"`
181 Text string `json:"text,omitempty"`
182 Data string `json:"data,omitempty"`
183 Format string `json:"format,omitempty"`
184 Summary string `json:"summary,omitempty"`
185 Signature string `json:"signature,omitempty"`
186 Index int `json:"index"`
187}
188
189// ReasoningData represents reasoning data for OpenRouter.
190type ReasoningData struct {
191 Reasoning string `json:"reasoning"`
192 ReasoningDetails []ReasoningDetail `json:"reasoning_details"`
193}
194
195// ReasoningEffortOption creates a pointer to a ReasoningEffort value for OpenRouter.
196func ReasoningEffortOption(e ReasoningEffort) *ReasoningEffort {
197 return &e
198}
199
200// NewProviderOptions creates new provider options for OpenRouter.
201func NewProviderOptions(opts *ProviderOptions) fantasy.ProviderOptions {
202 return fantasy.ProviderOptions{
203 Name: opts,
204 }
205}
206
207// ParseOptions parses provider options from a map for OpenRouter.
208func ParseOptions(data map[string]any) (*ProviderOptions, error) {
209 var options ProviderOptions
210 if err := fantasy.ParseOptions(data, &options); err != nil {
211 return nil, err
212 }
213 return &options, nil
214}
215
216// Register OpenRouter provider-specific types with the global registry.
217func init() {
218 fantasy.RegisterProviderType(TypeProviderOptions, func(data []byte) (fantasy.ProviderOptionsData, error) {
219 var v ProviderOptions
220 if err := json.Unmarshal(data, &v); err != nil {
221 return nil, err
222 }
223 return &v, nil
224 })
225 fantasy.RegisterProviderType(TypeProviderMetadata, func(data []byte) (fantasy.ProviderOptionsData, error) {
226 var v ProviderMetadata
227 if err := json.Unmarshal(data, &v); err != nil {
228 return nil, err
229 }
230 return &v, nil
231 })
232}