@@ -12,14 +12,19 @@ import (
1212
1313 openai "github.com/gptscript-ai/chat-completion-client"
1414 "github.com/gptscript-ai/gptscript/pkg/cache"
15+ "github.com/gptscript-ai/gptscript/pkg/config"
1516 "github.com/gptscript-ai/gptscript/pkg/counter"
17+ "github.com/gptscript-ai/gptscript/pkg/credentials"
1618 "github.com/gptscript-ai/gptscript/pkg/hash"
19+ "github.com/gptscript-ai/gptscript/pkg/prompt"
1720 "github.com/gptscript-ai/gptscript/pkg/system"
1821 "github.com/gptscript-ai/gptscript/pkg/types"
22+ "github.com/tidwall/gjson"
1923)
2024
2125const (
22- DefaultModel = openai .GPT4o
26+ DefaultModel = openai .GPT4o
27+ BuiltinCredName = "sys.openai"
2328)
2429
2530var (
@@ -28,13 +33,21 @@ var (
2833 azureModel = os .Getenv ("OPENAI_AZURE_DEPLOYMENT" )
2934)
3035
36+ type InvalidAuthError struct {}
37+
38+ func (InvalidAuthError ) Error () string {
39+ return "OPENAI_API_KEY is not set. Please set the OPENAI_API_KEY environment variable"
40+ }
41+
3142type Client struct {
3243 defaultModel string
3344 c * openai.Client
3445 cache * cache.Client
3546 invalidAuth bool
3647 cacheKeyBase string
3748 setSeed bool
49+ cliCfg * config.CLIConfig
50+ credCtx string
3851}
3952
4053type Options struct {
@@ -93,12 +106,28 @@ func GetAzureMapperFunction(defaultModel, azureModel string) func(string) string
93106 }
94107}
95108
96- func NewClient (opts ... Options ) (* Client , error ) {
109+ func NewClient (cliCfg * config. CLIConfig , credCtx string , opts ... Options ) (* Client , error ) {
97110 opt , err := complete (opts ... )
98111 if err != nil {
99112 return nil , err
100113 }
101114
115+ // If the API key is not set, try to get it from the cred store
116+ if opt .APIKey == "" && opt .BaseURL == "" {
117+ store , err := credentials .NewStore (cliCfg , credCtx )
118+ if err != nil {
119+ return nil , err
120+ }
121+
122+ cred , exists , err := store .Get (BuiltinCredName )
123+ if err != nil {
124+ return nil , err
125+ }
126+ if exists {
127+ opt .APIKey = cred .Env ["OPENAI_API_KEY" ]
128+ }
129+ }
130+
102131 cfg := openai .DefaultConfig (opt .APIKey )
103132 if strings .Contains (string (opt .APIType ), "AZURE" ) {
104133 cfg = openai .DefaultAzureConfig (key , url )
@@ -122,12 +151,14 @@ func NewClient(opts ...Options) (*Client, error) {
122151 cacheKeyBase : cacheKeyBase ,
123152 invalidAuth : opt .APIKey == "" && opt .BaseURL == "" ,
124153 setSeed : opt .SetSeed ,
154+ cliCfg : cliCfg ,
155+ credCtx : credCtx ,
125156 }, nil
126157}
127158
128159func (c * Client ) ValidAuth () error {
129160 if c .invalidAuth {
130- return fmt . Errorf ( "OPENAI_API_KEY is not set. Please set the OPENAI_API_KEY environment variable" )
161+ return InvalidAuthError {}
131162 }
132163 return nil
133164}
@@ -276,7 +307,9 @@ func toMessages(request types.CompletionRequest, compat bool) (result []openai.C
276307
277308func (c * Client ) Call (ctx context.Context , messageRequest types.CompletionRequest , status chan <- types.CompletionStatus ) (* types.CompletionMessage , error ) {
278309 if err := c .ValidAuth (); err != nil {
279- return nil , err
310+ if err := c .RetrieveAPIKey (); err != nil {
311+ return nil , err
312+ }
280313 }
281314
282315 if messageRequest .Model == "" {
@@ -525,6 +558,44 @@ func (c *Client) call(ctx context.Context, request openai.ChatCompletionRequest,
525558 }
526559}
527560
561+ func (c * Client ) RetrieveAPIKey () error {
562+ store , err := credentials .NewStore (c .cliCfg , c .credCtx )
563+ if err != nil {
564+ return err
565+ }
566+
567+ cred , exists , err := store .Get (BuiltinCredName )
568+ if err != nil {
569+ return err
570+ }
571+
572+ var k string
573+ if exists {
574+ k = cred .Env [key ]
575+ } else {
576+ // SysPrompt doesn't use its first two arguments, so we can safely pass nil to them
577+ result , err := prompt .SysPrompt (nil , nil , `{"message":"Please provide your OpenAI API key:","fields":"key","sensitive":"true"}` )
578+ if err != nil {
579+ return err
580+ }
581+
582+ k = gjson .Get (result , "key" ).String ()
583+ if err := store .Add (credentials.Credential {
584+ ToolName : BuiltinCredName ,
585+ Env : map [string ]string {
586+ "OPENAI_API_KEY" : k ,
587+ },
588+ }); err != nil {
589+ return err
590+ }
591+ log .Infof ("Saved API key as credential %s" , BuiltinCredName )
592+ }
593+
594+ c .c .SetAPIKey (k )
595+ c .invalidAuth = false
596+ return nil
597+ }
598+
528599func ptr [T any ](v T ) * T {
529600 return & v
530601}
0 commit comments