openrouter.go

 1package openrouter
 2
 3import (
 4	"encoding/json"
 5
 6	"github.com/charmbracelet/fantasy/ai"
 7	"github.com/charmbracelet/fantasy/openai"
 8	"github.com/openai/openai-go/v2/option"
 9)
10
11type options struct {
12	openaiOptions        []openai.Option
13	languageModelOptions []openai.LanguageModelOption
14}
15
16const (
17	DefaultURL = "https://openrouter.ai/api/v1"
18	Name       = "openrouter"
19)
20
21type Option = func(*options)
22
23func New(opts ...Option) ai.Provider {
24	providerOptions := options{
25		openaiOptions: []openai.Option{
26			openai.WithName(Name),
27			openai.WithBaseURL(DefaultURL),
28		},
29		languageModelOptions: []openai.LanguageModelOption{
30			openai.WithLanguageModelPrepareCallFunc(languagePrepareModelCall),
31			openai.WithLanguageModelUsageFunc(languageModelUsage),
32			openai.WithLanguageModelStreamUsageFunc(languageModelStreamUsage),
33			openai.WithLanguageModelStreamExtraFunc(languageModelStreamExtra),
34			openai.WithLanguageModelExtraContentFunc(languageModelExtraContent),
35			openai.WithLanguageModelMapFinishReasonFunc(languageModelMapFinishReason),
36		},
37	}
38	for _, o := range opts {
39		o(&providerOptions)
40	}
41
42	providerOptions.openaiOptions = append(providerOptions.openaiOptions, openai.WithLanguageModelOptions(providerOptions.languageModelOptions...))
43	return openai.New(providerOptions.openaiOptions...)
44}
45
46func WithAPIKey(apiKey string) Option {
47	return func(o *options) {
48		o.openaiOptions = append(o.openaiOptions, openai.WithAPIKey(apiKey))
49	}
50}
51
52func WithName(name string) Option {
53	return func(o *options) {
54		o.openaiOptions = append(o.openaiOptions, openai.WithName(name))
55	}
56}
57
58func WithHeaders(headers map[string]string) Option {
59	return func(o *options) {
60		o.openaiOptions = append(o.openaiOptions, openai.WithHeaders(headers))
61	}
62}
63
64func WithHTTPClient(client option.HTTPClient) Option {
65	return func(o *options) {
66		o.openaiOptions = append(o.openaiOptions, openai.WithHTTPClient(client))
67	}
68}
69
70func WithLanguageUniqueToolCallIds() Option {
71	return func(l *options) {
72		l.languageModelOptions = append(l.languageModelOptions, openai.WithLanguageUniqueToolCallIds())
73	}
74}
75
76func WithLanguageModelGenerateIDFunc(fn openai.LanguageModelGenerateIDFunc) Option {
77	return func(l *options) {
78		l.languageModelOptions = append(l.languageModelOptions, openai.WithLanguageModelGenerateIDFunc(fn))
79	}
80}
81
82func structToMapJSON(s any) (map[string]any, error) {
83	var result map[string]any
84	jsonBytes, err := json.Marshal(s)
85	if err != nil {
86		return nil, err
87	}
88	err = json.Unmarshal(jsonBytes, &result)
89	if err != nil {
90		return nil, err
91	}
92	return result, nil
93}