feat: manually mirror opencoze's code from bytedance

Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
fanlv
2025-07-20 17:36:12 +08:00
commit 890153324f
14811 changed files with 1923430 additions and 0 deletions

View File

@@ -0,0 +1,113 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package entity
import (
"sort"
"strings"
"github.com/coze-dev/coze-studio/backend/api/model/kvmemory"
"github.com/coze-dev/coze-studio/backend/api/model/project_memory"
)
type SysConfVariables []*kvmemory.VariableInfo
func (v SysConfVariables) ToVariables() *VariablesMeta {
vars := make([]*VariableMeta, 0)
for _, vv := range v {
if vv == nil {
continue
}
tmp := &VariableMeta{
Keyword: vv.Key,
Description: vv.Description,
DefaultValue: vv.DefaultValue,
VariableType: project_memory.VariableType_KVVariable,
Channel: project_memory.VariableChannel_System,
IsReadOnly: true,
EffectiveChannelList: vv.EffectiveChannelList,
}
tmp.SetupSchema()
vars = append(vars, tmp)
}
return &VariablesMeta{
Variables: vars,
}
}
func (v SysConfVariables) GroupByName() []*kvmemory.GroupVariableInfo {
groups := make(map[string]*kvmemory.GroupVariableInfo)
for _, variable := range v {
if variable == nil {
continue
}
groupName := variable.GroupName
if groupName == "" {
groupName = "未分组" // 处理空分组名
}
if _, ok := groups[groupName]; !ok {
groups[groupName] = &kvmemory.GroupVariableInfo{
GroupName: groupName,
GroupDesc: variable.GroupDesc,
VarInfoList: []*kvmemory.VariableInfo{},
}
}
groups[groupName].VarInfoList = append(groups[groupName].VarInfoList, variable)
}
// 转换为切片并按组名排序
result := make([]*kvmemory.GroupVariableInfo, 0, len(groups))
for _, group := range groups {
result = append(result, group)
}
// 可选:按组名排序
sort.Slice(result, func(i, j int) bool {
return result[i].GroupName < result[j].GroupName
})
return result
}
func (v SysConfVariables) RemoveLocalChannelVariable() SysConfVariables {
var res []*kvmemory.VariableInfo
for _, vv := range v {
ch := v.genChannelFromName(vv.Key)
if ch == project_memory.VariableChannel_Location {
continue
}
res = append(res, vv)
}
return res
}
func (v SysConfVariables) genChannelFromName(name string) project_memory.VariableChannel {
if strings.Contains(name, "lark") {
return project_memory.VariableChannel_Feishu
} else if strings.Contains(name, "lon") || strings.Contains(name, "lat") {
return project_memory.VariableChannel_Location
}
return project_memory.VariableChannel_System
}

View File

@@ -0,0 +1,121 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package entity
import (
"context"
"encoding/base64"
"fmt"
"strconv"
"strings"
"time"
"github.com/coze-dev/coze-studio/backend/api/model/crossdomain/variables"
"github.com/coze-dev/coze-studio/backend/api/model/kvmemory"
"github.com/coze-dev/coze-studio/backend/api/model/project_memory"
"github.com/coze-dev/coze-studio/backend/pkg/errorx"
"github.com/coze-dev/coze-studio/backend/types/errno"
)
type UserVariableMeta struct {
*variables.UserVariableMeta
}
func NewUserVariableMeta(v *variables.UserVariableMeta) *UserVariableMeta {
return &UserVariableMeta{
UserVariableMeta: v,
}
}
type VariableInstance struct {
ID int64
BizType project_memory.VariableConnector
BizID string
Version string
Keyword string
Type int32
Content string
ConnectorUID string
ConnectorID int64
CreatedAt int64
UpdatedAt int64
}
const (
sysUUIDKey string = "sys_uuid"
)
func (v *UserVariableMeta) GenSystemKV(ctx context.Context, keyword string) (*kvmemory.KVItem, error) {
if keyword != sysUUIDKey { // 外场暂时只支持这一个变量
return nil, nil
}
return v.genUUID(ctx)
}
func (v *UserVariableMeta) genUUID(ctx context.Context) (*kvmemory.KVItem, error) {
if v.BizID == "" {
return nil, errorx.New(errno.ErrMemoryGetSysUUIDInstanceCode, errorx.KV("msg", "biz_id is empty"))
}
if v.ConnectorUID == "" {
return nil, errorx.New(errno.ErrMemoryGetSysUUIDInstanceCode, errorx.KV("msg", "connector_uid is empty"))
}
if v.ConnectorID == 0 {
return nil, errorx.New(errno.ErrMemoryGetSysUUIDInstanceCode, errorx.KV("msg", "connector_id is empty"))
}
encryptSysUUIDKey := v.encryptSysUUIDKey(ctx)
now := time.Now().Unix()
return &kvmemory.KVItem{
Keyword: sysUUIDKey,
Value: encryptSysUUIDKey,
Schema: stringSchema,
CreateTime: now,
UpdateTime: now,
IsSystem: true,
}, nil
}
func (v *UserVariableMeta) encryptSysUUIDKey(ctx context.Context) string {
// 拼接四个字段,中间用特殊分隔符(如 |
plain := fmt.Sprintf("%d|%s|%s|%d", v.BizType, v.BizID, v.ConnectorUID, v.ConnectorID)
return base64.StdEncoding.EncodeToString([]byte(plain))
}
func (v *UserVariableMeta) DecryptSysUUIDKey(ctx context.Context, encryptSysUUIDKey string) *VariableInstance {
data, err := base64.StdEncoding.DecodeString(encryptSysUUIDKey)
if err != nil {
return nil
}
parts := strings.Split(string(data), "|")
if len(parts) != 4 {
return nil
}
bizType64, _ := strconv.ParseInt(parts[0], 10, 32)
connectorID, _ := strconv.ParseInt(parts[3], 10, 64)
return &VariableInstance{
BizType: project_memory.VariableConnector(bizType64),
BizID: parts[1],
ConnectorUID: parts[2],
ConnectorID: connectorID,
}
}

