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