@@ -2,20 +2,38 @@ package main
2
2
3
3
import (
4
4
"context"
5
+ "errors"
5
6
"fmt"
7
+ "strings"
6
8
7
9
// Packages
8
- "github.com/djthorpe/go-tablewriter"
10
+ tablewriter "github.com/djthorpe/go-tablewriter"
9
11
client "github.com/mutablelogic/go-client"
10
12
openai "github.com/mutablelogic/go-client/pkg/openai"
13
+ "github.com/mutablelogic/go-client/pkg/openai/schema"
14
+
15
+ // Namespace imports
16
+ . "github.com/djthorpe/go-errors"
11
17
)
12
18
13
19
///////////////////////////////////////////////////////////////////////////////
14
20
// GLOBALS
15
21
16
22
var (
17
- openaiName = "openai"
18
- openaiClient * openai.Client
23
+ openaiName = "openai"
24
+ openaiClient * openai.Client
25
+ openaiModel string
26
+ openaiQuality bool
27
+ openaiResponseFormat string
28
+ openaiStyle string
29
+ openaiFrequencyPenalty * float64
30
+ openaiPresencePenalty * float64
31
+ openaiMaxTokens uint64
32
+ openaiCount * uint64
33
+ openaiStream bool
34
+ openaiTemperature * float64
35
+ openaiUser string
36
+ openaiSystemPrompt string
19
37
)
20
38
21
39
///////////////////////////////////////////////////////////////////////////////
@@ -24,15 +42,29 @@ var (
24
42
func openaiRegister (flags * Flags ) {
25
43
// Register flags
26
44
flags .String (openaiName , "openai-api-key" , "${OPENAI_API_KEY}" , "OpenAI API key" )
45
+ // TODO flags.String(openaiName, "model", "", "The model to use")
46
+ // TODO flags.Unsigned(openaiName, "max-tokens", 0, "The maximum number of tokens that can be generated in the chat completion")
47
+ flags .Bool (openaiName , "hd" , false , "Create images with finer details and greater consistency across the image" )
48
+ flags .String (openaiName , "response-format" , "" , "The format in which the generated images are returned" )
49
+ flags .String (openaiName , "style" , "" , "The style of the generated images. Must be one of vivid or natural" )
50
+ flags .String (openaiName , "user" , "" , "A unique identifier representing your end-user" )
51
+ flags .Float (openaiName , "frequency-penalty" , 0 , "The model's likelihood to repeat the same line verbatim" )
52
+ flags .Float (openaiName , "presence-penalty" , 0 , "The model's likelihood to talk about new topics" )
53
+ flags .Unsigned (openaiName , "n" , 0 , "How many chat completion choices to generate for each input message" )
54
+ // TODO flags.String(openaiName, "system", "", "The system prompt")
55
+ // TODO flags.Bool(openaiName, "stream", false, "If set, partial message deltas will be sent, like in ChatGPT")
56
+ // TODO flags.Float(openaiName, "temperature", 0, "Sampling temperature to use, between 0.0 and 2.0")
27
57
28
58
// Register commands
29
59
flags .Register (Cmd {
30
60
Name : openaiName ,
31
61
Description : "Interact with OpenAI, from https://platform.openai.com/docs/api-reference" ,
32
62
Parse : openaiParse ,
33
63
Fn : []Fn {
34
- {Name : "models" , Call : openaiModels , Description : "Gets a list of available models" },
35
- {Name : "model" , Call : openaiModel , Description : "Return model information" , MinArgs : 1 , MaxArgs : 1 , Syntax : "<model>" },
64
+ {Name : "models" , Call : openaiListModels , Description : "Gets a list of available models" },
65
+ {Name : "model" , Call : openaiGetModel , Description : "Return model information" , MinArgs : 1 , MaxArgs : 1 , Syntax : "<model>" },
66
+ {Name : "image" , Call : openaiImage , Description : "Create image from a prompt" , MinArgs : 1 , Syntax : "<prompt>" },
67
+ {Name : "chat" , Call : openaiChat , Description : "Create a chat completion" , MinArgs : 1 , Syntax : "<text>..." },
36
68
},
37
69
})
38
70
}
@@ -47,25 +79,151 @@ func openaiParse(flags *Flags, opts ...client.ClientOpt) error {
47
79
openaiClient = client
48
80
}
49
81
82
+ // Set arguments
83
+ openaiModel = flags .GetString ("model" )
84
+ openaiQuality = flags .GetBool ("hd" )
85
+ openaiResponseFormat = flags .GetString ("response-format" )
86
+ openaiStyle = flags .GetString ("style" )
87
+ openaiStream = flags .GetBool ("stream" )
88
+ openaiUser = flags .GetString ("user" )
89
+ openaiSystemPrompt = flags .GetString ("system" )
90
+
91
+ if temp , err := flags .GetValue ("temperature" ); err == nil {
92
+ t := temp .(float64 )
93
+ openaiTemperature = & t
94
+ }
95
+ if value , err := flags .GetValue ("frequency-penalty" ); err == nil {
96
+ v := value .(float64 )
97
+ openaiFrequencyPenalty = & v
98
+ }
99
+ if value , err := flags .GetValue ("presence-penalty" ); err == nil {
100
+ v := value .(float64 )
101
+ openaiPresencePenalty = & v
102
+ }
103
+ if maxtokens , err := flags .GetValue ("max-tokens" ); err == nil {
104
+ t := maxtokens .(uint64 )
105
+ openaiMaxTokens = t
106
+ }
107
+ if count , err := flags .GetValue ("n" ); err == nil {
108
+ v := count .(uint64 )
109
+ openaiCount = & v
110
+ }
111
+
50
112
// Return success
51
113
return nil
52
114
}
53
115
54
116
///////////////////////////////////////////////////////////////////////////////
55
117
// METHODS
56
118
57
- func openaiModels (ctx context.Context , w * tablewriter.Writer , args []string ) error {
119
+ func openaiListModels (ctx context.Context , w * tablewriter.Writer , args []string ) error {
58
120
models , err := openaiClient .ListModels ()
59
121
if err != nil {
60
122
return err
61
123
}
62
124
return w .Write (models )
63
125
}
64
126
65
- func openaiModel (ctx context.Context , w * tablewriter.Writer , args []string ) error {
127
+ func openaiGetModel (ctx context.Context , w * tablewriter.Writer , args []string ) error {
66
128
model , err := openaiClient .GetModel (args [0 ])
67
129
if err != nil {
68
130
return err
69
131
}
70
132
return w .Write (model )
71
133
}
134
+
135
+ func openaiImage (ctx context.Context , w * tablewriter.Writer , args []string ) error {
136
+ opts := []openai.Opt {}
137
+ prompt := strings .Join (args , " " )
138
+
139
+ // Process options
140
+ if openaiModel != "" {
141
+ opts = append (opts , openai .OptModel (openaiModel ))
142
+ }
143
+ if openaiQuality {
144
+ opts = append (opts , openai .OptQuality ("hd" ))
145
+ }
146
+ if openaiResponseFormat != "" {
147
+ opts = append (opts , openai .OptResponseFormat (openaiResponseFormat ))
148
+ }
149
+ if openaiStyle != "" {
150
+ opts = append (opts , openai .OptStyle (openaiStyle ))
151
+ }
152
+ if openaiUser != "" {
153
+ opts = append (opts , openai .OptUser (openaiUser ))
154
+ }
155
+
156
+ // Request->Response
157
+ response , err := openaiClient .CreateImages (ctx , prompt , opts ... )
158
+ if err != nil {
159
+ return err
160
+ } else if len (response ) == 0 {
161
+ return ErrUnexpectedResponse .With ("no images returned" )
162
+ }
163
+
164
+ // Write each image
165
+ var result error
166
+ for _ , image := range response {
167
+ if n , err := openaiClient .WriteImage (w .Output (), image ); err != nil {
168
+ result = errors .Join (result , err )
169
+ } else {
170
+ openaiClient .Debugf ("openaiImage: wrote %v bytes" , n )
171
+ }
172
+ }
173
+
174
+ // Return success
175
+ return nil
176
+ }
177
+
178
+ func openaiChat (ctx context.Context , w * tablewriter.Writer , args []string ) error {
179
+ var messages []* schema.Message
180
+
181
+ // Set options
182
+ opts := []openai.Opt {}
183
+ if openaiModel != "" {
184
+ opts = append (opts , openai .OptModel (openaiModel ))
185
+ }
186
+ if openaiFrequencyPenalty != nil {
187
+ opts = append (opts , openai .OptFrequencyPenalty (float32 (* openaiFrequencyPenalty )))
188
+ }
189
+ if openaiPresencePenalty != nil {
190
+ opts = append (opts , openai .OptPresencePenalty (float32 (* openaiPresencePenalty )))
191
+ }
192
+ if openaiTemperature != nil {
193
+ opts = append (opts , openai .OptTemperature (float32 (* openaiTemperature )))
194
+ }
195
+ if openaiMaxTokens != 0 {
196
+ opts = append (opts , openai .OptMaxTokens (int (openaiMaxTokens )))
197
+ }
198
+ if openaiCount != nil && * openaiCount > 1 {
199
+ opts = append (opts , openai .OptCount (int (* openaiCount )))
200
+ }
201
+ if openaiResponseFormat != "" {
202
+ // TODO: Should be an object, not a string
203
+ opts = append (opts , openai .OptResponseFormat (openaiResponseFormat ))
204
+ }
205
+ if openaiStream {
206
+ opts = append (opts , openai .OptStream ())
207
+ }
208
+ if openaiUser != "" {
209
+ opts = append (opts , openai .OptUser (openaiUser ))
210
+ }
211
+ if openaiSystemPrompt != "" {
212
+ messages = append (messages , schema .NewMessage ("system" ).Add (schema .Text (openaiSystemPrompt )))
213
+ }
214
+
215
+ // Append user message
216
+ message := schema .NewMessage ("user" )
217
+ for _ , arg := range args {
218
+ message .Add (schema .Text (arg ))
219
+ }
220
+ messages = append (messages , message )
221
+
222
+ // Request->Response
223
+ responses , err := openaiClient .Chat (ctx , messages , opts ... )
224
+ if err != nil {
225
+ return err
226
+ }
227
+
228
+ return w .Write (responses )
229
+ }
0 commit comments