config.go•6.14 kB
// Copyright (c) 2024 Baidu, Inc. All Rights Reserved.
//
// 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 appbuilder
import (
"fmt"
"io"
"log"
"net/http"
"net/url"
"os"
"path"
"strings"
"github.com/google/uuid"
)
const (
GatewayURL = "GATEWAY_URL"
GatewayURLV2 = "GATEWAY_URL_V2"
SecretKey = "APPBUILDER_TOKEN"
ConsoleOpenAPIVersion = "CONSOLE_OPENAPI_VERSION"
ConsoleOpenAPIPrefix = "CONSOLE_OPENAPI_PREFIX"
SecretKeyPrefix = "SECRET_KEY_PREFIX"
DatasetID = "DATASET_ID"
SecretKeyV3 = "APPBUILDER_TOKEN_V3"
DatasetIDV3 = "DATASET_ID_V3"
DocumentIDV3 = "DOCUMENT_ID_V3"
DefaultSecretKeyPrefix = "Bearer"
DefaultGatewayURL = "https://appbuilder.baidu.com"
DefaultGatewayURLV2 = "https://qianfan.baidubce.com"
DefaultConsoleOpenAPIVersion = "/v2"
DefaultConsoleOpenAPIPrefix = ""
)
type SDKConfig struct {
GatewayURL string
GatewayURLV2 string
ConsoleOpenAPIVersion string
ConsoleOpenAPIPrefix string
SecretKey string
HTTPClient HTTPClient // custom HTTP Client, optional
logger *log.Logger
}
func NewSDKConfig(gatewayURL, secretKey string) (*SDKConfig, error) {
gatewayURL = getEnvWithDefault(GatewayURL, gatewayURL, DefaultGatewayURL)
gatewayURLV2 := getEnvWithDefault(GatewayURLV2, "", DefaultGatewayURLV2)
openAPIVersion := getEnvWithDefault(ConsoleOpenAPIVersion, "", DefaultConsoleOpenAPIVersion)
openAPIPrefix := getEnvWithDefault(ConsoleOpenAPIPrefix, "", DefaultConsoleOpenAPIPrefix)
secretKey = getEnvWithDefault(SecretKey, secretKey, "")
if len(secretKey) == 0 {
log.Println("Error: secret key is empty")
}
secretKeyPrefix := getEnvWithDefault(SecretKeyPrefix, "", DefaultSecretKeyPrefix)
if !strings.HasPrefix(secretKey, secretKeyPrefix) {
secretKey = secretKeyPrefix + " " + secretKey
}
sdkConfig := &SDKConfig{
GatewayURL: gatewayURL,
GatewayURLV2: gatewayURLV2,
ConsoleOpenAPIVersion: openAPIVersion,
ConsoleOpenAPIPrefix: openAPIPrefix,
SecretKey: secretKey,
}
logFile := os.Getenv("APPBUILDER_LOGFILE")
if len(logFile) > 0 {
f, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
sdkConfig.logger = log.New(os.Stdout, "", log.LstdFlags)
} else {
sdkConfig.logger = log.New(f, "", log.LstdFlags)
}
} else {
sdkConfig.logger = log.New(os.Stdout, "", log.LstdFlags)
}
return sdkConfig, nil
}
func getEnvWithDefault(key, paramValue, defaultValue string) string {
if paramValue != "" {
return paramValue
}
v := os.Getenv(key)
if v == "" {
return defaultValue
}
return v
}
func (t *SDKConfig) AuthHeader() http.Header {
header := t.authHeader()
header.Set("X-Appbuilder-Authorization", t.SecretKey)
t.logger.Printf("Auth Header %v", header)
return header
}
// AuthHeaderV2 适配OpenAPI,当前仅AgentBuilder使用
func (t *SDKConfig) AuthHeaderV2() http.Header {
header := t.authHeader()
header.Set("Authorization", t.SecretKey)
t.logger.Printf("Auth Header %v", header)
return header
}
func (t *SDKConfig) authHeader() http.Header {
header := make(http.Header)
platform := os.Getenv("APPBUILDER_SDK_PLATFORM")
if platform == "" {
platform = "unknown"
}
header.Set("X-Appbuilder-Origin", "appbuilder_sdk")
header.Set("X-Appbuilder-Sdk-Config", "{\"appbuilder_sdk_version\":\"1.1.0\",\"appbuilder_sdk_language\":\"go\",\"appbuilder_sdk_platform\":\""+platform+"\"}")
header.Set("X-Appbuilder-Request-Id", uuid.New().String())
return header
}
func (t *SDKConfig) ServiceURL(suffix string) (*url.URL, error) {
// 解析 GatewayURL
parsedURL, err := url.Parse(t.GatewayURL)
if err != nil {
return nil, err
}
// 使用 path.Join 拼接路径
parsedURL.Path = path.Join(parsedURL.Path, suffix)
// 将路径直接解析为最终 URL
endpoint, err := url.Parse(suffix)
if err != nil {
return nil, err
}
// 解析相对路径
parsedURL = parsedURL.ResolveReference(endpoint)
// 返回拼接后的 URL
return parsedURL, nil
}
// ServiceURLV2 适配OpenAPI,当前仅AppbuilderClient使用
func (t *SDKConfig) ServiceURLV2(suffix string) (*url.URL, error) {
suffix = path.Join(t.ConsoleOpenAPIPrefix, t.ConsoleOpenAPIVersion, suffix)
return t.formatURL(t.GatewayURLV2, suffix)
}
func (t *SDKConfig) formatURL(absolutePath, suffix string) (*url.URL, error) {
t.logger.Printf("Service URL %s", absolutePath)
url, err := url.Parse(absolutePath)
if err != nil {
return nil, err
}
endpoint, err := url.Parse(suffix)
if err != nil {
return nil, err
}
url = url.ResolveReference(endpoint)
return url, nil
}
type nopCloser struct {
io.Reader
}
func (nopCloser) Close() error { return nil }
func NopCloser(r io.Reader) io.ReadCloser {
return nopCloser{Reader: r}
}
func (t *SDKConfig) BuildCurlCommand(req *http.Request) {
curlCmd := fmt.Sprintf("curl -X %s -L '%v' \\\n", req.Method, req.URL.String())
for k, v := range req.Header {
header := fmt.Sprintf("-H '%v: %v' \\\n", k, v[0])
curlCmd = fmt.Sprintf("%v %v", curlCmd, header)
}
if req.Method == "POST" {
bodyBytes, err := io.ReadAll(req.Body)
if err != nil {
t.logger.Println("Failed to read request body:", err)
return
}
req.Body.Close()
req.Body = NopCloser(strings.NewReader(string(bodyBytes)))
body := fmt.Sprintf("-d '%v'", string(bodyBytes))
curlCmd = fmt.Sprintf("%v %v", curlCmd, body)
} else if req.Method == "GET" || req.Method == "DELETE" {
curlCmd = strings.TrimSuffix(curlCmd, " \\\n")
}
fmt.Println("\n" + curlCmd + "\n")
}