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,63 @@
/*
* 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 ctxcache
import (
"context"
"sync"
)
type ctxCacheKey struct{}
func Init(ctx context.Context) context.Context {
return context.WithValue(ctx, ctxCacheKey{}, new(sync.Map))
}
func Get[T any](ctx context.Context, key any) (value T, ok bool) {
var zero T
cacheMap, valid := ctx.Value(ctxCacheKey{}).(*sync.Map)
if !valid {
return zero, false
}
loadedValue, exists := cacheMap.Load(key)
if !exists {
return zero, false
}
if v, match := loadedValue.(T); match {
return v, true
}
return zero, false
}
func Store(ctx context.Context, key any, obj any) {
if cacheMap, ok := ctx.Value(ctxCacheKey{}).(*sync.Map); ok {
cacheMap.Store(key, obj)
}
}
func HasKey(ctx context.Context, key any) bool {
if cacheMap, ok := ctx.Value(ctxCacheKey{}).(*sync.Map); ok {
_, ok := cacheMap.Load(key)
return ok
}
return false
}

View File

@@ -0,0 +1,73 @@
/*
* 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 ctxcache
import (
"context"
"reflect"
"testing"
. "github.com/onsi/gomega"
)
func TestCtxCache(t *testing.T) {
g := NewGomegaWithT(t)
ctx := context.Background()
// 测试没有 initCtxCacheData 场景
Store(ctx, "test1", "1")
_, ok := Get[string](ctx, "test1")
g.Expect(ok).Should(BeFalse())
// 有 initCtxCacheData 场景
ctx = Init(ctx)
_, ok = Get[string](ctx, "test")
g.Expect(ok).Should(BeFalse())
Store(ctx, "key1", "abc")
data1, ok := Get[string](ctx, "key1")
g.Expect(ok).Should(BeTrue())
g.Expect(data1).Should(BeIdenticalTo("abc"))
type testKey struct{}
Store(ctx, testKey{}, int64(1))
data2, ok := Get[int64](ctx, testKey{})
g.Expect(ok).Should(BeTrue())
g.Expect(data2).Should(BeIdenticalTo(int64(1)))
type temp struct {
a string
b string
c int64
d []int64
}
te := temp{
a: "1",
b: "2",
c: 3,
d: []int64{123, 1232, 232},
}
Store(ctx, "temp", te)
newT, ok := Get[temp](ctx, "temp")
g.Expect(ok).Should(BeTrue())
g.Expect(reflect.DeepEqual(te, newT)).Should(BeTrue())
}

View File

@@ -0,0 +1,38 @@
/*
* 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 code
import (
"github.com/coze-dev/coze-studio/backend/pkg/errorx/internal"
)
type RegisterOptionFn = internal.RegisterOption
// WithAffectStability 设置稳定性标识, true:会影响系统稳定性, 并体现在接口错误率中, false:不影响稳定性.
func WithAffectStability(affectStability bool) RegisterOptionFn {
return internal.WithAffectStability(affectStability)
}
// Register 注册用户预定义的错误码信息, PSM服务对应的code_gen子module初始化时调用.
func Register(code int32, msg string, opts ...RegisterOptionFn) {
internal.Register(code, msg, opts...)
}
// SetDefaultErrorCode 带有PSM信息染色的code替换默认code.
func SetDefaultErrorCode(code int32) {
internal.SetDefaultErrorCode(code)
}

View File

@@ -0,0 +1,90 @@
/*
* 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 errorx
import (
"fmt"
"strings"
"github.com/coze-dev/coze-studio/backend/pkg/errorx/internal"
)
// StatusError is an interface for error with status code, you can
// create an error through New or WrapByCode and convert it back to
// StatusError through FromStatusError to obtain information such as
// error status code.
type StatusError interface {
error
Code() int32
Msg() string
IsAffectStability() bool
Extra() map[string]string
}
// Option is used to configure an StatusError.
type Option = internal.Option
func KV(k, v string) Option {
return internal.Param(k, v)
}
func KVf(k, v string, a ...any) Option {
formatValue := fmt.Sprintf(v, a...)
return internal.Param(k, formatValue)
}
func Extra(k, v string) Option {
return internal.Extra(k, v)
}
// New get an error predefined in the configuration file by statusCode
// with a stack trace at the point New is called.
func New(code int32, options ...Option) error {
return internal.NewByCode(code, options...)
}
// WrapByCode returns an error annotating err with a stack trace
// at the point WrapByCode is called, and the status code.
func WrapByCode(err error, statusCode int32, options ...Option) error {
if err == nil {
return nil
}
return internal.WrapByCode(err, statusCode, options...)
}
// Wrapf returns an error annotating err with a stack trace
// at the point Wrapf is called, and the format specifier.
func Wrapf(err error, format string, args ...interface{}) error {
if err == nil {
return nil
}
return internal.Wrapf(err, format, args...)
}
func ErrorWithoutStack(err error) string {
if err == nil {
return ""
}
errMsg := err.Error()
index := strings.Index(errMsg, "stack=")
if index != -1 {
errMsg = errMsg[:index]
}
return errMsg
}

View File

@@ -0,0 +1,53 @@
/*
* 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 errorx
import (
"errors"
"fmt"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/coze-dev/coze-studio/backend/pkg/errorx/code"
)
var (
ErrPermissionCode = int32(1000000)
errPermissionMessage = "unauthorized access : {msg}"
errPermissionAffectStability = false
)
func TestError(t *testing.T) {
code.Register(
ErrPermissionCode,
errPermissionMessage,
code.WithAffectStability(errPermissionAffectStability),
)
err := New(ErrPermissionCode, KV("msg", "test"))
fmt.Println(err)
fmt.Println(err.Error())
fmt.Println(err)
var customErr StatusError
b := errors.As(err, &customErr)
assert.Equal(t, b, true)
assert.Equal(t, customErr.Code(), ErrPermissionCode)
assert.Equal(t, customErr.Msg(), strings.Replace(errPermissionMessage, "{msg}", "test", 1))
}

View File

@@ -0,0 +1,50 @@
/*
* 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 internal
import (
"fmt"
)
type withMessage struct {
cause error
msg string
}
func (w *withMessage) Unwrap() error {
return w.cause
}
func (w *withMessage) Error() string {
return fmt.Sprintf("%s\ncause=%s", w.msg, w.cause.Error())
}
func wrapf(err error, format string, args ...interface{}) error {
if err == nil {
return nil
}
err = &withMessage{
cause: err,
msg: fmt.Sprintf(format, args...),
}
return err
}
func Wrapf(err error, format string, args ...interface{}) error {
return withStackTraceIfNotExists(wrapf(err, format, args...))
}

View File

@@ -0,0 +1,59 @@
/*
* 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 internal
const (
DefaultErrorMsg = "Service Internal Error"
DefaultIsAffectStability = true
)
var (
ServiceInternalErrorCode int32 = 1
CodeDefinitions = make(map[int32]*CodeDefinition)
)
type CodeDefinition struct {
Code int32
Message string
IsAffectStability bool
}
type RegisterOption func(definition *CodeDefinition)
func WithAffectStability(affectStability bool) RegisterOption {
return func(definition *CodeDefinition) {
definition.IsAffectStability = affectStability
}
}
func Register(code int32, msg string, opts ...RegisterOption) {
definition := &CodeDefinition{
Code: code,
Message: msg,
IsAffectStability: DefaultIsAffectStability,
}
for _, opt := range opts {
opt(definition)
}
CodeDefinitions[code] = definition
}
func SetDefaultErrorCode(code int32) {
ServiceInternalErrorCode = code
}

View File

@@ -0,0 +1,86 @@
/*
* 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 internal
import (
"errors"
"fmt"
"runtime"
"strings"
)
type StackTracer interface {
StackTrace() string
}
type withStack struct {
cause error
stack string
}
func (w *withStack) Unwrap() error {
return w.cause
}
func (w *withStack) StackTrace() string {
return w.stack
}
func (w *withStack) Error() string {
return fmt.Sprintf("%s\nstack=%s", w.cause.Error(), w.stack)
}
func stack() string {
const depth = 32
var pcs [depth]uintptr
n := runtime.Callers(2, pcs[:])
b := strings.Builder{}
for i := 0; i < n; i++ {
fn := runtime.FuncForPC(pcs[i])
file, line := fn.FileLine(pcs[i])
name := trimPathPrefix(fn.Name())
b.WriteString(fmt.Sprintf("%s:%d %s\n", file, line, name))
}
return b.String()
}
func trimPathPrefix(s string) string {
i := strings.LastIndex(s, "/")
s = s[i+1:]
i = strings.Index(s, ".")
return s[i+1:]
}
func withStackTraceIfNotExists(err error) error {
if err == nil {
return nil
}
// skip if stack has already exist
var stackTracer StackTracer
if errors.As(err, &stackTracer) {
return err
}
return &withStack{
err,
stack(),
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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 internal
import (
"errors"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestWithStack(t *testing.T) {
err := withStackTraceIfNotExists(errors.New("test error"))
output1 := fmt.Sprintf("%+v", err)
assert.Contains(t, output1, "stack_test.go")
assert.Contains(t, output1, "withStackTraceIfNotExists")
t.Log(output1)
}
func TestPrintStack(t *testing.T) {
t.Run("New with stack", func(t *testing.T) {
err := NewByCode(1)
output1 := fmt.Sprintf("%v", err)
assert.Contains(t, output1, "stack_test.go")
assert.Contains(t, output1, "TestPrintStack")
t.Log(output1)
})
t.Run("New with stack and wrap with fmt.Errorf", func(t *testing.T) {
err := NewByCode(1)
err1 := fmt.Errorf("err=%w", err)
output1 := fmt.Sprintf("%v", err1)
assert.Contains(t, output1, "stack_test.go")
assert.Contains(t, output1, "TestPrintStack")
t.Log(output1)
})
t.Run("wrapf with stack", func(t *testing.T) {
err := errors.New("original error")
err1 := Wrapf(err, "wrapped error")
output1 := fmt.Sprintf("%v", err1)
assert.Contains(t, output1, "stack_test.go")
assert.Contains(t, output1, "TestPrintStack")
t.Log(output1)
})
t.Run("skip wrap with stack if stack has already exist", func(t *testing.T) {
err := NewByCode(1)
err1 := fmt.Errorf("err1=%w", err)
err2 := withStackTraceIfNotExists(err1)
_, ok := err2.(StackTracer)
assert.False(t, ok)
output1 := fmt.Sprintf("%v", err2)
assert.Contains(t, output1, "stack_test.go")
assert.Contains(t, output1, "TestPrintStack")
t.Log(output1)
})
}

View File

@@ -0,0 +1,195 @@
/*
* 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 internal
import (
"errors"
"fmt"
"strings"
)
type StatusError interface {
error
Code() int32
}
type statusError struct {
statusCode int32
message string
ext Extension
}
type withStatus struct {
status *statusError
stack string
cause error
}
type Extension struct {
IsAffectStability bool
Extra map[string]string
}
func (w *statusError) Code() int32 {
return w.statusCode
}
func (w *statusError) IsAffectStability() bool {
return w.ext.IsAffectStability
}
func (w *statusError) Msg() string {
return w.message
}
func (w *statusError) Error() string {
return fmt.Sprintf("code=%d message=%s", w.statusCode, w.message)
}
func (w *statusError) Extra() map[string]string {
return w.ext.Extra
}
// Unwrap supports go errors.Unwrap().
func (w *withStatus) Unwrap() error {
return w.cause
}
// Is supports go errors.Is().
func (w *withStatus) Is(target error) bool {
var ws StatusError
if errors.As(target, &ws) && w.status.Code() == ws.Code() {
return true
}
return false
}
// As supports go errors.As().
func (w *withStatus) As(target interface{}) bool {
if errors.As(w.status, target) {
return true
}
return false
}
func (w *withStatus) StackTrace() string {
return w.stack
}
func (w *withStatus) Error() string {
b := strings.Builder{}
b.WriteString(w.status.Error())
if w.cause != nil {
b.WriteString("\n")
b.WriteString(fmt.Sprintf("cause=%s", w.cause))
}
if w.stack != "" {
b.WriteString("\n")
b.WriteString(fmt.Sprintf("stack=%s", w.stack))
}
return b.String()
}
type Option func(ws *withStatus)
func Param(k, v string) Option {
return func(ws *withStatus) {
if ws == nil || ws.status == nil {
return
}
ws.status.message = strings.Replace(ws.status.message, fmt.Sprintf("{%s}", k), v, -1)
}
}
func Extra(k, v string) Option {
return func(ws *withStatus) {
if ws == nil || ws.status == nil {
return
}
if ws.status.ext.Extra == nil {
ws.status.ext.Extra = make(map[string]string)
}
ws.status.ext.Extra[k] = v
}
}
func NewByCode(code int32, options ...Option) error {
ws := &withStatus{
status: getStatusByCode(code),
cause: nil,
stack: stack(),
}
for _, opt := range options {
opt(ws)
}
return ws
}
func WrapByCode(err error, code int32, options ...Option) error {
if err == nil {
return nil
}
ws := &withStatus{
status: getStatusByCode(code),
cause: err,
}
for _, opt := range options {
opt(ws)
}
// skip if stack has already exist
var stackTracer StackTracer
if errors.As(err, &stackTracer) {
return ws
}
ws.stack = stack()
return ws
}
func getStatusByCode(code int32) *statusError {
codeDefinition, ok := CodeDefinitions[code]
if ok {
// predefined err code
return &statusError{
statusCode: code,
message: codeDefinition.Message,
ext: Extension{
IsAffectStability: codeDefinition.IsAffectStability,
},
}
}
return &statusError{
statusCode: code,
message: DefaultErrorMsg,
ext: Extension{
IsAffectStability: DefaultIsAffectStability,
},
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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 goutil
import (
"context"
"fmt"
"runtime/debug"
"github.com/coze-dev/coze-studio/backend/pkg/logs"
)
func Recovery(ctx context.Context) {
e := recover()
if e == nil {
return
}
if ctx == nil {
ctx = context.Background() // nolint: byted_context_not_reinitialize -- false positive
}
err := fmt.Errorf("%v", e)
logs.CtxErrorf(ctx, fmt.Sprintf("[catch panic] err = %v \n stacktrace:\n%s", err, debug.Stack()))
}

View File

@@ -0,0 +1,44 @@
/*
* 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 goutil
import (
"os"
"path/filepath"
"github.com/coze-dev/coze-studio/backend/pkg/logs"
)
func GetPythonFilePath(fileName string) string {
cwd, err := os.Getwd()
if err != nil {
logs.Warnf("[GetPythonFilePath] Failed to get current working directory: %v", err)
return fileName
}
return filepath.Join(cwd, fileName)
}
func GetPython3Path() string {
cwd, err := os.Getwd()
if err != nil {
logs.Warnf("[GetPython3Path] Failed to get current working directory: %v", err)
return ".venv/bin/python3"
}
return filepath.Join(cwd, ".venv/bin/python3")
}

View File

@@ -0,0 +1,45 @@
/*
* 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 domain
import (
"net/url"
"github.com/cloudwego/hertz/pkg/app"
)
const (
HeaderKeyOfOrigin = "Origin"
HeaderKeyOfHost = "Host"
)
func GetOriginHost(c *app.RequestContext) string {
origin := c.Request.Header.Get(HeaderKeyOfOrigin)
if origin != "" {
u, err := url.Parse(origin)
if err == nil {
return u.Hostname()
}
}
host := c.Request.Header.Get(HeaderKeyOfHost)
if host != "" {
return host
}
return string(c.Request.URI().Host())
}

50
backend/pkg/i18n/i18n.go Normal file
View File

@@ -0,0 +1,50 @@
/*
* 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 i18n
import (
"context"
)
type Locale string
const (
LocaleEN Locale = "en-US"
LocaleZH Locale = "zh-CN"
)
const key = "i18n.locale.key"
func SetLocale(ctx context.Context, locale string) context.Context {
return context.WithValue(ctx, key, locale)
}
func GetLocale(ctx context.Context) Locale {
locale := ctx.Value(key)
if locale == nil {
return LocaleEN
}
switch locale.(string) {
case "en-US":
return LocaleEN
case "zh-CN":
return LocaleZH
default:
return LocaleEN
}
}

View File

@@ -0,0 +1,81 @@
/*
* 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 jsoncache
import (
"context"
"encoding/json"
"fmt"
"github.com/redis/go-redis/v9"
)
type JsonCache[T any] struct {
cache *redis.Client
prefix string
}
func New[T any](prefix string, cache *redis.Client) *JsonCache[T] {
return &JsonCache[T]{
prefix: prefix,
cache: cache,
}
}
func (g *JsonCache[T]) Save(ctx context.Context, k string, v *T) error {
if v == nil {
return fmt.Errorf("cannot save nil value for key: %s", k)
}
data, err := json.Marshal(v)
if err != nil {
return fmt.Errorf("marshal failed for type %T: %w", *v, err)
}
key := g.prefix + k
if err := g.cache.Set(ctx, key, data, 0).Err(); err != nil {
return fmt.Errorf("redis set failed for key %s: %w", k, err)
}
return nil
}
// Get returns default T if key not found
func (g *JsonCache[T]) Get(ctx context.Context, k string) (*T, error) {
key := g.prefix + k
var obj T
data, err := g.cache.Get(ctx, key).Result()
if err == redis.Nil {
return &obj, nil
}
if err != nil {
return nil, fmt.Errorf("failed to get key %s: %w", k, err)
}
if err := json.Unmarshal([]byte(data), &obj); err != nil {
return nil, fmt.Errorf("failed to unmarshal json for key %s: %w", k, err)
}
return &obj, nil
}
func (g *JsonCache[T]) Delete(ctx context.Context, k string) error {
if err := g.cache.Del(ctx, k).Err(); err != nil {
return fmt.Errorf("failed to delete key %s: %w", k, err)
}
return nil
}

View File

@@ -0,0 +1,74 @@
/*
* 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 conv
import (
"encoding/json"
"strconv"
"github.com/coze-dev/coze-studio/backend/pkg/lang/ptr"
)
// StrToInt64E returns strconv.ParseInt(v, 10, 64)
func StrToInt64(v string) (int64, error) {
return strconv.ParseInt(v, 10, 64)
}
// Int64ToStr returns strconv.FormatInt(v, 10) result
func Int64ToStr(v int64) string {
return strconv.FormatInt(v, 10)
}
// StrToInt64 returns strconv.ParseInt(v, 10, 64)'s value.
// if error occurs, returns defaultValue as result.
func StrToInt64D(v string, defaultValue int64) int64 {
toV, err := strconv.ParseInt(v, 10, 64)
if err != nil {
return defaultValue
}
return toV
}
// DebugJsonToStr
func DebugJsonToStr(v interface{}) string {
b, err := json.Marshal(v)
if err != nil {
return ""
}
return string(b)
}
func BoolToInt(p bool) int {
if p == true {
return 1
}
return 0
}
// BoolToIntPointer returns 1 or 0 as pointer
func BoolToIntPointer(p *bool) *int {
if p == nil {
return nil
}
if *p == true {
return ptr.Of(int(1))
}
return ptr.Of(int(0))
}

View File

@@ -0,0 +1,11 @@
package crypto
import (
"crypto/md5"
"encoding/hex"
)
func MD5HexValue(input string) string {
md5Hash := md5.Sum([]byte(input))
return hex.EncodeToString(md5Hash[:])
}

View File

@@ -0,0 +1,34 @@
/*
* 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 maps
func ToAnyValue[K comparable, V any](m map[K]V) map[K]any {
n := make(map[K]any, len(m))
for k, v := range m {
n[k] = v
}
return n
}
func TransformKey[K1, K2 comparable, V any](m map[K1]V, f func(K1) K2) map[K2]V {
n := make(map[K2]V, len(m))
for k1, v := range m {
n[f(k1)] = v
}
return n
}

View File

@@ -0,0 +1,36 @@
/*
* 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 ptr
func Of[T any](t T) *T {
return &t
}
func From[T any](p *T) T {
if p != nil {
return *p
}
var t T
return t
}
func FromOrDefault[T any](p *T, def T) T {
if p != nil {
return *p
}
return def
}

View File

@@ -0,0 +1,42 @@
/*
* 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 sets
type Set[T comparable] map[T]struct{}
func FromSlice[T comparable](s []T) Set[T] {
set := make(Set[T], len(s))
for _, elem := range s {
cpElem := elem
set[cpElem] = struct{}{}
}
return set
}
func (s Set[T]) ToSlice() []T {
sl := make([]T, 0, len(s))
for elem := range s {
cpElem := elem
sl = append(sl, cpElem)
}
return sl
}
func (s Set[T]) Contains(elem T) bool {
_, ok := s[elem]
return ok
}

View File

@@ -0,0 +1,29 @@
/*
* 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 signal
import (
"os"
"os/signal"
"syscall"
)
func WaitExit() {
signals := make(chan os.Signal, 1)
signal.Notify(signals, syscall.SIGINT, syscall.SIGHUP, syscall.SIGTERM)
<-signals
}

View File

@@ -0,0 +1,125 @@
/*
* 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 slices
func Transform[A, B any](src []A, fn func(A) B) []B {
if src == nil {
return nil
}
dst := make([]B, 0, len(src))
for _, a := range src {
dst = append(dst, fn(a))
}
return dst
}
func TransformWithErrorCheck[A, B any](src []A, fn func(A) (B, error)) ([]B, error) {
if src == nil {
return nil, nil
}
dst := make([]B, 0, len(src))
for _, a := range src {
item, err := fn(a)
if err != nil {
return nil, err
}
dst = append(dst, item)
}
return dst, nil
}
func GroupBy[A, K comparable, V any](src []A, fn func(A) (K, V)) map[K][]V {
if src == nil {
return nil
}
dst := make(map[K][]V, len(src))
for _, a := range src {
k, v := fn(a)
dst[k] = append(dst[k], v)
}
return dst
}
func Unique[T comparable](src []T) []T {
if src == nil {
return nil
}
dst := make([]T, 0, len(src))
m := make(map[T]struct{}, len(src))
for _, s := range src {
if _, ok := m[s]; ok {
continue
}
dst = append(dst, s)
m[s] = struct{}{}
}
return dst
}
func Fill[T any](val T, size int) []T {
slice := make([]T, size)
for i := 0; i < size; i++ {
slice[i] = val
}
return slice
}
func Chunks[T any](s []T, chunkSize int) [][]T {
sliceLen := len(s)
chunks := make([][]T, 0, sliceLen/chunkSize)
for start := 0; start < sliceLen; start += chunkSize {
end := start + chunkSize
if end > sliceLen {
end = sliceLen
}
chunks = append(chunks, s[start:end])
}
return chunks
}
func ToMap[E any, K comparable, V any](src []E, fn func(e E) (K, V)) map[K]V {
if src == nil {
return nil
}
dst := make(map[K]V, len(src))
for _, e := range src {
k, v := fn(e)
dst[k] = v
}
return dst
}
func Reverse[T any](slice []T) []T {
left := 0
right := len(slice) - 1
for left < right {
slice[left], slice[right] = slice[right], slice[left]
left++
right--
}
return slice
}

View File

@@ -0,0 +1,33 @@
/*
* 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 sqlutil
import (
"database/sql/driver"
)
func DriverValue[T any](v T) driver.Valuer {
return value[T]{val: v}
}
type value[T any] struct {
val T
}
func (i value[T]) Value() (driver.Value, error) {
return i.val, nil
}

View File

@@ -0,0 +1,24 @@
/*
* 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 ternary
func IFElse[T any](ok bool, trueValue, falseValue T) T {
if ok {
return trueValue
}
return falseValue
}

292
backend/pkg/logs/default.go Normal file
View File

@@ -0,0 +1,292 @@
/*
* 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 logs
import (
"context"
"fmt"
"io"
"log"
"os"
)
var logger FullLogger = &defaultLogger{
level: LevelInfo,
stdlog: log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile|log.Lmicroseconds),
}
// SetOutput sets the output of default logs. By default, it is stderr.
func SetOutput(w io.Writer) {
logger.SetOutput(w)
}
// SetLevel sets the level of logs below which logs will not be output.
// The default log level is LevelTrace.
// Note that this method is not concurrent-safe.
func SetLevel(lv Level) {
logger.SetLevel(lv)
}
// DefaultLogger return the default logs for kitex.
func DefaultLogger() FullLogger {
return logger
}
// SetLogger sets the default logs.
// Note that this method is not concurrent-safe and must not be called
// after the use of DefaultLogger and global functions in this package.
func SetLogger(v FullLogger) {
logger = v
}
// Fatal calls the default logs's Fatal method and then os.Exit(1).
func Fatal(v ...interface{}) {
logger.Fatal(v...)
}
// Error calls the default logs's Error method.
func Error(v ...interface{}) {
logger.Error(v...)
}
// Warn calls the default logs's Warn method.
func Warn(v ...interface{}) {
logger.Warn(v...)
}
// Notice calls the default logs's Notice method.
func Notice(v ...interface{}) {
logger.Notice(v...)
}
// Info calls the default logs's Info method.
func Info(v ...interface{}) {
logger.Info(v...)
}
// Debug calls the default logs's Debug method.
func Debug(v ...interface{}) {
logger.Debug(v...)
}
// Trace calls the default logs's Trace method.
func Trace(v ...interface{}) {
logger.Trace(v...)
}
// Fatalf calls the default logs's Fatalf method and then os.Exit(1).
func Fatalf(format string, v ...interface{}) {
logger.Fatalf(format, v...)
}
// Errorf calls the default logs's Errorf method.
func Errorf(format string, v ...interface{}) {
logger.Errorf(format, v...)
}
// Warnf calls the default logs's Warnf method.
func Warnf(format string, v ...interface{}) {
logger.Warnf(format, v...)
}
// Noticef calls the default logs's Noticef method.
func Noticef(format string, v ...interface{}) {
logger.Noticef(format, v...)
}
// Infof calls the default logs's Infof method.
func Infof(format string, v ...interface{}) {
logger.Infof(format, v...)
}
// Debugf calls the default logs's Debugf method.
func Debugf(format string, v ...interface{}) {
logger.Debugf(format, v...)
}
// Tracef calls the default logs's Tracef method.
func Tracef(format string, v ...interface{}) {
logger.Tracef(format, v...)
}
// CtxFatalf calls the default logs's CtxFatalf method and then os.Exit(1).
func CtxFatalf(ctx context.Context, format string, v ...interface{}) {
logger.CtxFatalf(ctx, format, v...)
}
// CtxErrorf calls the default logs's CtxErrorf method.
func CtxErrorf(ctx context.Context, format string, v ...interface{}) {
logger.CtxErrorf(ctx, format, v...)
}
// CtxWarnf calls the default logs's CtxWarnf method.
func CtxWarnf(ctx context.Context, format string, v ...interface{}) {
logger.CtxWarnf(ctx, format, v...)
}
// CtxNoticef calls the default logs's CtxNoticef method.
func CtxNoticef(ctx context.Context, format string, v ...interface{}) {
logger.CtxNoticef(ctx, format, v...)
}
// CtxInfof calls the default logs's CtxInfof method.
func CtxInfof(ctx context.Context, format string, v ...interface{}) {
logger.CtxInfof(ctx, format, v...)
}
// CtxDebugf calls the default logs's CtxDebugf method.
func CtxDebugf(ctx context.Context, format string, v ...interface{}) {
logger.CtxDebugf(ctx, format, v...)
}
// CtxTracef calls the default logs's CtxTracef method.
func CtxTracef(ctx context.Context, format string, v ...interface{}) {
logger.CtxTracef(ctx, format, v...)
}
type defaultLogger struct {
stdlog *log.Logger
level Level
}
func (ll *defaultLogger) SetOutput(w io.Writer) {
ll.stdlog.SetOutput(w)
}
func (ll *defaultLogger) SetLevel(lv Level) {
ll.level = lv
}
func (ll *defaultLogger) logf(lv Level, format *string, v ...interface{}) {
if ll.level > lv {
return
}
msg := lv.toString()
if format != nil {
msg += fmt.Sprintf(*format, v...)
} else {
msg += fmt.Sprint(v...)
}
ll.stdlog.Output(4, msg)
if lv == LevelFatal {
os.Exit(1)
}
}
func (ll *defaultLogger) logfCtx(ctx context.Context, lv Level, format *string, v ...interface{}) {
if ll.level > lv {
return
}
msg := lv.toString()
logID := ctx.Value("log-id")
if logID != nil {
msg += fmt.Sprintf("[log-id: %v] ", logID)
}
if format != nil {
msg += fmt.Sprintf(*format, v...)
} else {
msg += fmt.Sprint(v...)
}
ll.stdlog.Output(4, msg)
if lv == LevelFatal {
os.Exit(1)
}
}
func (ll *defaultLogger) Fatal(v ...interface{}) {
ll.logf(LevelFatal, nil, v...)
}
func (ll *defaultLogger) Error(v ...interface{}) {
ll.logf(LevelError, nil, v...)
}
func (ll *defaultLogger) Warn(v ...interface{}) {
ll.logf(LevelWarn, nil, v...)
}
func (ll *defaultLogger) Notice(v ...interface{}) {
ll.logf(LevelNotice, nil, v...)
}
func (ll *defaultLogger) Info(v ...interface{}) {
ll.logf(LevelInfo, nil, v...)
}
func (ll *defaultLogger) Debug(v ...interface{}) {
ll.logf(LevelDebug, nil, v...)
}
func (ll *defaultLogger) Trace(v ...interface{}) {
ll.logf(LevelTrace, nil, v...)
}
func (ll *defaultLogger) Fatalf(format string, v ...interface{}) {
ll.logf(LevelFatal, &format, v...)
}
func (ll *defaultLogger) Errorf(format string, v ...interface{}) {
ll.logf(LevelError, &format, v...)
}
func (ll *defaultLogger) Warnf(format string, v ...interface{}) {
ll.logf(LevelWarn, &format, v...)
}
func (ll *defaultLogger) Noticef(format string, v ...interface{}) {
ll.logf(LevelNotice, &format, v...)
}
func (ll *defaultLogger) Infof(format string, v ...interface{}) {
ll.logf(LevelInfo, &format, v...)
}
func (ll *defaultLogger) Debugf(format string, v ...interface{}) {
ll.logf(LevelDebug, &format, v...)
}
func (ll *defaultLogger) Tracef(format string, v ...interface{}) {
ll.logf(LevelTrace, &format, v...)
}
func (ll *defaultLogger) CtxFatalf(ctx context.Context, format string, v ...interface{}) {
ll.logfCtx(ctx, LevelFatal, &format, v...)
}
func (ll *defaultLogger) CtxErrorf(ctx context.Context, format string, v ...interface{}) {
ll.logfCtx(ctx, LevelError, &format, v...)
}
func (ll *defaultLogger) CtxWarnf(ctx context.Context, format string, v ...interface{}) {
ll.logfCtx(ctx, LevelWarn, &format, v...)
}
func (ll *defaultLogger) CtxNoticef(ctx context.Context, format string, v ...interface{}) {
ll.logfCtx(ctx, LevelNotice, &format, v...)
}
func (ll *defaultLogger) CtxInfof(ctx context.Context, format string, v ...interface{}) {
ll.logfCtx(ctx, LevelInfo, &format, v...)
}
func (ll *defaultLogger) CtxDebugf(ctx context.Context, format string, v ...interface{}) {
ll.logfCtx(ctx, LevelDebug, &format, v...)
}
func (ll *defaultLogger) CtxTracef(ctx context.Context, format string, v ...interface{}) {
ll.logfCtx(ctx, LevelTrace, &format, v...)
}

104
backend/pkg/logs/logger.go Normal file
View File

@@ -0,0 +1,104 @@
/*
* 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 logs
import (
"context"
"fmt"
"io"
)
// FormatLogger is a logs interface that output logs with a format.
type FormatLogger interface {
Tracef(format string, v ...interface{})
Debugf(format string, v ...interface{})
Infof(format string, v ...interface{})
Noticef(format string, v ...interface{})
Warnf(format string, v ...interface{})
Errorf(format string, v ...interface{})
Fatalf(format string, v ...interface{})
}
// Logger is a logs interface that provides logging function with levels.
type Logger interface {
Trace(v ...interface{})
Debug(v ...interface{})
Info(v ...interface{})
Notice(v ...interface{})
Warn(v ...interface{})
Error(v ...interface{})
Fatal(v ...interface{})
}
// CtxLogger is a logs interface that accepts a context argument and output
// logs with a format.
type CtxLogger interface {
CtxTracef(ctx context.Context, format string, v ...interface{})
CtxDebugf(ctx context.Context, format string, v ...interface{})
CtxInfof(ctx context.Context, format string, v ...interface{})
CtxNoticef(ctx context.Context, format string, v ...interface{})
CtxWarnf(ctx context.Context, format string, v ...interface{})
CtxErrorf(ctx context.Context, format string, v ...interface{})
CtxFatalf(ctx context.Context, format string, v ...interface{})
}
// Control provides methods to config a logs.
type Control interface {
SetLevel(Level)
SetOutput(io.Writer)
}
// FullLogger is the combination of Logger, FormatLogger, CtxLogger and Control.
type FullLogger interface {
Logger
FormatLogger
CtxLogger
Control
}
// Level defines the priority of a log message.
// When a logs is configured with a level, any log message with a lower
// log level (smaller by integer comparison) will not be output.
type Level int
// The levels of logs.
const (
LevelTrace Level = iota
LevelDebug
LevelInfo
LevelNotice
LevelWarn
LevelError
LevelFatal
)
var strs = []string{
"[Trace] ",
"[Debug] ",
"[Info] ",
"[Notice] ",
"[Warn] ",
"[Error] ",
"[Fatal] ",
}
func (lv Level) toString() string {
if lv >= LevelTrace && lv <= LevelFatal {
return strs[lv]
}
return fmt.Sprintf("[?%d] ", lv)
}

View File

@@ -0,0 +1,40 @@
/*
* 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 safego
import (
"fmt"
)
type panicErr struct {
info any
stack []byte
}
func (p *panicErr) Error() string {
return fmt.Sprintf("panic error: %v, \nstack: %s", p.info, string(p.stack))
}
// NewPanicErr creates a new panic error.
// panicErr is a wrapper of panic info and stack trace.
// it implements the error interface, can print error message of info and stack trace.
func NewPanicErr(info any, stack []byte) error {
return &panicErr{
info: info,
stack: stack,
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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 safego
import (
"context"
"github.com/coze-dev/coze-studio/backend/pkg/goutil"
)
func Go(ctx context.Context, fn func()) {
go func() {
defer goutil.Recovery(ctx)
fn()
}()
}

View File

@@ -0,0 +1,52 @@
/*
* 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 sonic
import "github.com/bytedance/sonic"
var config = sonic.Config{
UseInt64: true,
}.Froze()
// Marshal returns the JSON encoding bytes of v.
func Marshal(val interface{}) ([]byte, error) {
return config.Marshal(val)
}
// MarshalIndent is like Marshal but applies Indent to format the output.
// Each JSON element in the output will begin on a new line beginning with prefix
// followed by one or more copies of indent according to the indentation nesting.
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
return config.MarshalIndent(v, prefix, indent)
}
// MarshalString returns the JSON encoding string of v.
func MarshalString(val interface{}) (string, error) {
return config.MarshalToString(val)
}
// Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v.
// NOTICE: This API copies given buffer by default,
// if you want to pass JSON more efficiently, use UnmarshalString instead.
func Unmarshal(buf []byte, val interface{}) error {
return config.Unmarshal(buf, val)
}
// UnmarshalString is like Unmarshal, except buf is a string.
func UnmarshalString(buf string, val interface{}) error {
return config.UnmarshalFromString(buf, val)
}

View File

@@ -0,0 +1,81 @@
/*
* 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 taskgroup
import (
"context"
"sync/atomic"
"golang.org/x/sync/errgroup"
"github.com/coze-dev/coze-studio/backend/pkg/logs"
)
type TaskGroup interface {
Go(f func() error)
Wait() error
}
type taskGroup struct {
errGroup *errgroup.Group
ctx context.Context
execAllTask atomic.Bool
}
// NewTaskGroup if one task return error, the rest task will stop
func NewTaskGroup(ctx context.Context, concurrentCount int) TaskGroup {
t := &taskGroup{}
t.errGroup, t.ctx = errgroup.WithContext(ctx)
t.errGroup.SetLimit(concurrentCount)
t.execAllTask.Store(false)
return t
}
// NewUninterruptibleTaskGroup if one task return error, the rest task will continue
func NewUninterruptibleTaskGroup(ctx context.Context, concurrentCount int) TaskGroup {
t := &taskGroup{}
t.errGroup, t.ctx = errgroup.WithContext(ctx)
t.errGroup.SetLimit(concurrentCount)
t.execAllTask.Store(true)
return t
}
func (t *taskGroup) Go(f func() error) {
t.errGroup.Go(func() error {
defer func() {
if err := recover(); err != nil {
logs.CtxErrorf(t.ctx, "[TaskGroup] exec panic recover:%+v", err)
}
}()
if !t.execAllTask.Load() {
select {
case <-t.ctx.Done():
return t.ctx.Err()
default:
}
}
return f()
})
}
func (t *taskGroup) Wait() error {
return t.errGroup.Wait()
}