View File

@@ -0,0 +1,98 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package entity
import (
"context"
"fmt"
"github.com/coze-dev/coze-studio/backend/api/model/project_memory"
)
type VariableMeta struct {
Keyword string
DefaultValue string
VariableType project_memory.VariableType
Channel project_memory.VariableChannel
Description string
Enable bool
EffectiveChannelList []string
Schema string
IsReadOnly bool
PromptDisabled bool
}
func NewVariableMeta(e *project_memory.Variable) *VariableMeta {
return &VariableMeta{
Keyword: e.Keyword,
DefaultValue: e.DefaultValue,
VariableType: e.VariableType,
Channel: e.Channel,
Description: e.Description,
Enable: e.Enable,
EffectiveChannelList: e.EffectiveChannelList,
Schema: e.Schema,
IsReadOnly: e.IsReadOnly,
}
}
func (v *VariableMeta) ToProjectVariable() *project_memory.Variable {
return &project_memory.Variable{
Keyword: v.Keyword,
DefaultValue: v.DefaultValue,
VariableType: v.VariableType,
Channel: v.Channel,
Description: v.Description,
Enable: v.Enable,
EffectiveChannelList: v.EffectiveChannelList,
Schema: v.Schema,
IsReadOnly: v.IsReadOnly,
}
}
func (v *VariableMeta) GetSchema(ctx context.Context) (*VariableMetaSchema, error) {
return NewVariableMetaSchema([]byte(v.Schema))
}
func (v *VariableMeta) CheckSchema(ctx context.Context) error {
schema, err := NewVariableMetaSchema([]byte(v.Schema))
if err != nil {
return err
}
return schema.check(ctx)
}
const stringSchema = "{\n \"type\": \"string\",\n \"name\": \"%v\",\n \"required\": false\n}"
func (v *VariableMeta) SetupSchema() {
if v.Schema == "" {
v.Schema = fmt.Sprintf(stringSchema, v.Keyword)
}
}
func (v *VariableMeta) SetupIsReadOnly() {
if v.Channel == project_memory.VariableChannel_Feishu ||
v.Channel == project_memory.VariableChannel_Location ||
v.Channel == project_memory.VariableChannel_System {
v.IsReadOnly = true
}
}
func (v *VariableMeta) IsSystem() bool {
return v.Channel == project_memory.VariableChannel_System
}

View File

