From d05210b66862fbc2e1c1e8aacca3494d7ce99d9a Mon Sep 17 00:00:00 2001 From: Donnie Adams Date: Fri, 22 Nov 2024 11:29:01 -0500 Subject: [PATCH] feat: add default model aliases Default model aliases replace the concept of a model being a default. Now, a user can set up their default model aliases and tools can use those to interact with the default models without needing to worry about which model they are actually using. This change extends to knowledge so that knowledge will always use the same text-embedding model for a give knowledge set. Another minor change here: renaming agent usage to llm. Signed-off-by: Donnie Adams --- apiclient/types/agent.go | 5 +- apiclient/types/defaultmodelalias.go | 21 + apiclient/types/model.go | 3 +- apiclient/types/workflow.go | 5 +- apiclient/types/zz_generated.deepcopy.go | 161 +++++++ pkg/alias/get.go | 38 ++ pkg/api/handlers/agent.go | 79 ++- pkg/api/handlers/defaultmodelalias.go | 93 ++++ pkg/api/handlers/model.go | 22 +- pkg/api/handlers/workflows.go | 81 +++- pkg/api/router/router.go | 8 + pkg/controller/data/data.go | 19 + .../data/default-model-aliases.yaml | 37 ++ pkg/controller/data/default-models.yaml | 7 +- pkg/controller/handlers/alias/alias.go | 1 - .../handlers/knowledgefile/knowledgefile.go | 7 + .../handlers/knowledgeset/knowledgeset.go | 62 ++- pkg/controller/handlers/threads/threads.go | 12 +- pkg/controller/routes.go | 5 + pkg/gateway/server/dispatcher/dispatcher.go | 17 +- pkg/invoke/invoker.go | 8 +- .../otto.otto8.ai/v1/defaultmodelalias.go | 46 ++ .../apis/otto.otto8.ai/v1/knowledgeset.go | 4 + pkg/storage/apis/otto.otto8.ai/v1/model.go | 26 - pkg/storage/apis/otto.otto8.ai/v1/run.go | 11 +- pkg/storage/apis/otto.otto8.ai/v1/scheme.go | 2 + pkg/storage/apis/otto.otto8.ai/v1/thread.go | 1 + .../otto.otto8.ai/v1/zz_generated.deepcopy.go | 90 ++++ .../openapi/generated/openapi_generated.go | 456 +++++++++++++++++- pkg/system/ids.go | 1 + ui/admin/app/components/agent/AgentForm.tsx | 5 +- ui/admin/app/components/model/ModelForm.tsx | 2 +- ui/admin/app/lib/model/models.ts | 4 +- 33 files changed, 1220 insertions(+), 119 deletions(-) create mode 100644 apiclient/types/defaultmodelalias.go create mode 100644 pkg/api/handlers/defaultmodelalias.go create mode 100644 pkg/controller/data/default-model-aliases.yaml create mode 100644 pkg/storage/apis/otto.otto8.ai/v1/defaultmodelalias.go diff --git a/apiclient/types/agent.go b/apiclient/types/agent.go index 7e3bca47b..bc7f14a10 100644 --- a/apiclient/types/agent.go +++ b/apiclient/types/agent.go @@ -11,8 +11,9 @@ import ( type Agent struct { Metadata AgentManifest - AliasAssigned bool `json:"aliasAssigned,omitempty"` - AuthStatus map[string]OAuthAppLoginAuthStatus `json:"authStatus,omitempty"` + AliasAssigned bool `json:"aliasAssigned,omitempty"` + AuthStatus map[string]OAuthAppLoginAuthStatus `json:"authStatus,omitempty"` + TextEmbeddingModel string `json:"textEmbeddingModel,omitempty"` } type AgentList List[Agent] diff --git a/apiclient/types/defaultmodelalias.go b/apiclient/types/defaultmodelalias.go new file mode 100644 index 000000000..ae509e205 --- /dev/null +++ b/apiclient/types/defaultmodelalias.go @@ -0,0 +1,21 @@ +package types + +type DefaultModelAliasType string + +const ( + DefaultModelAliasTypeTextEmbedding DefaultModelAliasType = "text-embedding" + DefaultModelAliasTypeLLM DefaultModelAliasType = "llm" + DefaultModelAliasTypeLLMMini DefaultModelAliasType = "llm-mini" + DefaultModelAliasTypeImageGeneration DefaultModelAliasType = "image-generation" +) + +type DefaultModelAlias struct { + DefaultModelAliasManifest +} + +type DefaultModelAliasManifest struct { + Alias string `json:"alias"` + Model string `json:"model"` +} + +type DefaultModelAliasList List[DefaultModelAlias] diff --git a/apiclient/types/model.go b/apiclient/types/model.go index 5b1d9530a..d2d6ba4cd 100644 --- a/apiclient/types/model.go +++ b/apiclient/types/model.go @@ -12,7 +12,6 @@ type ModelManifest struct { ModelProvider string `json:"modelProvider,omitempty"` Alias string `json:"alias,omitempty"` Active bool `json:"active"` - Default bool `json:"default"` Usage ModelUsage `json:"usage,omitempty"` } @@ -31,7 +30,7 @@ type ModelProviderStatus struct { type ModelUsage string const ( - ModelUsageAgent ModelUsage = "agent" + ModelUsageLLM ModelUsage = "llm" ModelUsageEmbedding ModelUsage = "text-embedding" ModelUsageImage ModelUsage = "image-generation" ) diff --git a/apiclient/types/workflow.go b/apiclient/types/workflow.go index 5a08acc76..64b104ddb 100644 --- a/apiclient/types/workflow.go +++ b/apiclient/types/workflow.go @@ -5,8 +5,9 @@ import "strings" type Workflow struct { Metadata WorkflowManifest - AliasAssigned bool `json:"aliasAssigned,omitempty"` - AuthStatus map[string]OAuthAppLoginAuthStatus `json:"authStatus,omitempty"` + AliasAssigned bool `json:"aliasAssigned,omitempty"` + AuthStatus map[string]OAuthAppLoginAuthStatus `json:"authStatus,omitempty"` + TextEmbeddingModel string `json:"textEmbeddingModel,omitempty"` } type WorkflowList List[Workflow] diff --git a/apiclient/types/zz_generated.deepcopy.go b/apiclient/types/zz_generated.deepcopy.go index 4e1d33681..e6c496f37 100644 --- a/apiclient/types/zz_generated.deepcopy.go +++ b/apiclient/types/zz_generated.deepcopy.go @@ -322,6 +322,57 @@ func (in *CronJobManifest) DeepCopy() *CronJobManifest { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DefaultModelAlias) DeepCopyInto(out *DefaultModelAlias) { + *out = *in + out.DefaultModelAliasManifest = in.DefaultModelAliasManifest +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DefaultModelAlias. +func (in *DefaultModelAlias) DeepCopy() *DefaultModelAlias { + if in == nil { + return nil + } + out := new(DefaultModelAlias) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DefaultModelAliasList) DeepCopyInto(out *DefaultModelAliasList) { + *out = *in + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]DefaultModelAlias, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DefaultModelAliasList. +func (in *DefaultModelAliasList) DeepCopy() *DefaultModelAliasList { + if in == nil { + return nil + } + out := new(DefaultModelAliasList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DefaultModelAliasManifest) DeepCopyInto(out *DefaultModelAliasManifest) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DefaultModelAliasManifest. +func (in *DefaultModelAliasManifest) DeepCopy() *DefaultModelAliasManifest { + if in == nil { + return nil + } + out := new(DefaultModelAliasManifest) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EmailReceiver) DeepCopyInto(out *EmailReceiver) { *out = *in @@ -1126,6 +1177,116 @@ func (in *SubFlow) DeepCopy() *SubFlow { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Task) DeepCopyInto(out *Task) { + *out = *in + in.Metadata.DeepCopyInto(&out.Metadata) + in.TaskManifest.DeepCopyInto(&out.TaskManifest) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Task. +func (in *Task) DeepCopy() *Task { + if in == nil { + return nil + } + out := new(Task) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskIf) DeepCopyInto(out *TaskIf) { + *out = *in + if in.Steps != nil { + in, out := &in.Steps, &out.Steps + *out = make([]TaskStep, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Else != nil { + in, out := &in.Else, &out.Else + *out = make([]TaskStep, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskIf. +func (in *TaskIf) DeepCopy() *TaskIf { + if in == nil { + return nil + } + out := new(TaskIf) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskList) DeepCopyInto(out *TaskList) { + *out = *in + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Task, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskList. +func (in *TaskList) DeepCopy() *TaskList { + if in == nil { + return nil + } + out := new(TaskList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskManifest) DeepCopyInto(out *TaskManifest) { + *out = *in + if in.Steps != nil { + in, out := &in.Steps, &out.Steps + *out = make([]TaskStep, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskManifest. +func (in *TaskManifest) DeepCopy() *TaskManifest { + if in == nil { + return nil + } + out := new(TaskManifest) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskStep) DeepCopyInto(out *TaskStep) { + *out = *in + if in.If != nil { + in, out := &in.If, &out.If + *out = new(TaskIf) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskStep. +func (in *TaskStep) DeepCopy() *TaskStep { + if in == nil { + return nil + } + out := new(TaskStep) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Template) DeepCopyInto(out *Template) { *out = *in diff --git a/pkg/alias/get.go b/pkg/alias/get.go index a9404e1ff..8e0c56791 100644 --- a/pkg/alias/get.go +++ b/pkg/alias/get.go @@ -53,6 +53,44 @@ func Get(ctx context.Context, c kclient.Client, obj v1.Aliasable, namespace stri return c.Get(ctx, router.Key(alias.Spec.TargetNamespace, alias.Spec.TargetName), obj.(kclient.Object)) } +func GetFromScope(ctx context.Context, c kclient.Client, scope, namespace, name string) (kclient.Object, error) { + gvk := schema.GroupVersionKind{ + Group: v1.SchemeGroupVersion.Group, + Version: v1.Version, + Kind: scope, + } + + obj, err := c.Scheme().New(gvk) + if err != nil { + return nil, apierrors.NewNotFound(schema.GroupResource{ + Group: gvk.Group, + Resource: gvk.Kind, + }, name) + } + + cObj := obj.(kclient.Object) + + var alias v1.Alias + if err := c.Get(ctx, router.Key("", KeyFromScopeID(scope, name)), &alias); apierrors.IsNotFound(err) { + return cObj, c.Get(ctx, router.Key(namespace, name), cObj) + } else if err != nil { + return nil, err + } + + gvk.Kind = alias.Spec.TargetKind + obj, err = c.Scheme().New(gvk) + if err != nil { + return nil, apierrors.NewNotFound(schema.GroupResource{ + Group: gvk.Group, + Resource: gvk.Kind, + }, name) + } + + cObj = obj.(kclient.Object) + + return cObj, c.Get(ctx, router.Key(alias.Spec.TargetNamespace, alias.Spec.TargetName), cObj) +} + func KeyFromScopeID(scope, id string) string { return system.AliasPrefix + hash.String(name.SafeHashConcatName(id, scope))[:8] } diff --git a/pkg/api/handlers/agent.go b/pkg/api/handlers/agent.go index 29394654b..4309b0ab1 100644 --- a/pkg/api/handlers/agent.go +++ b/pkg/api/handlers/agent.go @@ -54,17 +54,17 @@ func (a *AgentHandler) Update(req api.Context) error { return err } - return req.Write(convertAgent(agent, server.GetURLPrefix(req))) + resp, err := convertAgent(agent, req) + if err != nil { + return err + } + return req.WriteCreated(resp) } func (a *AgentHandler) Delete(req api.Context) error { - var ( - id = req.PathValue("id") - ) - return req.Delete(&v1.Agent{ ObjectMeta: metav1.ObjectMeta{ - Name: id, + Name: req.PathValue("id"), Namespace: req.Namespace(), }, }) @@ -89,12 +89,16 @@ func (a *AgentHandler) Create(req api.Context) error { return err } - return req.WriteCreated(convertAgent(agent, server.GetURLPrefix(req))) + resp, err := convertAgent(agent, req) + if err != nil { + return err + } + return req.WriteCreated(resp) } -func convertAgent(agent v1.Agent, prefix string) *types.Agent { +func convertAgent(agent v1.Agent, req api.Context) (*types.Agent, error) { var links []string - if prefix != "" { + if prefix := server.GetURLPrefix(req); prefix != "" { alias := agent.Name if agent.Status.AliasAssigned && agent.Spec.Manifest.Alias != "" { alias = agent.Spec.Manifest.Alias @@ -102,12 +106,22 @@ func convertAgent(agent v1.Agent, prefix string) *types.Agent { links = []string{"invoke", prefix + "/invoke/" + alias} } - return &types.Agent{ - Metadata: MetadataFrom(&agent, links...), - AgentManifest: agent.Spec.Manifest, - AliasAssigned: agent.Status.AliasAssigned, - AuthStatus: agent.Status.AuthStatus, + var embeddingModel string + if len(agent.Status.KnowledgeSetNames) > 0 { + var ks v1.KnowledgeSet + if err := req.Get(&ks, agent.Status.KnowledgeSetNames[0]); err != nil { + return nil, fmt.Errorf("failed to get KnowledgeSet %q: %v", agent.Status.KnowledgeSetNames[0], err) + } + embeddingModel = ks.Status.TextEmbeddingModel } + + return &types.Agent{ + Metadata: MetadataFrom(&agent, links...), + AgentManifest: agent.Spec.Manifest, + AliasAssigned: agent.Status.AliasAssigned, + AuthStatus: agent.Status.AuthStatus, + TextEmbeddingModel: embeddingModel, + }, nil } func (a *AgentHandler) ByID(req api.Context) error { @@ -115,7 +129,12 @@ func (a *AgentHandler) ByID(req api.Context) error { if err := alias.Get(req.Context(), req.Storage, &agent, req.Namespace(), req.PathValue("id")); err != nil { return err } - return req.Write(convertAgent(agent, server.GetURLPrefix(req))) + + resp, err := convertAgent(agent, req) + if err != nil { + return err + } + return req.WriteCreated(resp) } func (a *AgentHandler) List(req api.Context) error { @@ -126,7 +145,11 @@ func (a *AgentHandler) List(req api.Context) error { var resp types.AgentList for _, agent := range agentList.Items { - resp.Items = append(resp.Items, *convertAgent(agent, server.GetURLPrefix(req))) + convertedAgent, err := convertAgent(agent, req) + if err != nil { + return err + } + resp.Items = append(resp.Items, *convertedAgent) } return req.Write(resp) @@ -503,12 +526,20 @@ func (a *AgentHandler) EnsureCredentialForKnowledgeSource(req api.Context) error // If auth is not required, then don't continue. if authStatus.Required != nil && !*authStatus.Required { - return req.Write(convertAgent(agent, server.GetURLPrefix(req))) + resp, err := convertAgent(agent, req) + if err != nil { + return err + } + return req.WriteCreated(resp) } // if auth is already authenticated, then don't continue. if authStatus.Authenticated { - return req.Write(convertAgent(agent, server.GetURLPrefix(req))) + resp, err := convertAgent(agent, req) + if err != nil { + return err + } + return req.WriteCreated(resp) } credentialTool, err := v1.CredentialTool(req.Context(), req.Storage, req.Namespace(), ref) @@ -524,7 +555,11 @@ func (a *AgentHandler) EnsureCredentialForKnowledgeSource(req api.Context) error authStatus.Required = &[]bool{false}[0] agent.Status.AuthStatus[ref] = authStatus - return req.Write(convertAgent(agent, server.GetURLPrefix(req))) + resp, err := convertAgent(agent, req) + if err != nil { + return err + } + return req.WriteCreated(resp) } oauthLogin := &v1.OAuthAppLogin{ @@ -558,7 +593,11 @@ func (a *AgentHandler) EnsureCredentialForKnowledgeSource(req api.Context) error } agent.Status.AuthStatus[ref] = oauthLogin.Status.External - return req.Write(convertAgent(agent, server.GetURLPrefix(req))) + resp, err := convertAgent(agent, req) + if err != nil { + return err + } + return req.WriteCreated(resp) } func (a *AgentHandler) Script(req api.Context) error { diff --git a/pkg/api/handlers/defaultmodelalias.go b/pkg/api/handlers/defaultmodelalias.go new file mode 100644 index 000000000..85c508df9 --- /dev/null +++ b/pkg/api/handlers/defaultmodelalias.go @@ -0,0 +1,93 @@ +package handlers + +import ( + "github.com/otto8-ai/otto8/apiclient/types" + "github.com/otto8-ai/otto8/pkg/api" + v1 "github.com/otto8-ai/otto8/pkg/storage/apis/otto.otto8.ai/v1" + "github.com/otto8-ai/otto8/pkg/system" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type DefaultModelAliasHandler struct{} + +func NewDefaultModelAliasHandler() *DefaultModelAliasHandler { + return &DefaultModelAliasHandler{} +} + +func (d *DefaultModelAliasHandler) Create(req api.Context) error { + var manifest types.DefaultModelAliasManifest + if err := req.Read(&manifest); err != nil { + return err + } + + dma := v1.DefaultModelAlias{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: system.DefaultModelAliasPrefix, + Namespace: req.Namespace(), + }, + Spec: v1.DefaultModelAliasSpec{ + Manifest: manifest, + }, + } + + if err := req.Create(&dma); err != nil { + return err + } + + return req.WriteCreated(convertDefaultModelAlias(dma)) +} + +func (d *DefaultModelAliasHandler) GetByID(req api.Context) error { + var dma v1.DefaultModelAlias + if err := req.Get(&dma, req.PathValue("id")); err != nil { + return err + } + return req.Write(convertDefaultModelAlias(dma)) +} + +func (d *DefaultModelAliasHandler) List(req api.Context) error { + var dmaList v1.DefaultModelAliasList + if err := req.List(&dmaList); err != nil { + return err + } + + resp := make([]types.DefaultModelAlias, 0, len(dmaList.Items)) + for _, dma := range dmaList.Items { + resp = append(resp, convertDefaultModelAlias(dma)) + } + return req.Write(types.DefaultModelAliasList{Items: resp}) +} + +func (d *DefaultModelAliasHandler) Update(req api.Context) error { + var dma v1.DefaultModelAlias + if err := req.Get(&dma, req.PathValue("id")); err != nil { + return err + } + + var manifest types.DefaultModelAliasManifest + if err := req.Read(&manifest); err != nil { + return err + } + + dma.Spec.Manifest = manifest + if err := req.Update(&dma); err != nil { + return err + } + + return req.WriteCreated(convertDefaultModelAlias(dma)) +} + +func (d *DefaultModelAliasHandler) Delete(req api.Context) error { + return req.Delete(&v1.DefaultModelAlias{ + ObjectMeta: metav1.ObjectMeta{ + Name: req.PathValue("id"), + Namespace: req.Namespace(), + }, + }) +} + +func convertDefaultModelAlias(d v1.DefaultModelAlias) types.DefaultModelAlias { + return types.DefaultModelAlias{ + DefaultModelAliasManifest: d.Spec.Manifest, + } +} diff --git a/pkg/api/handlers/model.go b/pkg/api/handlers/model.go index d8e3e6d67..761fc1ba6 100644 --- a/pkg/api/handlers/model.go +++ b/pkg/api/handlers/model.go @@ -70,7 +70,7 @@ func (a *ModelHandler) Update(req api.Context) error { existing.Spec.Manifest = model - if err := validateModelManifestAndSetDefaults(req.Context(), req.Storage, &existing); err != nil { + if err := validateModelManifestAndSetDefaults(&existing); err != nil { return err } @@ -115,7 +115,7 @@ func (a *ModelHandler) Create(req api.Context) error { }, } - if err := validateModelManifestAndSetDefaults(req.Context(), req.Storage, &model); err != nil { + if err := validateModelManifestAndSetDefaults(&model); err != nil { return err } @@ -187,7 +187,7 @@ func convertModelProviderToolRef(toolRef v1.ToolReference) *types.ModelProviderS } } -func validateModelManifestAndSetDefaults(ctx context.Context, c kclient.Client, newModel *v1.Model) error { +func validateModelManifestAndSetDefaults(newModel *v1.Model) error { var errs []error if newModel.Spec.Manifest.TargetModel == "" { errs = append(errs, fmt.Errorf("field targetModel is required")) @@ -197,21 +197,7 @@ func validateModelManifestAndSetDefaults(ctx context.Context, c kclient.Client, } if newModel.Spec.Manifest.Usage == "" { - newModel.Spec.Manifest.Usage = types.ModelUsageAgent - } - - if newModel.Spec.Manifest.Default && newModel.Spec.Manifest.Active { - // Ensure only one default model exists - var modelList v1.ModelList - if err := c.List(ctx, &modelList); err != nil { - return err - } - - for _, model := range modelList.Items { - if model.Spec.Manifest.Default && model.Spec.Manifest.Active && model.Name != newModel.Name { - return types.NewErrBadRequest("model %s is already the default model", model.Name) - } - } + newModel.Spec.Manifest.Usage = types.ModelUsageLLM } return errors.Join(errs...) diff --git a/pkg/api/handlers/workflows.go b/pkg/api/handlers/workflows.go index 63bdc8d60..6c24de7fe 100644 --- a/pkg/api/handlers/workflows.go +++ b/pkg/api/handlers/workflows.go @@ -87,7 +87,12 @@ func (a *WorkflowHandler) Update(req api.Context) error { return err } - return req.Write(convertWorkflow(wf, server.GetURLPrefix(req))) + resp, err := convertWorkflow(wf, req) + if err != nil { + return err + } + + return req.WriteCreated(resp) } func (a *WorkflowHandler) Delete(req api.Context) error { @@ -123,24 +128,40 @@ func (a *WorkflowHandler) Create(req api.Context) error { return err } - return req.WriteCreated(convertWorkflow(workflow, server.GetURLPrefix(req))) + resp, err := convertWorkflow(workflow, req) + if err != nil { + return err + } + + return req.WriteCreated(resp) } -func convertWorkflow(workflow v1.Workflow, prefix string) *types.Workflow { +func convertWorkflow(workflow v1.Workflow, req api.Context) (*types.Workflow, error) { var links []string - if prefix != "" { + if prefix := server.GetURLPrefix(req); prefix != "" { alias := workflow.Name if workflow.Status.AliasAssigned && workflow.Spec.Manifest.Alias != "" { alias = workflow.Spec.Manifest.Alias } links = []string{"invoke", prefix + "/invoke/" + alias} } - return &types.Workflow{ - Metadata: MetadataFrom(&workflow, links...), - WorkflowManifest: workflow.Spec.Manifest, - AliasAssigned: workflow.Status.AliasAssigned, - AuthStatus: workflow.Status.AuthStatus, + + var embeddingModel string + if len(workflow.Status.KnowledgeSetNames) > 0 { + var ks v1.KnowledgeSet + if err := req.Get(&ks, workflow.Status.KnowledgeSetNames[0]); err != nil { + return nil, fmt.Errorf("failed to get KnowledgeSet %q: %v", workflow.Status.KnowledgeSetNames[0], err) + } + embeddingModel = ks.Status.TextEmbeddingModel } + + return &types.Workflow{ + Metadata: MetadataFrom(&workflow, links...), + WorkflowManifest: workflow.Spec.Manifest, + AliasAssigned: workflow.Status.AliasAssigned, + AuthStatus: workflow.Status.AuthStatus, + TextEmbeddingModel: embeddingModel, + }, nil } func (a *WorkflowHandler) ByID(req api.Context) error { @@ -153,7 +174,12 @@ func (a *WorkflowHandler) ByID(req api.Context) error { return err } - return req.Write(convertWorkflow(workflow, server.GetURLPrefix(req))) + resp, err := convertWorkflow(workflow, req) + if err != nil { + return err + } + + return req.WriteCreated(resp) } func (a *WorkflowHandler) List(req api.Context) error { @@ -164,7 +190,12 @@ func (a *WorkflowHandler) List(req api.Context) error { var resp types.WorkflowList for _, workflow := range workflowList.Items { - resp.Items = append(resp.Items, *convertWorkflow(workflow, server.GetURLPrefix(req))) + convertedWorkflow, err := convertWorkflow(workflow, req) + if err != nil { + return err + } + + resp.Items = append(resp.Items, *convertedWorkflow) } return req.Write(resp) @@ -181,12 +212,22 @@ func (a *WorkflowHandler) EnsureCredentialForKnowledgeSource(req api.Context) er // If auth is not required, then don't continue. if authStatus.Required != nil && !*authStatus.Required { - return req.Write(convertWorkflow(wf, server.GetURLPrefix(req))) + resp, err := convertWorkflow(wf, req) + if err != nil { + return err + } + + return req.WriteCreated(resp) } // if auth is already authenticated, then don't continue. if authStatus.Authenticated { - return req.Write(convertWorkflow(wf, server.GetURLPrefix(req))) + resp, err := convertWorkflow(wf, req) + if err != nil { + return err + } + + return req.WriteCreated(resp) } credentialTool, err := v1.CredentialTool(req.Context(), req.Storage, req.Namespace(), ref) @@ -202,7 +243,12 @@ func (a *WorkflowHandler) EnsureCredentialForKnowledgeSource(req api.Context) er authStatus.Required = &[]bool{false}[0] wf.Status.AuthStatus[ref] = authStatus - return req.Write(convertWorkflow(wf, server.GetURLPrefix(req))) + resp, err := convertWorkflow(wf, req) + if err != nil { + return err + } + + return req.WriteCreated(resp) } oauthLogin := &v1.OAuthAppLogin{ @@ -236,7 +282,12 @@ func (a *WorkflowHandler) EnsureCredentialForKnowledgeSource(req api.Context) er } wf.Status.AuthStatus[ref] = oauthLogin.Status.External - return req.Write(convertWorkflow(wf, server.GetURLPrefix(req))) + resp, err := convertWorkflow(wf, req) + if err != nil { + return err + } + + return req.WriteCreated(resp) } func (a *WorkflowHandler) Script(req api.Context) error { diff --git a/pkg/api/router/router.go b/pkg/api/router/router.go index c5812f2e3..371b9a76b 100644 --- a/pkg/api/router/router.go +++ b/pkg/api/router/router.go @@ -24,6 +24,7 @@ func Router(services *services.Services) (http.Handler, error) { models := handlers.NewModelHandler() prompt := handlers.NewPromptHandler(services.GPTClient) emailreceiver := handlers.NewEmailReceiverHandler(services.EmailServerName) + defaultModelAliases := handlers.NewDefaultModelAliasHandler() // Version mux.HandleFunc("GET /api/version", handlers.GetVersion) @@ -217,6 +218,13 @@ func Router(services *services.Services) (http.Handler, error) { mux.HandleFunc("GET /api/models", models.List) mux.HandleFunc("GET /api/models/{id}", models.ByID) + // Default Model Aliases + mux.HandleFunc("GET /api/default-model-aliases", defaultModelAliases.List) + mux.HandleFunc("GET /api/default-model-aliases/{id}", defaultModelAliases.GetByID) + mux.HandleFunc("POST /api/default-model-aliases", defaultModelAliases.Create) + mux.HandleFunc("PUT /api/default-model-aliases/{id}", defaultModelAliases.Update) + mux.HandleFunc("DELETE /api/default-model-aliases/{id}", defaultModelAliases.Delete) + // Prompt mux.HandleFunc("POST /api/prompt", prompt.Prompt) diff --git a/pkg/controller/data/data.go b/pkg/controller/data/data.go index 0ae598fea..6fbc57793 100644 --- a/pkg/controller/data/data.go +++ b/pkg/controller/data/data.go @@ -17,6 +17,9 @@ var ottoData []byte //go:embed default-models.yaml var defaultModelsData []byte +//go:embed default-model-aliases.yaml +var defaultModelAliasesData []byte + func Data(ctx context.Context, c kclient.Client) error { var defaultModels v1.ModelList if err := yaml.Unmarshal(defaultModelsData, &defaultModels); err != nil { @@ -42,6 +45,22 @@ func Data(ctx context.Context, c kclient.Client) error { } } + var defaultModelAliases v1.DefaultModelAliasList + if err := yaml.Unmarshal(defaultModelAliasesData, &defaultModelAliases); err != nil { + return err + } + + for _, alias := range defaultModelAliases.Items { + var existing v1.DefaultModelAlias + if err := c.Get(ctx, kclient.ObjectKey{Namespace: alias.Namespace, Name: alias.Name}, &existing); apierrors.IsNotFound(err) { + if err := c.Create(ctx, &alias); err != nil { + return err + } + } else if err != nil { + return err + } + } + var otto v1.Agent if err := yaml.Unmarshal(ottoData, &otto); err != nil { return err diff --git a/pkg/controller/data/default-model-aliases.yaml b/pkg/controller/data/default-model-aliases.yaml new file mode 100644 index 000000000..2df678916 --- /dev/null +++ b/pkg/controller/data/default-model-aliases.yaml @@ -0,0 +1,37 @@ +items: + - apiVersion: otto.otto8.ai/v1 + kind: DefaultModelAlias + metadata: + name: llm + namespace: default + spec: + manifest: + alias: llm + model: gpt-4o + - apiVersion: otto.otto8.ai/v1 + kind: DefaultModelAlias + metadata: + name: llm-mini + namespace: default + spec: + manifest: + alias: llm-mini + model: gpt-4o-mini + - apiVersion: otto.otto8.ai/v1 + kind: DefaultModelAlias + metadata: + name: text-embedding + namespace: default + spec: + manifest: + alias: text-embedding + model: text-embedding-3-small + - apiVersion: otto.otto8.ai/v1 + kind: DefaultModelAlias + metadata: + name: image-generation + namespace: default + spec: + manifest: + alias: image-generation + model: dall-e-3 \ No newline at end of file diff --git a/pkg/controller/data/default-models.yaml b/pkg/controller/data/default-models.yaml index 2ce80a347..26da4bd27 100644 --- a/pkg/controller/data/default-models.yaml +++ b/pkg/controller/data/default-models.yaml @@ -8,9 +8,8 @@ items: manifest: targetModel: gpt-4o modelProvider: openai-model-provider - default: true active: true - usage: agent + usage: llm - apiVersion: otto.otto8.ai/v1 kind: Model metadata: @@ -54,7 +53,7 @@ items: targetModel: gpt-4o-mini modelProvider: openai-model-provider active: true - usage: agent + usage: llm - apiVersion: otto.otto8.ai/v1 kind: Model metadata: @@ -65,4 +64,4 @@ items: targetModel: gpt-3.5-turbo modelProvider: openai-model-provider active: true - usage: agent + usage: llm diff --git a/pkg/controller/handlers/alias/alias.go b/pkg/controller/handlers/alias/alias.go index 68a5f349e..7f82b815e 100644 --- a/pkg/controller/handlers/alias/alias.go +++ b/pkg/controller/handlers/alias/alias.go @@ -55,7 +55,6 @@ func AssignAlias(req router.Request, _ router.Response) error { TargetNamespace: req.Object.GetNamespace(), TargetKind: gvk.Kind, }, - Status: v1.EmptyStatus{}, } if err := create.IfNotExists(req.Ctx, req.Client, alias); err != nil { return err diff --git a/pkg/controller/handlers/knowledgefile/knowledgefile.go b/pkg/controller/handlers/knowledgefile/knowledgefile.go index c6d6da844..ae2fab400 100644 --- a/pkg/controller/handlers/knowledgefile/knowledgefile.go +++ b/pkg/controller/handlers/knowledgefile/knowledgefile.go @@ -81,6 +81,11 @@ func (h *Handler) IngestFile(req router.Request, _ router.Response) error { return kclient.IgnoreNotFound(err) } + if ks.Status.TextEmbeddingModel == "" { + // Wait for the embedding model to be set + return nil + } + thread, err := getThread(req.Ctx, req.Client, &ks, &source) if err != nil { return err @@ -255,6 +260,8 @@ func (h *Handler) ingest(ctx context.Context, client kclient.Client, file *v1.Kn "workspaceID": thread.Status.WorkspaceID, "workspaceFileName": outputFile(file.Spec.FileName), }, + }, invoke.SystemTaskOptions{ + Env: []string{"OPENAI_EMBEDDING_MODEL=" + ks.Status.TextEmbeddingModel}, }) if err != nil { return fmt.Errorf("failed to invoke ingestion task, error: %w", err) diff --git a/pkg/controller/handlers/knowledgeset/knowledgeset.go b/pkg/controller/handlers/knowledgeset/knowledgeset.go index 1e13a83db..e97ff926c 100644 --- a/pkg/controller/handlers/knowledgeset/knowledgeset.go +++ b/pkg/controller/handlers/knowledgeset/knowledgeset.go @@ -7,6 +7,7 @@ import ( "github.com/otto8-ai/nah/pkg/name" "github.com/otto8-ai/nah/pkg/router" + "github.com/otto8-ai/otto8/apiclient/types" "github.com/otto8-ai/otto8/pkg/aihelper" "github.com/otto8-ai/otto8/pkg/create" "github.com/otto8-ai/otto8/pkg/invoke" @@ -105,13 +106,62 @@ func (h *Handler) createThread(ctx context.Context, c kclient.Client, ks *v1.Kno func (h *Handler) CheckHasContent(req router.Request, _ router.Response) error { ks := req.Object.(*v1.KnowledgeSet) - files := &v1.KnowledgeFileList{} - if err := req.Client.List(req.Ctx, files, kclient.InNamespace(ks.Namespace), kclient.MatchingFields{ + + // This is a hack to track exactly when the knowledge set has no more content. + // The issue is triggers. Triggers on field or label selectors work fine, but not for deleted objects. + // When an object is deleted, there is no way to tell if it matches the field selector because the object is gone. + // Therefore, field and label selector triggers don't trigger on deletion. + // However, it is important that we clean up the dataset when the knowledge set is empty. + // So, we track a single file because this will be triggered when the file is deleted. Once the last file is deleted, then the knowledge set is empty, + // and we can clean up the dataset. + if ks.Status.ExistingFile != "" { + var file v1.KnowledgeFile + if err := req.Get(&file, req.Namespace, ks.Status.ExistingFile); err == nil { + return nil + } else if !apierrors.IsNotFound(err) { + return err + } + } + + var files v1.KnowledgeFileList + if err := req.Client.List(req.Ctx, &files, kclient.InNamespace(ks.Namespace), kclient.MatchingFields{ "spec.knowledgeSetName": ks.Name, - }, kclient.Limit(1)); err != nil { + }); err != nil { return err } + ks.Status.HasContent = len(files.Items) > 0 + if !ks.Status.HasContent { + // Reset the embedding model so it can be implicitly updated when knowledge is added. + ks.Status.TextEmbeddingModel = "" + ks.Status.ExistingFile = "" + } else { + ks.Status.ExistingFile = files.Items[0].Name + } + + return nil +} + +func (h *Handler) SetEmbeddingModel(req router.Request, _ router.Response) error { + ks := req.Object.(*v1.KnowledgeSet) + if !ks.Status.HasContent || ks.Status.TextEmbeddingModel != "" { + return nil + } + + if ks.Spec.TextEmbeddingModel != "" { + ks.Status.TextEmbeddingModel = ks.Spec.TextEmbeddingModel + return nil + } + + var defaultEmbeddingModel v1.DefaultModelAlias + if err := req.Get(&defaultEmbeddingModel, req.Namespace, string(types.DefaultModelAliasTypeTextEmbedding)); err == nil { + ks.Status.TextEmbeddingModel = defaultEmbeddingModel.Spec.Manifest.Model + } else if apierrors.IsNotFound(err) { + ks.Status.TextEmbeddingModel = "text-embedding-3-small" + } else if err != nil { + return err + } + return nil } @@ -127,7 +177,7 @@ func (h *Handler) CreateWorkspace(req router.Request, _ router.Response) error { func (h *Handler) Cleanup(req router.Request, _ router.Response) error { ks := req.Object.(*v1.KnowledgeSet) - if ks.Status.ThreadName == "" { + if ks.Status.ThreadName == "" || (ks.DeletionTimestamp.IsZero() && ks.Status.HasContent) { return nil } @@ -138,9 +188,7 @@ func (h *Handler) Cleanup(req router.Request, _ router.Response) error { return err } - task, err := h.invoker.SystemTask(req.Ctx, &thread, system.KnowledgeDeleteTool, map[string]any{ - "dataset": ks.Namespace + "/" + ks.Name, - }) + task, err := h.invoker.SystemTask(req.Ctx, &thread, system.KnowledgeDeleteTool, ks.Namespace+"/"+ks.Name) if err != nil { return err } diff --git a/pkg/controller/handlers/threads/threads.go b/pkg/controller/handlers/threads/threads.go index 3604f2793..56d310344 100644 --- a/pkg/controller/handlers/threads/threads.go +++ b/pkg/controller/handlers/threads/threads.go @@ -87,13 +87,23 @@ func CreateKnowledgeSet(req router.Request, _ router.Response) error { Finalizers: []string{v1.KnowledgeSetFinalizer}, }, Spec: v1.KnowledgeSetSpec{ - ThreadName: thread.Name, + ThreadName: thread.Name, + TextEmbeddingModel: thread.Spec.TextEmbeddingModel, }, } + if err := create.OrGet(req.Ctx, req.Client, ws); err != nil { return err } + if ws.Spec.TextEmbeddingModel != thread.Spec.TextEmbeddingModel { + // The thread knowledge set must have the same text embedding model as its agent. + ws.Spec.TextEmbeddingModel = thread.Spec.TextEmbeddingModel + if err := req.Client.Update(req.Ctx, ws); err != nil { + return err + } + } + thread.Status.KnowledgeSetNames = append(thread.Status.KnowledgeSetNames, ws.Name) return req.Client.Status().Update(req.Ctx, thread) } diff --git a/pkg/controller/routes.go b/pkg/controller/routes.go index 0ecc84713..c6bf8607c 100644 --- a/pkg/controller/routes.go +++ b/pkg/controller/routes.go @@ -74,6 +74,7 @@ func (c *Controller) setupRoutes() error { root.Type(&v1.Agent{}).HandlerFunc(alias.AssignAlias) root.Type(&v1.Workflow{}).HandlerFunc(alias.AssignAlias) root.Type(&v1.Model{}).HandlerFunc(alias.AssignAlias) + root.Type(&v1.DefaultModelAlias{}).HandlerFunc(alias.AssignAlias) // Knowledge files root.Type(&v1.KnowledgeFile{}).HandlerFunc(cleanup.Cleanup) @@ -89,9 +90,13 @@ func (c *Controller) setupRoutes() error { // KnowledgeSets root.Type(&v1.KnowledgeSet{}).HandlerFunc(cleanup.Cleanup) root.Type(&v1.KnowledgeSet{}).FinalizeFunc(v1.KnowledgeSetFinalizer, knowledgeset.Cleanup) + // Also cleanup the dataset when there is no content. + // This will allow the user to switch the embedding model implicitly. + root.Type(&v1.KnowledgeSet{}).HandlerFunc(knowledgeset.Cleanup) root.Type(&v1.KnowledgeSet{}).HandlerFunc(knowledgeset.GenerateDataDescription) root.Type(&v1.KnowledgeSet{}).HandlerFunc(knowledgeset.CreateWorkspace) root.Type(&v1.KnowledgeSet{}).HandlerFunc(knowledgeset.CheckHasContent) + root.Type(&v1.KnowledgeSet{}).HandlerFunc(knowledgeset.SetEmbeddingModel) // Webhooks root.Type(&v1.Webhook{}).HandlerFunc(cleanup.Cleanup) diff --git a/pkg/gateway/server/dispatcher/dispatcher.go b/pkg/gateway/server/dispatcher/dispatcher.go index d3dbcc6a7..41e9291a8 100644 --- a/pkg/gateway/server/dispatcher/dispatcher.go +++ b/pkg/gateway/server/dispatcher/dispatcher.go @@ -82,12 +82,23 @@ func (d *Dispatcher) TransformRequest(req *http.Request, namespace string) error } func (d *Dispatcher) getModelProviderForModel(ctx context.Context, namespace, model string) (*v1.Model, error) { - var m v1.Model - if err := alias.Get(ctx, d.client, &m, namespace, model); err != nil { + m, err := alias.GetFromScope(ctx, d.client, "Model", namespace, model) + if err != nil { return nil, err } - return &m, nil + switch m := m.(type) { + case *v1.DefaultModelAlias: + var model v1.Model + if err := alias.Get(ctx, d.client, &model, namespace, m.Spec.Manifest.Model); err != nil { + return nil, err + } + return &model, nil + case *v1.Model: + return m, nil + } + + return nil, fmt.Errorf("model %q not found", model) } func (d *Dispatcher) startModelProvider(ctx context.Context, model *v1.Model) (*url.URL, error) { diff --git a/pkg/invoke/invoker.go b/pkg/invoke/invoker.go index 102ca52ab..1b519ec42 100644 --- a/pkg/invoke/invoker.go +++ b/pkg/invoke/invoker.go @@ -200,7 +200,7 @@ func CreateThreadForAgent(ctx context.Context, c kclient.WithWatch, agent *v1.Ag if agent.Name != "" { agent, err = wait.For(ctx, c, agent, func(agent *v1.Agent) bool { - return agent.Status.WorkspaceName != "" + return agent.Status.WorkspaceName != "" && len(agent.Status.KnowledgeSetNames) > 0 }) if err != nil { return nil, err @@ -208,6 +208,11 @@ func CreateThreadForAgent(ctx context.Context, c kclient.WithWatch, agent *v1.Ag fromWorkspaceNames = []string{agent.Status.WorkspaceName} } + var agentKnowledgeSet v1.KnowledgeSet + if err = c.Get(ctx, router.Key(agent.Namespace, agent.Status.KnowledgeSetNames[0]), &agentKnowledgeSet); err != nil { + return nil, err + } + thread := v1.Thread{ ObjectMeta: metav1.ObjectMeta{ GenerateName: system.ThreadPrefix, @@ -222,6 +227,7 @@ func CreateThreadForAgent(ctx context.Context, c kclient.WithWatch, agent *v1.Ag FromWorkspaceNames: fromWorkspaceNames, UserUID: userUID, AgentAlias: agentAlias, + TextEmbeddingModel: agentKnowledgeSet.Spec.TextEmbeddingModel, }, } return &thread, c.Create(ctx, &thread) diff --git a/pkg/storage/apis/otto.otto8.ai/v1/defaultmodelalias.go b/pkg/storage/apis/otto.otto8.ai/v1/defaultmodelalias.go new file mode 100644 index 000000000..1ea151037 --- /dev/null +++ b/pkg/storage/apis/otto.otto8.ai/v1/defaultmodelalias.go @@ -0,0 +1,46 @@ +package v1 + +import ( + "github.com/otto8-ai/otto8/apiclient/types" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +type DefaultModelAlias struct { + v1.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty"` + + Spec DefaultModelAliasSpec `json:"spec"` + Status DefaultModelAliasStatus `json:"status"` +} + +func (a *DefaultModelAlias) IsAssigned() bool { + return true +} + +func (a *DefaultModelAlias) GetAliasName() string { + return a.Spec.Manifest.Alias +} + +func (a *DefaultModelAlias) SetAssigned(bool) {} + +func (a *DefaultModelAlias) GetAliasScope() string { + return "Model" +} + +type DefaultModelAliasSpec struct { + Manifest types.DefaultModelAliasManifest `json:"manifest"` +} + +type DefaultModelAliasStatus struct { + SetAliasName string `json:"setAliasName"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +type DefaultModelAliasList struct { + v1.TypeMeta `json:",inline"` + v1.ListMeta `json:"metadata,omitempty"` + Items []DefaultModelAlias `json:"items"` +} diff --git a/pkg/storage/apis/otto.otto8.ai/v1/knowledgeset.go b/pkg/storage/apis/otto.otto8.ai/v1/knowledgeset.go index a994d9a3b..d3a742c80 100644 --- a/pkg/storage/apis/otto.otto8.ai/v1/knowledgeset.go +++ b/pkg/storage/apis/otto.otto8.ai/v1/knowledgeset.go @@ -23,6 +23,8 @@ type KnowledgeSetSpec struct { WorkflowName string `json:"workflowName,omitempty"` // ThreadName is the name of the thread that created and owns this knowledge set ThreadName string `json:"threadName,omitempty"` + // TextEmbeddingModel is set when the model is predetermined on creation. For example, agent threads. + TextEmbeddingModel string `json:"textEmbeddingModel,omitempty"` } func (in *KnowledgeSet) GetColumns() [][]string { @@ -73,6 +75,8 @@ type KnowledgeSetStatus struct { SuggestedDataDescription string `json:"suggestedDataDescription,omitempty"` WorkspaceName string `json:"workspaceName,omitempty"` ThreadName string `json:"threadName,omitempty"` + ExistingFile string `json:"existingFile,omitempty"` + TextEmbeddingModel string `json:"textEmbeddingModel,omitempty"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/storage/apis/otto.otto8.ai/v1/model.go b/pkg/storage/apis/otto.otto8.ai/v1/model.go index db262a2f3..89847c6e7 100644 --- a/pkg/storage/apis/otto.otto8.ai/v1/model.go +++ b/pkg/storage/apis/otto.otto8.ai/v1/model.go @@ -1,17 +1,10 @@ package v1 import ( - "fmt" - - "github.com/otto8-ai/nah/pkg/fields" "github.com/otto8-ai/otto8/apiclient/types" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -var ( - _ fields.Fields = (*Model)(nil) -) - // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type Model struct { @@ -33,25 +26,6 @@ func (m *Model) SetAssigned(assigned bool) { m.Status.AliasAssigned = assigned } -func (m *Model) Has(field string) bool { - return m.Get(field) != "" -} - -func (m *Model) Get(field string) string { - if m != nil { - switch field { - case "spec.manifest.default": - return fmt.Sprintf("%v", m.Spec.Manifest.Default) - } - } - - return "" -} - -func (m *Model) FieldNames() []string { - return []string{"spec.manifest.default"} -} - type ModelSpec struct { Manifest types.ModelManifest `json:"manifest,omitempty"` } diff --git a/pkg/storage/apis/otto.otto8.ai/v1/run.go b/pkg/storage/apis/otto.otto8.ai/v1/run.go index 3dc313744..5437142f8 100644 --- a/pkg/storage/apis/otto.otto8.ai/v1/run.go +++ b/pkg/storage/apis/otto.otto8.ai/v1/run.go @@ -7,11 +7,12 @@ import ( ) const ( - RunFinalizer = "otto.otto8.ai/run" - KnowledgeFileFinalizer = "otto.otto8.ai/knowledge-file" - WorkspaceFinalizer = "otto.otto8.ai/workspace" - KnowledgeSetFinalizer = "otto.otto8.ai/knowledge-set" - KnowledgeSourceFinalizer = "otto.otto8.ai/knowledge-source" + RunFinalizer = "otto.otto8.ai/run" + KnowledgeFileFinalizer = "otto.otto8.ai/knowledge-file" + WorkspaceFinalizer = "otto.otto8.ai/workspace" + KnowledgeSetFinalizer = "otto.otto8.ai/knowledge-set" + KnowledgeSourceFinalizer = "otto.otto8.ai/knowledge-source" + DefaultModelAliasFinalizer = "otto.otto8.ai/default-model-alias" ) // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/storage/apis/otto.otto8.ai/v1/scheme.go b/pkg/storage/apis/otto.otto8.ai/v1/scheme.go index c31d16c08..5bc7b5775 100644 --- a/pkg/storage/apis/otto.otto8.ai/v1/scheme.go +++ b/pkg/storage/apis/otto.otto8.ai/v1/scheme.go @@ -59,6 +59,8 @@ func AddToSchemeWithGV(scheme *runtime.Scheme, schemeGroupVersion schema.GroupVe &OAuthAppLoginList{}, &Model{}, &ModelList{}, + &DefaultModelAlias{}, + &DefaultModelAliasList{}, ); err != nil { return err } diff --git a/pkg/storage/apis/otto.otto8.ai/v1/thread.go b/pkg/storage/apis/otto.otto8.ai/v1/thread.go index c85e05905..d7220b089 100644 --- a/pkg/storage/apis/otto.otto8.ai/v1/thread.go +++ b/pkg/storage/apis/otto.otto8.ai/v1/thread.go @@ -63,6 +63,7 @@ type ThreadSpec struct { FromWorkspaceNames []string `json:"fromWorkspaceNames,omitempty"` OAuthAppLoginName string `json:"oAuthAppLoginName,omitempty"` UserUID string `json:"userUID,omitempty"` + TextEmbeddingModel string `json:"textEmbeddingModel,omitempty"` SystemTask bool `json:"systemTask,omitempty"` } diff --git a/pkg/storage/apis/otto.otto8.ai/v1/zz_generated.deepcopy.go b/pkg/storage/apis/otto.otto8.ai/v1/zz_generated.deepcopy.go index 43bb0ac6c..88c31d86e 100644 --- a/pkg/storage/apis/otto.otto8.ai/v1/zz_generated.deepcopy.go +++ b/pkg/storage/apis/otto.otto8.ai/v1/zz_generated.deepcopy.go @@ -299,6 +299,96 @@ func (in *CronJobStatus) DeepCopy() *CronJobStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DefaultModelAlias) DeepCopyInto(out *DefaultModelAlias) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DefaultModelAlias. +func (in *DefaultModelAlias) DeepCopy() *DefaultModelAlias { + if in == nil { + return nil + } + out := new(DefaultModelAlias) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DefaultModelAlias) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DefaultModelAliasList) DeepCopyInto(out *DefaultModelAliasList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]DefaultModelAlias, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DefaultModelAliasList. +func (in *DefaultModelAliasList) DeepCopy() *DefaultModelAliasList { + if in == nil { + return nil + } + out := new(DefaultModelAliasList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DefaultModelAliasList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DefaultModelAliasSpec) DeepCopyInto(out *DefaultModelAliasSpec) { + *out = *in + out.Manifest = in.Manifest +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DefaultModelAliasSpec. +func (in *DefaultModelAliasSpec) DeepCopy() *DefaultModelAliasSpec { + if in == nil { + return nil + } + out := new(DefaultModelAliasSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DefaultModelAliasStatus) DeepCopyInto(out *DefaultModelAliasStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DefaultModelAliasStatus. +func (in *DefaultModelAliasStatus) DeepCopy() *DefaultModelAliasStatus { + if in == nil { + return nil + } + out := new(DefaultModelAliasStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EmailReceiver) DeepCopyInto(out *EmailReceiver) { *out = *in diff --git a/pkg/storage/openapi/generated/openapi_generated.go b/pkg/storage/openapi/generated/openapi_generated.go index f146ee664..c7c348ed1 100644 --- a/pkg/storage/openapi/generated/openapi_generated.go +++ b/pkg/storage/openapi/generated/openapi_generated.go @@ -29,6 +29,9 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/otto8-ai/otto8/apiclient/types.CronJob": schema_otto8_ai_otto8_apiclient_types_CronJob(ref), "github.com/otto8-ai/otto8/apiclient/types.CronJobList": schema_otto8_ai_otto8_apiclient_types_CronJobList(ref), "github.com/otto8-ai/otto8/apiclient/types.CronJobManifest": schema_otto8_ai_otto8_apiclient_types_CronJobManifest(ref), + "github.com/otto8-ai/otto8/apiclient/types.DefaultModelAlias": schema_otto8_ai_otto8_apiclient_types_DefaultModelAlias(ref), + "github.com/otto8-ai/otto8/apiclient/types.DefaultModelAliasList": schema_otto8_ai_otto8_apiclient_types_DefaultModelAliasList(ref), + "github.com/otto8-ai/otto8/apiclient/types.DefaultModelAliasManifest": schema_otto8_ai_otto8_apiclient_types_DefaultModelAliasManifest(ref), "github.com/otto8-ai/otto8/apiclient/types.EmailReceiver": schema_otto8_ai_otto8_apiclient_types_EmailReceiver(ref), "github.com/otto8-ai/otto8/apiclient/types.EmailReceiverList": schema_otto8_ai_otto8_apiclient_types_EmailReceiverList(ref), "github.com/otto8-ai/otto8/apiclient/types.EmailReceiverManifest": schema_otto8_ai_otto8_apiclient_types_EmailReceiverManifest(ref), @@ -63,6 +66,11 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/otto8-ai/otto8/apiclient/types.Step": schema_otto8_ai_otto8_apiclient_types_Step(ref), "github.com/otto8-ai/otto8/apiclient/types.StepTemplateInvoke": schema_otto8_ai_otto8_apiclient_types_StepTemplateInvoke(ref), "github.com/otto8-ai/otto8/apiclient/types.SubFlow": schema_otto8_ai_otto8_apiclient_types_SubFlow(ref), + "github.com/otto8-ai/otto8/apiclient/types.Task": schema_otto8_ai_otto8_apiclient_types_Task(ref), + "github.com/otto8-ai/otto8/apiclient/types.TaskIf": schema_otto8_ai_otto8_apiclient_types_TaskIf(ref), + "github.com/otto8-ai/otto8/apiclient/types.TaskList": schema_otto8_ai_otto8_apiclient_types_TaskList(ref), + "github.com/otto8-ai/otto8/apiclient/types.TaskManifest": schema_otto8_ai_otto8_apiclient_types_TaskManifest(ref), + "github.com/otto8-ai/otto8/apiclient/types.TaskStep": schema_otto8_ai_otto8_apiclient_types_TaskStep(ref), "github.com/otto8-ai/otto8/apiclient/types.Template": schema_otto8_ai_otto8_apiclient_types_Template(ref), "github.com/otto8-ai/otto8/apiclient/types.Thread": schema_otto8_ai_otto8_apiclient_types_Thread(ref), "github.com/otto8-ai/otto8/apiclient/types.ThreadList": schema_otto8_ai_otto8_apiclient_types_ThreadList(ref), @@ -96,6 +104,10 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/otto8-ai/otto8/pkg/storage/apis/otto.otto8.ai/v1.CronJobList": schema_storage_apis_ottootto8ai_v1_CronJobList(ref), "github.com/otto8-ai/otto8/pkg/storage/apis/otto.otto8.ai/v1.CronJobSpec": schema_storage_apis_ottootto8ai_v1_CronJobSpec(ref), "github.com/otto8-ai/otto8/pkg/storage/apis/otto.otto8.ai/v1.CronJobStatus": schema_storage_apis_ottootto8ai_v1_CronJobStatus(ref), + "github.com/otto8-ai/otto8/pkg/storage/apis/otto.otto8.ai/v1.DefaultModelAlias": schema_storage_apis_ottootto8ai_v1_DefaultModelAlias(ref), + "github.com/otto8-ai/otto8/pkg/storage/apis/otto.otto8.ai/v1.DefaultModelAliasList": schema_storage_apis_ottootto8ai_v1_DefaultModelAliasList(ref), + "github.com/otto8-ai/otto8/pkg/storage/apis/otto.otto8.ai/v1.DefaultModelAliasSpec": schema_storage_apis_ottootto8ai_v1_DefaultModelAliasSpec(ref), + "github.com/otto8-ai/otto8/pkg/storage/apis/otto.otto8.ai/v1.DefaultModelAliasStatus": schema_storage_apis_ottootto8ai_v1_DefaultModelAliasStatus(ref), "github.com/otto8-ai/otto8/pkg/storage/apis/otto.otto8.ai/v1.EmailReceiver": schema_storage_apis_ottootto8ai_v1_EmailReceiver(ref), "github.com/otto8-ai/otto8/pkg/storage/apis/otto.otto8.ai/v1.EmailReceiverList": schema_storage_apis_ottootto8ai_v1_EmailReceiverList(ref), "github.com/otto8-ai/otto8/pkg/storage/apis/otto.otto8.ai/v1.EmailReceiverSpec": schema_storage_apis_ottootto8ai_v1_EmailReceiverSpec(ref), @@ -263,6 +275,12 @@ func schema_otto8_ai_otto8_apiclient_types_Agent(ref common.ReferenceCallback) c }, }, }, + "textEmbeddingModel": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, }, Required: []string{"Metadata", "AgentManifest"}, }, @@ -861,6 +879,82 @@ func schema_otto8_ai_otto8_apiclient_types_CronJobManifest(ref common.ReferenceC } } +func schema_otto8_ai_otto8_apiclient_types_DefaultModelAlias(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "DefaultModelAliasManifest": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/otto8-ai/otto8/apiclient/types.DefaultModelAliasManifest"), + }, + }, + }, + Required: []string{"DefaultModelAliasManifest"}, + }, + }, + Dependencies: []string{ + "github.com/otto8-ai/otto8/apiclient/types.DefaultModelAliasManifest"}, + } +} + +func schema_otto8_ai_otto8_apiclient_types_DefaultModelAliasList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/otto8-ai/otto8/apiclient/types.DefaultModelAlias"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "github.com/otto8-ai/otto8/apiclient/types.DefaultModelAlias"}, + } +} + +func schema_otto8_ai_otto8_apiclient_types_DefaultModelAliasManifest(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "alias": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "model": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"alias", "model"}, + }, + }, + } +} + func schema_otto8_ai_otto8_apiclient_types_EmailReceiver(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -1614,13 +1708,6 @@ func schema_otto8_ai_otto8_apiclient_types_ModelManifest(ref common.ReferenceCal Format: "", }, }, - "default": { - SchemaProps: spec.SchemaProps{ - Default: false, - Type: []string{"boolean"}, - Format: "", - }, - }, "usage": { SchemaProps: spec.SchemaProps{ Type: []string{"string"}, @@ -1628,7 +1715,7 @@ func schema_otto8_ai_otto8_apiclient_types_ModelManifest(ref common.ReferenceCal }, }, }, - Required: []string{"active", "default"}, + Required: []string{"active"}, }, }, } @@ -2440,6 +2527,180 @@ func schema_otto8_ai_otto8_apiclient_types_SubFlow(ref common.ReferenceCallback) } } +func schema_otto8_ai_otto8_apiclient_types_Task(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "Metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/otto8-ai/otto8/apiclient/types.Metadata"), + }, + }, + "TaskManifest": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/otto8-ai/otto8/apiclient/types.TaskManifest"), + }, + }, + }, + Required: []string{"Metadata", "TaskManifest"}, + }, + }, + Dependencies: []string{ + "github.com/otto8-ai/otto8/apiclient/types.Metadata", "github.com/otto8-ai/otto8/apiclient/types.TaskManifest"}, + } +} + +func schema_otto8_ai_otto8_apiclient_types_TaskIf(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "condition": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "steps": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/otto8-ai/otto8/apiclient/types.TaskStep"), + }, + }, + }, + }, + }, + "else": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/otto8-ai/otto8/apiclient/types.TaskStep"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/otto8-ai/otto8/apiclient/types.TaskStep"}, + } +} + +func schema_otto8_ai_otto8_apiclient_types_TaskList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/otto8-ai/otto8/apiclient/types.Task"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "github.com/otto8-ai/otto8/apiclient/types.Task"}, + } +} + +func schema_otto8_ai_otto8_apiclient_types_TaskManifest(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "steps": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/otto8-ai/otto8/apiclient/types.TaskStep"), + }, + }, + }, + }, + }, + }, + Required: []string{"name", "description", "steps"}, + }, + }, + Dependencies: []string{ + "github.com/otto8-ai/otto8/apiclient/types.TaskStep"}, + } +} + +func schema_otto8_ai_otto8_apiclient_types_TaskStep(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "id": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "if": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/otto8-ai/otto8/apiclient/types.TaskIf"), + }, + }, + "step": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/otto8-ai/otto8/apiclient/types.TaskIf"}, + } +} + func schema_otto8_ai_otto8_apiclient_types_Template(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -3176,6 +3437,12 @@ func schema_otto8_ai_otto8_apiclient_types_Workflow(ref common.ReferenceCallback }, }, }, + "textEmbeddingModel": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, }, Required: []string{"Metadata", "WorkflowManifest"}, }, @@ -4016,6 +4283,142 @@ func schema_storage_apis_ottootto8ai_v1_CronJobStatus(ref common.ReferenceCallba } } +func schema_storage_apis_ottootto8ai_v1_DefaultModelAlias(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/otto8-ai/otto8/pkg/storage/apis/otto.otto8.ai/v1.DefaultModelAliasSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/otto8-ai/otto8/pkg/storage/apis/otto.otto8.ai/v1.DefaultModelAliasStatus"), + }, + }, + }, + Required: []string{"spec", "status"}, + }, + }, + Dependencies: []string{ + "github.com/otto8-ai/otto8/pkg/storage/apis/otto.otto8.ai/v1.DefaultModelAliasSpec", "github.com/otto8-ai/otto8/pkg/storage/apis/otto.otto8.ai/v1.DefaultModelAliasStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_storage_apis_ottootto8ai_v1_DefaultModelAliasList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/otto8-ai/otto8/pkg/storage/apis/otto.otto8.ai/v1.DefaultModelAlias"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "github.com/otto8-ai/otto8/pkg/storage/apis/otto.otto8.ai/v1.DefaultModelAlias", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_storage_apis_ottootto8ai_v1_DefaultModelAliasSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "manifest": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/otto8-ai/otto8/apiclient/types.DefaultModelAliasManifest"), + }, + }, + }, + Required: []string{"manifest"}, + }, + }, + Dependencies: []string{ + "github.com/otto8-ai/otto8/apiclient/types.DefaultModelAliasManifest"}, + } +} + +func schema_storage_apis_ottootto8ai_v1_DefaultModelAliasStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "setAliasName": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"setAliasName"}, + }, + }, + } +} + func schema_storage_apis_ottootto8ai_v1_EmailReceiver(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -4572,6 +4975,13 @@ func schema_storage_apis_ottootto8ai_v1_KnowledgeSetSpec(ref common.ReferenceCal Format: "", }, }, + "textEmbeddingModel": { + SchemaProps: spec.SchemaProps{ + Description: "TextEmbeddingModel is set when the model is predetermined on creation. For example, agent threads.", + Type: []string{"string"}, + Format: "", + }, + }, }, }, }, @@ -4610,6 +5020,18 @@ func schema_storage_apis_ottootto8ai_v1_KnowledgeSetStatus(ref common.ReferenceC Format: "", }, }, + "existingFile": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "textEmbeddingModel": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, }, }, }, @@ -5899,6 +6321,12 @@ func schema_storage_apis_ottootto8ai_v1_ThreadSpec(ref common.ReferenceCallback) Format: "", }, }, + "textEmbeddingModel": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, "systemTask": { SchemaProps: spec.SchemaProps{ Type: []string{"boolean"}, @@ -6727,6 +7155,18 @@ func schema_storage_apis_ottootto8ai_v1_WorkflowSpec(ref common.ReferenceCallbac SchemaProps: spec.SchemaProps{ Type: []string{"object"}, Properties: map[string]spec.Schema{ + "agentName": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "userID": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, "manifest": { SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, diff --git a/pkg/system/ids.go b/pkg/system/ids.go index 0ce338b4c..3b7e56f81 100644 --- a/pkg/system/ids.go +++ b/pkg/system/ids.go @@ -19,6 +19,7 @@ const ( EmailReceiverPrefix = "er1" ModelPrefix = "m1" AliasPrefix = "al1" + DefaultModelAliasPrefix = "dma1" ) func IsThreadID(id string) bool { diff --git a/ui/admin/app/components/agent/AgentForm.tsx b/ui/admin/app/components/agent/AgentForm.tsx index 0545ab521..b5b59d4d5 100644 --- a/ui/admin/app/components/agent/AgentForm.tsx +++ b/ui/admin/app/components/agent/AgentForm.tsx @@ -5,6 +5,7 @@ import { useForm } from "react-hook-form"; import useSWR from "swr"; import { z } from "zod"; +import { ModelUsage } from "~/lib/model/models"; import { ModelApiService } from "~/lib/service/api/modelApiService"; import { TypographyH4 } from "~/components/Typography"; @@ -49,7 +50,9 @@ export function AgentForm({ agent, onSubmit, onChange }: AgentFormProps) { const models = useMemo(() => { if (!getModels.data) return []; - return getModels.data.filter((m) => !m.usage || m.usage === "agent"); + return getModels.data.filter( + (m) => !m.usage || m.usage === ModelUsage.LLM + ); }, [getModels.data]); const form = useForm({ diff --git a/ui/admin/app/components/model/ModelForm.tsx b/ui/admin/app/components/model/ModelForm.tsx index ffb2982ef..87bd2423a 100644 --- a/ui/admin/app/components/model/ModelForm.tsx +++ b/ui/admin/app/components/model/ModelForm.tsx @@ -72,7 +72,7 @@ export function ModelForm(props: ModelFormProps) { modelProvider: model?.modelProvider ?? "", active: model?.active ?? true, default: model?.default ?? false, - usage: model?.usage ?? "agent", + usage: model?.usage ?? ModelUsage.LLM, }; }, [model]); diff --git a/ui/admin/app/lib/model/models.ts b/ui/admin/app/lib/model/models.ts index dade1986f..5486dd7a7 100644 --- a/ui/admin/app/lib/model/models.ts +++ b/ui/admin/app/lib/model/models.ts @@ -3,14 +3,14 @@ import { z } from "zod"; import { EntityMeta } from "~/lib/model/primitives"; export const ModelUsage = { - Agent: "agent", + LLM: "llm", TextEmbedding: "text-embedding", ImageGeneration: "image-generation", } as const; export type ModelUsage = (typeof ModelUsage)[keyof typeof ModelUsage]; const ModelUsageLabels = { - [ModelUsage.Agent]: "Agent", + [ModelUsage.LLM]: "LLM", [ModelUsage.TextEmbedding]: "Text Embedding", [ModelUsage.ImageGeneration]: "Image Generation", } as const;