Skip to content

Commit

Permalink
chore: refactor model/auth provider metadata (obot-platform#1538)
Browse files Browse the repository at this point in the history
model- and auth-providers now define their metadata in their `tool.gpt` file as a JSON blob in a metadata section with `!metadata:<tool name>:providerMeta`
  • Loading branch information
iwilltry42 authored Feb 4, 2025
1 parent a31161e commit 7dbf52d
Show file tree
Hide file tree
Showing 20 changed files with 430 additions and 497 deletions.
10 changes: 5 additions & 5 deletions apiclient/types/authprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ type AuthProviderManifest struct {
}

type AuthProviderStatus struct {
Icon string `json:"icon,omitempty"`
Configured bool `json:"configured"`
RequiredConfigurationParameters []string `json:"requiredConfigurationParameters,omitempty"`
MissingConfigurationParameters []string `json:"missingConfigurationParameters,omitempty"`
OptionalConfigurationParameters []string `json:"optionalConfigurationParameters,omitempty"`
CommonProviderMetadata
Configured bool `json:"configured"`
RequiredConfigurationParameters []ProviderConfigurationParameter `json:"requiredConfigurationParameters,omitempty"`
OptionalConfigurationParameters []ProviderConfigurationParameter `json:"optionalConfigurationParameters,omitempty"`
MissingConfigurationParameters []string `json:"missingConfigurationParameters,omitempty"`
}

type AuthProviderList List[AuthProvider]
26 changes: 20 additions & 6 deletions apiclient/types/modelprovider.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
package types

type CommonProviderMetadata struct {
Icon string `json:"icon,omitempty"`
IconDark string `json:"iconDark,omitempty"`
Description string `json:"description,omitempty"`
Link string `json:"link,omitempty"`
}

type ProviderConfigurationParameter struct {
Name string `json:"name"`
FriendlyName string `json:"friendlyName,omitempty"`
Description string `json:"description,omitempty"`
Sensitive bool `json:"sensitive,omitempty"`
}

type ModelProvider struct {
Metadata
ModelProviderManifest
Expand All @@ -12,12 +26,12 @@ type ModelProviderManifest struct {
}

type ModelProviderStatus struct {
Icon string `json:"icon,omitempty"`
Configured bool `json:"configured"`
ModelsBackPopulated *bool `json:"modelsBackPopulated,omitempty"`
RequiredConfigurationParameters []string `json:"requiredConfigurationParameters,omitempty"`
MissingConfigurationParameters []string `json:"missingConfigurationParameters,omitempty"`
OptionalConfigurationParameters []string `json:"optionalConfigurationParameters,omitempty"`
CommonProviderMetadata
Configured bool `json:"configured"`
ModelsBackPopulated *bool `json:"modelsBackPopulated,omitempty"`
RequiredConfigurationParameters []ProviderConfigurationParameter `json:"requiredConfigurationParameters,omitempty"`
OptionalConfigurationParameters []ProviderConfigurationParameter `json:"optionalConfigurationParameters,omitempty"`
MissingConfigurationParameters []string `json:"missingConfigurationParameters,omitempty"`
}

type ModelProviderList List[ModelProvider]
56 changes: 44 additions & 12 deletions apiclient/types/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

75 changes: 26 additions & 49 deletions pkg/api/handlers/authprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,18 @@ import (
"encoding/base64"
"errors"
"fmt"
"slices"
"strings"

"github.com/gptscript-ai/go-gptscript"
"github.com/obot-platform/obot/apiclient/types"
"github.com/obot-platform/obot/pkg/api"
"github.com/obot-platform/obot/pkg/api/handlers/providers"
"github.com/obot-platform/obot/pkg/gateway/server/dispatcher"
v1 "github.com/obot-platform/obot/pkg/storage/apis/obot.obot.ai/v1"
"github.com/obot-platform/obot/pkg/system"
"k8s.io/apimachinery/pkg/fields"
kclient "sigs.k8s.io/controller-runtime/pkg/client"
)

const cookieSecretEnvVar = "OBOT_AUTH_PROVIDER_COOKIE_SECRET"

type AuthProviderHandler struct {
gptscript *gptscript.GPTScript
dispatcher *dispatcher.Dispatcher
Expand Down Expand Up @@ -48,7 +45,11 @@ func (ap *AuthProviderHandler) ByID(req api.Context) error {

var credEnvVars map[string]string
if ref.Status.Tool != nil {
if envVars := ref.Status.Tool.Metadata["envVars"]; envVars != "" {
aps, err := providers.ConvertModelProviderToolRef(ref, nil)
if err != nil {
return err
}
if len(aps.RequiredConfigurationParameters) > 0 {
cred, err := ap.gptscript.RevealCredential(req.Context(), []string{string(ref.UID), system.GenericAuthProviderCredentialContext}, ref.Name)
if err != nil && !errors.As(err, &gptscript.ErrNotFound{}) {
return fmt.Errorf("failed to reveal credential for auth provider %q: %w", ref.Name, err)
Expand All @@ -58,7 +59,12 @@ func (ap *AuthProviderHandler) ByID(req api.Context) error {
}
}

return req.Write(convertToolReferenceToAuthProvider(ref, credEnvVars))
authProvider, err := convertToolReferenceToAuthProvider(ref, credEnvVars)
if err != nil {
return err
}

return req.Write(authProvider)
}

func (ap *AuthProviderHandler) List(req api.Context) error {
Expand Down Expand Up @@ -105,7 +111,12 @@ func (ap *AuthProviderHandler) listAuthProviders(req api.Context) ([]types.AuthP
if !ok {
env = credMap[system.GenericAuthProviderCredentialContext+ref.Name]
}
resp = append(resp, convertToolReferenceToAuthProvider(ref, env))
authProvider, err := convertToolReferenceToAuthProvider(ref, env)
if err != nil {
log.Warnf("failed to convert auth provider %q: %v", ref.Name, err)
continue
}
resp = append(resp, authProvider)
}
return resp, nil
}
Expand Down Expand Up @@ -142,7 +153,7 @@ func (ap *AuthProviderHandler) Configure(req api.Context) error {
if err != nil {
return err
}
envVars[cookieSecretEnvVar] = cookieSecret
envVars[providers.CookieSecretEnvVar] = cookieSecret

// Allow for updating credentials. The only way to update a credential is to delete the existing one and recreate it.
cred, err := ap.gptscript.RevealCredential(req.Context(), []string{string(ref.UID), system.GenericAuthProviderCredentialContext}, ref.Name)
Expand Down Expand Up @@ -245,58 +256,24 @@ func authProviderNameFromToolRef(ref v1.ToolReference) string {
return name
}

func convertToolReferenceToAuthProvider(ref v1.ToolReference, credEnvVars map[string]string) types.AuthProvider {
func convertToolReferenceToAuthProvider(ref v1.ToolReference, credEnvVars map[string]string) (types.AuthProvider, error) {
aps, err := providers.ConvertAuthProviderToolRef(ref, credEnvVars)
if err != nil {
return types.AuthProvider{}, err
}
ap := types.AuthProvider{
Metadata: MetadataFrom(&ref),
AuthProviderManifest: types.AuthProviderManifest{
Name: authProviderNameFromToolRef(ref),
Namespace: ref.Namespace,
ToolReference: ref.Spec.Reference,
},
AuthProviderStatus: *convertAuthProviderToolRef(ref, credEnvVars),
AuthProviderStatus: *aps,
}

ap.Type = "authprovider"

return ap
}

func convertAuthProviderToolRef(toolRef v1.ToolReference, cred map[string]string) *types.AuthProviderStatus {
var (
requiredEnvVars, missingEnvVars, optionalEnvVars []string
icon string
)
if toolRef.Status.Tool != nil {
if toolRef.Status.Tool.Metadata["envVars"] != "" {
requiredEnvVars = strings.Split(toolRef.Status.Tool.Metadata["envVars"], ",")

// Remove the cookie secret environment variable if it's there.
idx := slices.Index(requiredEnvVars, cookieSecretEnvVar)
if idx != -1 {
requiredEnvVars = append(requiredEnvVars[:idx], requiredEnvVars[idx+1:]...)
}
}

for _, envVar := range requiredEnvVars {
if _, ok := cred[envVar]; !ok {
missingEnvVars = append(missingEnvVars, envVar)
}
}

icon = toolRef.Status.Tool.Metadata["icon"]

if optionalEnvVarMetadata := toolRef.Status.Tool.Metadata["optionalEnvVars"]; optionalEnvVarMetadata != "" {
optionalEnvVars = strings.Split(optionalEnvVarMetadata, ",")
}
}

return &types.AuthProviderStatus{
Icon: icon,
Configured: toolRef.Status.Tool != nil && len(missingEnvVars) == 0,
RequiredConfigurationParameters: requiredEnvVars,
MissingConfigurationParameters: missingEnvVars,
OptionalConfigurationParameters: optionalEnvVars,
}
return ap, nil
}

func generateCookieSecret() (string, error) {
Expand Down
21 changes: 18 additions & 3 deletions pkg/api/handlers/availablemodels.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"fmt"
"strings"

"github.com/obot-platform/obot/pkg/api/handlers/providers"

openai "github.com/gptscript-ai/chat-completion-client"
"github.com/gptscript-ai/go-gptscript"
"github.com/obot-platform/obot/apiclient/types"
Expand Down Expand Up @@ -59,7 +61,11 @@ func (a *AvailableModelsHandler) List(req api.Context) error {

var oModels openai.ModelsList
for _, modelProvider := range modelProviderReferences.Items {
convertedModelProvider := convertModelProviderToolRef(modelProvider, credMap[string(modelProvider.UID)+modelProvider.Name])
convertedModelProvider, err := providers.ConvertModelProviderToolRef(modelProvider, credMap[string(modelProvider.UID)+modelProvider.Name])
if err != nil {
log.Warnf("failed to convert model provider %q: %v", modelProvider.Name, err)
continue
}
if !convertedModelProvider.Configured || modelProvider.Name == system.ModelProviderTool {
continue
}
Expand Down Expand Up @@ -91,9 +97,14 @@ func (a *AvailableModelsHandler) ListForModelProvider(req api.Context) error {
return types.NewErrBadRequest("%s is not a model provider", modelProviderReference.Name)
}

modelProvider, err := providers.ConvertModelProviderToolRef(modelProviderReference, nil)
if err != nil {
return err
}

var credEnvVars map[string]string
if modelProviderReference.Status.Tool != nil {
if envVars := modelProviderReference.Status.Tool.Metadata["envVars"]; envVars != "" {
if len(modelProvider.RequiredConfigurationParameters) > 0 {
cred, err := a.gptscript.RevealCredential(req.Context(), []string{string(modelProviderReference.UID), system.GenericModelProviderCredentialContext}, modelProviderReference.Name)
if err != nil && !errors.As(err, &gptscript.ErrNotFound{}) {
return fmt.Errorf("failed to reveal credential for model provider %q: %w", modelProviderReference.Name, err)
Expand All @@ -103,7 +114,11 @@ func (a *AvailableModelsHandler) ListForModelProvider(req api.Context) error {
}
}

if modelProvider := convertModelProviderToolRef(modelProviderReference, credEnvVars); !modelProvider.Configured {
modelProvider, err = providers.ConvertModelProviderToolRef(modelProviderReference, credEnvVars)
if err != nil {
return err
}
if !modelProvider.Configured {
return types.NewErrBadRequest("model provider %s is not configured, missing configuration parameters: %s", modelProviderReference.Name, strings.Join(modelProvider.MissingConfigurationParameters, ", "))
}

Expand Down
Loading

0 comments on commit 7dbf52d

Please sign in to comment.