@@ -0,0 +1,177 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package entity
import (
"context"
"encoding/json"
"regexp"
"github.com/coze-dev/coze-studio/backend/pkg/errorx"
"github.com/coze-dev/coze-studio/backend/types/errno"
)
const (
variableMetaSchemaTypeObject = "object"
variableMetaSchemaTypeArray = "list"
variableMetaSchemaTypeInteger = "integer"
variableMetaSchemaTypeString = "string"
variableMetaSchemaTypeBoolean = "boolean"
variableMetaSchemaTypeNumber = "float"
)
// TODO: remove me later
// {"name":"app_var_arr","enable":true,"description":"121222","type":"list","readonly":false,"schema":{"type":"integer"}}
type VariableMetaSchema struct {
Type string `json:"type,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Readonly bool `json:"readonly,omitempty"`
Enable bool `json:"enable,omitempty"`
Schema json.RawMessage `json:"schema,omitempty"`
}
func NewVariableMetaSchema(schema []byte) (*VariableMetaSchema, error) {
schemaObj := &VariableMetaSchema{}
err := json.Unmarshal(schema, schemaObj)
if err != nil {
return nil, errorx.New(errno.ErrMemorySchemeInvalidCode, errorx.KVf("msg", "schema json invalid: %s \n json = %s", err.Error(), string(schema)))
}
return schemaObj, nil
}
func (v *VariableMetaSchema) IsArrayType() bool {
return v.Type == variableMetaSchemaTypeArray
}
// GetArrayType e.g. schema = {"type":"int"}
func (v *VariableMetaSchema) GetArrayType(schema []byte) (string, error) {
schemaObj, err := NewVariableMetaSchema(schema)
if err != nil {
return "", errorx.New(errno.ErrMemorySchemeInvalidCode, errorx.KVf("msg", "NewVariableMetaSchema failed, %v", err.Error()))
}
if schemaObj.Type == "" {
return "", errorx.New(errno.ErrMemorySchemeInvalidCode, errorx.KVf("msg", "array type not found in %s", schema))
}
return schemaObj.Type, nil
}
func (v *VariableMetaSchema) IsStringType() bool {
return v.Type == variableMetaSchemaTypeString
}
func (v *VariableMetaSchema) IsIntegerType() bool {
return v.Type == variableMetaSchemaTypeInteger
}
func (v *VariableMetaSchema) IsBooleanType() bool {
return v.Type == variableMetaSchemaTypeBoolean
}
func (v *VariableMetaSchema) IsNumberType() bool {
return v.Type == variableMetaSchemaTypeNumber
}
func (v *VariableMetaSchema) IsObjectType() bool {
return v.Type == variableMetaSchemaTypeObject
}
// GetObjetProperties e.g. schema = [{"name":"app_var_12_sdd","enable":true,"description":"s22","type":"string","readonly":false,"schema":""}]
func (v *VariableMetaSchema) GetObjectProperties(schema []byte) (map[string]*VariableMetaSchema, error) {
schemas := make([]*VariableMetaSchema, 0)
err := json.Unmarshal(schema, &schemas)
if err != nil {
return nil, errorx.New(errno.ErrMemorySchemeInvalidCode, errorx.KV("msg", "schema array content json invalid"))
}
properties := make(map[string]*VariableMetaSchema)
for _, schemaObj := range schemas {
properties[schemaObj.Name] = schemaObj
}
return properties, nil
}
func (v *VariableMetaSchema) check(ctx context.Context) error {
return v.checkAppVariableSchema(ctx, v, "")
}
func (v *VariableMetaSchema) checkAppVariableSchema(ctx context.Context, schemaObj *VariableMetaSchema, schema string) (err error) {
if len(schema) == 0 && schemaObj == nil {
return errorx.New(errno.ErrMemorySchemeInvalidCode, errorx.KV("msg", "schema is nil"))
}
if schemaObj == nil {
schemaObj, err = NewVariableMetaSchema([]byte(schema))
if err != nil {
return errorx.New(errno.ErrMemorySchemeInvalidCode, errorx.KVf("msg", "checkAppVariableSchema failed , %v", err.Error()))
}
}
if !schemaObj.nameValidate() {
return errorx.New(errno.ErrMemorySchemeInvalidCode, errorx.KVf("msg", "name(%s) is invalid", schemaObj.Name))
}
if schemaObj.Type == variableMetaSchemaTypeObject {
return v.checkSchemaObj(ctx, schemaObj.Schema)
} else if schemaObj.Type == variableMetaSchemaTypeArray {
_, err := v.GetArrayType(schemaObj.Schema)
if err != nil {
return errorx.New(errno.ErrMemorySchemeInvalidCode, errorx.KVf("msg", "GetArrayType failed : %v", err.Error()))
}
}
return nil
}
func (v *VariableMetaSchema) checkSchemaObj(ctx context.Context, schema []byte) error {
properties, err := v.GetObjectProperties(schema)
if err != nil {
return errorx.New(errno.ErrMemorySchemeInvalidCode, errorx.KVf("msg", "GetObjectProperties failed : %v", err.Error()))
}
for _, schemaObj := range properties {
if err := v.checkAppVariableSchema(ctx, schemaObj, ""); err != nil {
return err
}
}
return nil
}
func (v *VariableMetaSchema) nameValidate() bool {
identifier := v.Name
reservedWords := map[string]bool{
"true": true, "false": true, "and": true, "AND": true,
"or": true, "OR": true, "not": true, "NOT": true,
"null": true, "nil": true, "If": true, "Switch": true,
}
if reservedWords[identifier] {
return false
}
// 检查是否符合后面的部分正则规则
pattern := `^[a-zA-Z_][a-zA-Z_$0-9]*$`
match, _ := regexp.MatchString(pattern, identifier)
return match
}

View File

@@ -0,0 +1,160 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package entity
import (
"fmt"
"github.com/coze-dev/coze-studio/backend/api/model/ocean/cloud/bot_common"
"github.com/coze-dev/coze-studio/backend/api/model/project_memory"
)
type VariablesMeta struct {
ID int64
CreatorID int64
BizType project_memory.VariableConnector
BizID string
CreatedAt int64
UpdatedAt int64
Version string
Variables []*VariableMeta
}
func NewVariablesWithAgentVariables(vars []*bot_common.Variable) *VariablesMeta {
res := make([]*VariableMeta, 0)
for _, variable := range vars {
res = append(res, agentVariableMetaToProjectVariableMeta(variable))
}
return &VariablesMeta{
Variables: res,
}
}
func NewVariables(vars []*project_memory.Variable) *VariablesMeta {
res := make([]*VariableMeta, 0)
for _, variable := range vars {
res = append(res, &VariableMeta{
Keyword: variable.Keyword,
DefaultValue: variable.DefaultValue,
VariableType: variable.VariableType,
Channel: variable.Channel,
Description: variable.Description,
Enable: variable.Enable,
EffectiveChannelList: variable.EffectiveChannelList,
Schema: variable.Schema,
IsReadOnly: variable.IsReadOnly,
})
}
return &VariablesMeta{
Variables: res,
}
}
func (v *VariablesMeta) ToAgentVariables() []*bot_common.Variable {
res := make([]*bot_common.Variable, 0, len(v.Variables))
for idx := range v.Variables {
v := v.Variables[idx]
isSystem := v.Channel == project_memory.VariableChannel_System
isDisabled := !v.Enable
agentVariable := &bot_common.Variable{
Key: &v.Keyword,
DefaultValue: &v.DefaultValue,
Description: &v.Description,
IsDisabled: &isDisabled,
IsSystem: &isSystem,
PromptDisabled: &v.PromptDisabled,
}
res = append(res, agentVariable)
}
return res
}
func (v *VariablesMeta) ToProjectVariables() []*project_memory.Variable {
res := make([]*project_memory.Variable, 0, len(v.Variables))
for _, v := range v.Variables {
res = append(res, v.ToProjectVariable())
}
return res
}
func (v *VariablesMeta) SetupIsReadOnly() {
for _, variable := range v.Variables {
variable.SetupIsReadOnly()
}
}
func (v *VariablesMeta) SetupSchema() {
for _, variable := range v.Variables {
variable.SetupSchema()
}
}
func agentVariableMetaToProjectVariableMeta(variable *bot_common.Variable) *VariableMeta {
temp := &VariableMeta{
Keyword: variable.GetKey(),
DefaultValue: variable.GetDefaultValue(),
VariableType: project_memory.VariableType_KVVariable,
Description: variable.GetDescription(),
Enable: !variable.GetIsDisabled(),
Schema: fmt.Sprintf(stringSchema, variable.GetKey()),
PromptDisabled: variable.GetPromptDisabled(),
}
if variable.GetIsSystem() {
temp.IsReadOnly = true
temp.Channel = project_memory.VariableChannel_System
} else {
temp.Channel = project_memory.VariableChannel_Custom
}
return temp
}
func (v *VariablesMeta) GroupByChannel() map[project_memory.VariableChannel][]*project_memory.Variable {
res := make(map[project_memory.VariableChannel][]*project_memory.Variable)
for _, variable := range v.Variables {
ch := variable.Channel
res[ch] = append(res[ch], variable.ToProjectVariable())
}
return res
}
func (v *VariablesMeta) RemoveDisableVariable() {
var res []*VariableMeta
for _, vv := range v.Variables {
if vv.Enable {
res = append(res, vv)
}
}
v.Variables = res
}
func (v *VariablesMeta) FilterChannelVariable(ch project_memory.VariableChannel) {
var res []*VariableMeta
for _, vv := range v.Variables {
if vv.Channel != ch {
continue
}
res = append(res, vv)
}
v.Variables = res
}