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