From c20e35419015b123a2e0ff9e685b5878ff5ec18b 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 7e3bca47..bc7f14a1 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 00000000..ae509e20 --- /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 5b1d9530..d2d6ba4c 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 5a08acc7..64b104dd 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 4e1d3368..e6c496f3 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 a9404e1f..8e0c5679 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 29394654..4309b0ab 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 00000000..85c508df --- /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 d8e3e6d6..761fc1ba 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 63bdc8d6..6c24de7f 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 c5812f2e..371b9a76 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 0ae598fe..6fbc5779 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 00000000..2df67891 --- /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 2ce80a34..26da4bd2 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 68a5f349..7f82b815 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 c6d6da84..ae2fab40 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 1e13a83d..e97ff926 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 3604f279..56d31034 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 0ecc8471..c6bf8607 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 d3dbcc6a..41e9291a 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 102ca52a..1b519ec4 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 00000000..1ea15103 --- /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 a994d9a3..d3a742c8 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 db262a2f..89847c6e 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 3dc31374..5437142f 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 c31d16c0..5bc7b577 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 c85e0590..d7220b08 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 43bb0ac6..88c31d86 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 f146ee66..c7c348ed 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 0ce338b4..3b7e56f8 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 0545ab52..b5b59d4d 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 ffb2982e..87bd2423 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 dade1986..5486dd7a 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;