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	"charm.land/fantasy/providers/openai"
 9	"github.com/openai/openai-go/v2/option"
10)
11
12type options struct {
13	openaiOptions        []openai.Option
14	languageModelOptions []openai.LanguageModelOption
15}
16
17const (
18	// DefaultURL is the default URL for the OpenRouter API.
19	DefaultURL = "https://openrouter.ai/api/v1"
20	// Name is the name of the OpenRouter provider.
21	Name = "openrouter"
22)
23
24// Option defines a function that configures OpenRouter provider options.
25type Option = func(*options)
26
27// New creates a new OpenRouter provider with the given options.
28func New(opts ...Option) (fantasy.Provider, error) {
29	providerOptions := options{
30		openaiOptions: []openai.Option{
31			openai.WithName(Name),
32			openai.WithBaseURL(DefaultURL),
33		},
34		languageModelOptions: []openai.LanguageModelOption{
35			openai.WithLanguageModelPrepareCallFunc(languagePrepareModelCall),
36			openai.WithLanguageModelUsageFunc(languageModelUsage),
37			openai.WithLanguageModelStreamUsageFunc(languageModelStreamUsage),
38			openai.WithLanguageModelStreamExtraFunc(languageModelStreamExtra),
39			openai.WithLanguageModelExtraContentFunc(languageModelExtraContent),
40			openai.WithLanguageModelToPromptFunc(languageModelToPrompt),
41		},
42	}
43	for _, o := range opts {
44		o(&providerOptions)
45	}
46
47	providerOptions.openaiOptions = append(providerOptions.openaiOptions, openai.WithLanguageModelOptions(providerOptions.languageModelOptions...))
48	return openai.New(providerOptions.openaiOptions...)
49}
50
51// WithAPIKey sets the API key for the OpenRouter provider.
52func WithAPIKey(apiKey string) Option {
53	return func(o *options) {
54		o.openaiOptions = append(o.openaiOptions, openai.WithAPIKey(apiKey))
55	}
56}
57
58// WithName sets the name for the OpenRouter provider.
59func WithName(name string) Option {
60	return func(o *options) {
61		o.openaiOptions = append(o.openaiOptions, openai.WithName(name))
62	}
63}
64
65// WithHeaders sets the headers for the OpenRouter provider.
66func WithHeaders(headers map[string]string) Option {
67	return func(o *options) {
68		o.openaiOptions = append(o.openaiOptions, openai.WithHeaders(headers))
69	}
70}
71
72// WithHTTPClient sets the HTTP client for the OpenRouter provider.
73func WithHTTPClient(client option.HTTPClient) Option {
74	return func(o *options) {
75		o.openaiOptions = append(o.openaiOptions, openai.WithHTTPClient(client))
76	}
77}
78
79func structToMapJSON(s any) (map[string]any, error) {
80	var result map[string]any
81	jsonBytes, err := json.Marshal(s)
82	if err != nil {
83		return nil, err
84	}
85	err = json.Unmarshal(jsonBytes, &result)
86	if err != nil {
87		return nil, err
88	}
89	return result, nil
90}