Skip to content

Commit 76db187

Browse files
committed
feat: add support for OAuth PATs
Instead of configuring and using OAuth, an app can specify that it supports using personal access tokens. If this is the case, then Obot will pass an extra environment variable to the oauth credential tool to indicate which integrations support tokens. If the oauth2 credential tool should prompt the user for a token instead of using OAuth, then Obot will not pass the environment variables that feed the URLs to the tool. A side effect of this change is that OAuth apps no longer default to global. Signed-off-by: Donnie Adams <[email protected]>
1 parent bd46fac commit 76db187

File tree

12 files changed

+83
-39
lines changed

12 files changed

+83
-39
lines changed

apiclient/types/oauthapp.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ type OAuthAppManifest struct {
3737
// This field is required, it correlates to the integration name in the gptscript oauth cred tool
3838
Integration string `json:"integration,omitempty"`
3939
// Global indicates if the OAuth app is globally applied to all agents.
40-
Global *bool `json:"global,omitempty"`
40+
Global bool `json:"global,omitempty"`
4141
// This field is only used by Salesforce
4242
InstanceURL string `json:"instanceURL,omitempty"`
4343
}

apiclient/types/zz_generated.deepcopy.go

Lines changed: 0 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/api/handlers/agent.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -918,7 +918,7 @@ func runAuthForAgent(ctx context.Context, c kclient.WithWatch, invoker *invoke.I
918918

919919
var toolRef v1.ToolReference
920920
for _, tool := range tools {
921-
if strings.ContainsAny(tool, "./") {
921+
if render.IsExternalTool(tool) {
922922
prg, err := gClient.LoadFile(ctx, tool)
923923
if err != nil {
924924
return nil, err
@@ -965,7 +965,7 @@ func removeToolCredentials(ctx context.Context, client kclient.Client, gClient *
965965
credentialNames []string
966966
)
967967
for _, tool := range tools {
968-
if strings.ContainsAny(tool, "./") {
968+
if render.IsExternalTool(tool) {
969969
prg, err := gClient.LoadFile(ctx, tool)
970970
if err != nil {
971971
errs = append(errs, err)

pkg/controller/handlers/oauthapp/oauthapplogin.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ func (h *LoginHandler) RunTool(req router.Request, _ router.Response) error {
5353
return err
5454
}
5555

56-
oauthAppEnv, err := render.OAuthAppEnv(req.Ctx, req.Client, login.Spec.OAuthApps, login.Namespace, h.serverURL)
56+
oauthAppEnv, err := render.OAuthAppEnv(req.Ctx, req.Client, login.Spec.OAuthApps, login.Namespace, h.serverURL, login.Spec.PATSupportedIntegrations)
5757
if err != nil {
5858
return err
5959
}

pkg/controller/handlers/toolinfo/toolinfo.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ package toolinfo
33
import (
44
"context"
55
"fmt"
6-
"strings"
76

87
"github.com/gptscript-ai/go-gptscript"
98
"github.com/obot-platform/nah/pkg/router"
109
"github.com/obot-platform/obot/apiclient/types"
1110
"github.com/obot-platform/obot/pkg/controller/creds"
11+
"github.com/obot-platform/obot/pkg/render"
1212
v1 "github.com/obot-platform/obot/pkg/storage/apis/obot.obot.ai/v1"
1313
apierror "k8s.io/apimachinery/pkg/api/errors"
1414
"k8s.io/apimachinery/pkg/util/sets"
@@ -57,7 +57,7 @@ func (h *Handler) SetToolInfoStatus(req router.Request, resp router.Response) (e
5757
credNames []string
5858
)
5959
for _, tool := range tools {
60-
if strings.ContainsAny(tool, "/.") {
60+
if render.IsExternalTool(tool) {
6161
credNames, err = h.credentialNamesForNonToolReferences(req.Ctx, tool)
6262
if err != nil {
6363
return err

pkg/gateway/types/oauth_apps.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ func MergeOAuthAppManifests(r, other types.OAuthAppManifest) types.OAuthAppManif
166166
if other.OptionalScope != "" {
167167
retVal.OptionalScope = other.OptionalScope
168168
}
169-
if other.Global != nil {
169+
if other.Global {
170170
retVal.Global = other.Global
171171
}
172172

pkg/render/render.go

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,11 @@ func Agent(ctx context.Context, db kclient.Client, agent *v1.Agent, oauthServerU
7474
}
7575

7676
if opts.Thread != nil {
77-
for _, tool := range opts.Thread.Spec.Manifest.Tools {
78-
if !added && tool == knowledgeToolName {
77+
for _, t := range opts.Thread.Spec.Manifest.Tools {
78+
if !added && t == knowledgeToolName {
7979
continue
8080
}
81-
name, tools, err := Tool(ctx, db, agent.Namespace, tool)
81+
name, _, tools, err := tool(ctx, db, agent.Namespace, t)
8282
if err != nil {
8383
return nil, nil, err
8484
}
@@ -99,18 +99,33 @@ func Agent(ctx context.Context, db kclient.Client, agent *v1.Agent, oauthServerU
9999
}
100100
}
101101

102-
for _, tool := range agent.Spec.Manifest.Tools {
103-
if !added && tool == knowledgeToolName {
102+
var patSupportedIntegrations []string
103+
for _, t := range agent.Spec.Manifest.Tools {
104+
if !added && t == knowledgeToolName {
104105
continue
105106
}
106-
name, tools, err := Tool(ctx, db, agent.Namespace, tool)
107+
name, metadata, tools, err := tool(ctx, db, agent.Namespace, t)
107108
if err != nil {
108109
return nil, nil, err
109110
}
110111
if name != "" {
111112
mainTool.Tools = append(mainTool.Tools, name)
112113
}
113-
otherTools = append(otherTools, tools...)
114+
115+
if metadata["oauthPATSupported"] == "true" {
116+
if integration := metadata["oauth"]; integration != "" {
117+
patSupportedIntegrations = append(patSupportedIntegrations, integration)
118+
}
119+
}
120+
121+
for _, t := range tools {
122+
if t.MetaData["oauthPATSupported"] == "true" {
123+
if integration := t.MetaData["oauth"]; integration != "" {
124+
patSupportedIntegrations = append(patSupportedIntegrations, integration)
125+
}
126+
}
127+
otherTools = append(otherTools, t)
128+
}
114129
}
115130

116131
for _, tool := range agent.Spec.SystemTools {
@@ -134,7 +149,7 @@ func Agent(ctx context.Context, db kclient.Client, agent *v1.Agent, oauthServerU
134149
return nil, nil, err
135150
}
136151

137-
oauthEnv, err := OAuthAppEnv(ctx, db, agent.Spec.Manifest.OAuthApps, agent.Namespace, oauthServerURL)
152+
oauthEnv, err := OAuthAppEnv(ctx, db, agent.Spec.Manifest.OAuthApps, agent.Namespace, oauthServerURL, patSupportedIntegrations)
138153
if err != nil {
139154
return nil, nil, err
140155
}
@@ -144,7 +159,7 @@ func Agent(ctx context.Context, db kclient.Client, agent *v1.Agent, oauthServerU
144159
return append([]gptscript.ToolDef{mainTool}, otherTools...), extraEnv, nil
145160
}
146161

147-
func OAuthAppEnv(ctx context.Context, db kclient.Client, oauthAppNames []string, namespace, serverURL string) (extraEnv []string, _ error) {
162+
func OAuthAppEnv(ctx context.Context, db kclient.Client, oauthAppNames []string, namespace, serverURL string, patIntegrations []string) (extraEnv []string, _ error) {
148163
apps, err := oauthAppsByName(ctx, db, namespace)
149164
if err != nil {
150165
return nil, err
@@ -153,7 +168,7 @@ func OAuthAppEnv(ctx context.Context, db kclient.Client, oauthAppNames []string,
153168
activeIntegrations := map[string]v1.OAuthApp{}
154169
for _, name := range slices.Sorted(maps.Keys(apps)) {
155170
app := apps[name]
156-
if app.Spec.Manifest.Global == nil || !*app.Spec.Manifest.Global || app.Spec.Manifest.ClientID == "" || app.Spec.Manifest.ClientSecret == "" || app.Spec.Manifest.Integration == "" {
171+
if !app.Spec.Manifest.Global || app.Spec.Manifest.ClientID == "" || app.Spec.Manifest.ClientSecret == "" || app.Spec.Manifest.Integration == "" {
157172
continue
158173
}
159174
activeIntegrations[app.Spec.Manifest.Integration] = app
@@ -184,6 +199,10 @@ func OAuthAppEnv(ctx context.Context, db kclient.Client, oauthAppNames []string,
184199
fmt.Sprintf("GPTSCRIPT_OAUTH_%s_TOKEN_URL=%s", integrationEnv, v1.OAuthAppGetTokenURL(serverURL)))
185200
}
186201

202+
if len(patIntegrations) > 0 {
203+
extraEnv = append(extraEnv, fmt.Sprintf("GPTSCRIPT_OAUTH_PAT_INTEGRATIONS=%s", strings.Join(patIntegrations, ",")))
204+
}
205+
187206
return extraEnv, nil
188207
}
189208

pkg/render/tool.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,25 +93,25 @@ END INSTRUCTIONS: TOOL %q`, tool.Spec.Manifest.Name, tool.Spec.Manifest.Context,
9393
return toolDefs, nil
9494
}
9595

96-
func Tool(ctx context.Context, c client.Client, ns, name string) (_ string, toolDefs []gptscript.ToolDef, _ error) {
96+
func tool(ctx context.Context, c client.Client, ns, name string) (string, map[string]string, []gptscript.ToolDef, error) {
9797
if !system.IsToolID(name) {
98-
name, err := ResolveToolReference(ctx, c, types.ToolReferenceTypeTool, ns, name)
99-
return name, nil, err
98+
name, metadata, err := resolveToolReferenceWithMetadata(ctx, c, types.ToolReferenceTypeTool, ns, name)
99+
return name, metadata, nil, err
100100
}
101101

102102
var tool v1.Tool
103103
if err := c.Get(ctx, router.Key(ns, name), &tool); err != nil {
104-
return name, nil, err
104+
return name, nil, nil, err
105105
}
106106

107107
toolDefs, err := CustomTool(ctx, c, tool)
108108
if err != nil {
109-
return "", nil, err
109+
return "", nil, nil, err
110110
}
111111

112112
if len(toolDefs) == 0 {
113-
return "", toolDefs, nil
113+
return "", nil, toolDefs, nil
114114
}
115115

116-
return toolDefs[0].Name, toolDefs, nil
116+
return toolDefs[0].Name, nil, toolDefs, nil
117117
}

pkg/render/workflow.go

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,26 +29,36 @@ func IsExternalTool(tool string) bool {
2929
}
3030

3131
func ResolveToolReference(ctx context.Context, c kclient.Client, toolRefType types.ToolReferenceType, ns, name string) (string, error) {
32+
name, _, err := resolveToolReferenceWithMetadata(ctx, c, toolRefType, ns, name)
33+
return name, err
34+
}
35+
36+
func resolveToolReferenceWithMetadata(ctx context.Context, c kclient.Client, toolRefType types.ToolReferenceType, ns, name string) (string, map[string]string, error) {
3237
if IsExternalTool(name) {
33-
return name, nil
38+
return name, nil, nil
3439
}
3540

3641
var tool v1.ToolReference
3742
if err := c.Get(ctx, router.Key(ns, name), &tool); apierror.IsNotFound(err) {
38-
return name, nil
43+
return name, nil, nil
3944
} else if err != nil {
40-
return "", err
45+
return "", nil, err
46+
}
47+
48+
var metadata map[string]string
49+
if tool.Status.Tool != nil {
50+
metadata = tool.Status.Tool.Metadata
4151
}
4252
if toolRefType != "" && tool.Spec.Type != toolRefType {
43-
return name, fmt.Errorf("tool reference %s is not of type %s", name, toolRefType)
53+
return name, metadata, fmt.Errorf("tool reference %s is not of type %s", name, toolRefType)
4454
}
4555
if tool.Status.Reference == "" {
46-
return "", fmt.Errorf("tool reference %s has no reference", name)
56+
return "", nil, fmt.Errorf("tool reference %s has no reference", name)
4757
}
4858
if toolRefType == types.ToolReferenceTypeTool {
49-
return fmt.Sprintf("%s as %s", tool.Status.Reference, name), nil
59+
return fmt.Sprintf("%s as %s", tool.Status.Reference, name), metadata, nil
5060
}
51-
return tool.Status.Reference, nil
61+
return tool.Status.Reference, metadata, nil
5262
}
5363

5464
func Workflow(ctx context.Context, c kclient.Client, wf *v1.Workflow, opts WorkflowOptions) (*v1.Agent, error) {

pkg/storage/apis/obot.obot.ai/v1/oauthapp.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,10 @@ func (o *OAuthAppLogin) DeleteRefs() []Ref {
129129
}
130130

131131
type OAuthAppLoginSpec struct {
132-
CredentialContext string `json:"credentialContext,omitempty"`
133-
ToolReference string `json:"toolReference,omitempty"`
134-
OAuthApps []string `json:"oauthApps,omitempty"`
132+
CredentialContext string `json:"credentialContext,omitempty"`
133+
ToolReference string `json:"toolReference,omitempty"`
134+
OAuthApps []string `json:"oauthApps,omitempty"`
135+
PATSupportedIntegrations []string `json:"patSupportedIntegrations,omitempty"`
135136
}
136137

137138
type OAuthAppLoginStatus struct {

pkg/storage/apis/obot.obot.ai/v1/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/storage/openapi/generated/openapi_generated.go

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)