feat: support detail debugging (#118)

This commit is contained in:
Zhang Minghan 2024-03-15 14:16:54 +08:00
parent 14dc10025f
commit 92fcbab39e
12 changed files with 124 additions and 5 deletions

View File

@ -95,7 +95,7 @@ _🚀 **Next Generation AI One-Stop Solution**_
- [x] Chat Completions (support *vision*, *tools_calling* and *function_calling*)
- [x] Image Generation
- [x] Azure OpenAI
- [x] Anthropic Claude (claude-2, claude-2.1, claude-instant)
- [x] Anthropic Claude (support *vision*)
- [x] Slack Claude (deprecated)
- [x] Sparkdesk (support *function_calling*)
- [x] Google Gemini (PaLM2)

View File

@ -15,6 +15,7 @@ export type GeneralState = {
docs: string;
file: string;
pwa_manifest: string;
debug_mode: boolean;
};
export type MailState = {
@ -122,6 +123,7 @@ export const initialSystemState: SystemProps = {
docs: "",
file: "",
pwa_manifest: "",
debug_mode: false,
},
site: {
close_register: false,

View File

@ -692,6 +692,8 @@
"backend": "后端域名",
"backendTip": "后端域名docker 安装默认路径为 /api用于接收回调和存储等默认为空",
"backendPlaceholder": "后端回调域名,默认为空,接受回调必填(如 {{backend}}",
"debugMode": "调试模式",
"debugModeTip": "调试模式,开启后日志将输出详细的请求参数等的日志,用于排查问题",
"mailHost": "发件域名",
"mailPort": "SMTP 端口",
"mailUser": "用户名",

View File

@ -570,7 +570,9 @@
"image_storeTip": "Images generated by the OpenAI channel DALL-E will be stored on the server to prevent invalidation of the images",
"image_storeNoBackend": "No backend domain configured, cannot enable image storage",
"closeRelay": "Turn off Staging API",
"closeRelayTip": "Turn off the staging API, the staging API will not be available after turning off"
"closeRelayTip": "Turn off the staging API, the staging API will not be available after turning off",
"debugMode": "debugging mode",
"debugModeTip": "Debug mode, after turning on, the log will output detailed request parameters and other logs for troubleshooting"
},
"user": "Users",
"invitation-code": "Invitation Code",

View File

@ -570,7 +570,9 @@
"image_storeTip": "OpenAIチャンネルDALL - Eによって生成された画像は、画像の無効化を防ぐためにサーバーに保存されます",
"image_storeNoBackend": "バックエンドドメインが設定されていません。画像ストレージを有効にできません",
"closeRelay": "ステージングAPIをオフにする",
"closeRelayTip": "ステージングAPIをオフにすると、オフにするとステージングAPIは使用できなくなります"
"closeRelayTip": "ステージングAPIをオフにすると、オフにするとステージングAPIは使用できなくなります",
"debugMode": "試験調整モード",
"debugModeTip": "デバッグモード、オンにすると、ログは詳細な要求パラメータとトラブルシューティングのための他のログを出力します"
},
"user": "ユーザー管理",
"invitation-code": "招待コード",

View File

@ -570,7 +570,9 @@
"image_storeTip": "Изображения, сгенерированные каналом OpenAI DALL-E, будут храниться на сервере, чтобы предотвратить недействительность изображений",
"image_storeNoBackend": "Нет настроенного внутреннего домена, невозможно включить хранение изображений",
"closeRelay": "Отключить Staging API",
"closeRelayTip": "Отключите промежуточный API, промежуточный API будет недоступен после отключения"
"closeRelayTip": "Отключите промежуточный API, промежуточный API будет недоступен после отключения",
"debugMode": "Режим отладки",
"debugModeTip": "Режим отладки, после включения журнал выведет подробные параметры запроса и другие журналы для устранения неполадок"
},
"user": "Управление пользователями",
"invitation-code": "Код приглашения",

View File

@ -236,6 +236,22 @@ function General({ data, dispatch, onChange }: CompProps<GeneralState>) {
</Button>
</JSONEditorProvider>
</ParagraphItem>
<ParagraphItem>
<Label>
{t("admin.system.debugMode")}
<Tips
className={`inline-block`}
content={t("admin.system.debugModeTip")}
/>
</Label>
<Switch
checked={data.debug_mode}
onCheckedChange={(value) => {
dispatch({ type: "update:general.debug_mode", value });
}}
/>
</ParagraphItem>
<ParagraphSpace />
<ParagraphFooter>
<div className={`grow`} />
<RootDialog />

View File

@ -31,6 +31,7 @@ type generalState struct {
File string `json:"file" mapstructure:"file"`
Docs string `json:"docs" mapstructure:"docs"`
PWAManifest string `json:"pwa_manifest" mapstructure:"pwamanifest"`
DebugMode bool `json:"debug_mode" mapstructure:"debugmode"`
}
type siteState struct {
@ -94,6 +95,7 @@ func NewSystemConfig() *SystemConfig {
func (c *SystemConfig) Load() {
globals.NotifyUrl = c.GetBackend()
globals.DebugMode = c.General.DebugMode
globals.CloseRegistration = c.Site.CloseRegister
globals.CloseRelay = c.Site.CloseRelay

View File

@ -11,6 +11,7 @@ const AnonymousMaxThread = 1
var AllowedOrigins []string
var DebugMode bool
var NotifyUrl = ""
var ArticlePermissionGroup []string
var GenerationPermissionGroup []string

View File

@ -74,8 +74,16 @@ func fillHeaders(req *http.Request, headers map[string]string) {
}
func Http(uri string, method string, ptr interface{}, headers map[string]string, body io.Reader, config []globals.ProxyConfig) (err error) {
if globals.DebugMode {
globals.Debug(fmt.Sprintf("[http] %s %s\nheaders: \n%s\nbody: \n%s", method, uri, Marshal(headers), Marshal(body)))
}
req, err := http.NewRequest(method, uri, body)
if err != nil {
if globals.DebugMode {
globals.Debug(fmt.Sprintf("[http] failed to create request: %s", err))
}
return err
}
fillHeaders(req, headers)
@ -83,20 +91,40 @@ func Http(uri string, method string, ptr interface{}, headers map[string]string,
client := newClient(config)
resp, err := client.Do(req)
if err != nil {
if globals.DebugMode {
globals.Debug(fmt.Sprintf("[http] failed to send request: %s", err))
}
return err
}
defer resp.Body.Close()
if err = json.NewDecoder(resp.Body).Decode(ptr); err != nil {
if globals.DebugMode {
globals.Debug(fmt.Sprintf("[http] failed to decode response: %s\nresponse: %s", err, resp.Body))
}
return err
}
if globals.DebugMode {
globals.Debug(fmt.Sprintf("[http] response: %s", Marshal(ptr)))
}
return nil
}
func HttpRaw(uri string, method string, headers map[string]string, body io.Reader, config []globals.ProxyConfig) (data []byte, err error) {
if globals.DebugMode {
globals.Debug(fmt.Sprintf("[http] %s %s\nheaders: \n%s\nbody: \n%s", method, uri, Marshal(headers), Marshal(body)))
}
req, err := http.NewRequest(method, uri, body)
if err != nil {
if globals.DebugMode {
globals.Debug(fmt.Sprintf("[http] failed to create request: %s", err))
}
return nil, err
}
fillHeaders(req, headers)
@ -104,14 +132,26 @@ func HttpRaw(uri string, method string, headers map[string]string, body io.Reade
client := newClient(config)
resp, err := client.Do(req)
if err != nil {
if globals.DebugMode {
globals.Debug(fmt.Sprintf("[http] failed to send request: %s", err))
}
return nil, err
}
defer resp.Body.Close()
if data, err = io.ReadAll(resp.Body); err != nil {
if globals.DebugMode {
globals.Debug(fmt.Sprintf("[http] failed to read response: %s", err))
}
return nil, err
}
if globals.DebugMode {
globals.Debug(fmt.Sprintf("[http] response: %s", string(data)))
}
return data, nil
}
@ -159,9 +199,17 @@ func EventSource(method string, uri string, headers map[string]string, body inte
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
if globals.DebugMode {
globals.Debug(fmt.Sprintf("[http-stream] %s %s\nheaders: \n%s\nbody: \n%s", method, uri, Marshal(headers), Marshal(body)))
}
client := newClient(config)
req, err := http.NewRequest(method, uri, ConvertBody(body))
if err != nil {
if globals.DebugMode {
globals.Debug(fmt.Sprintf("[http-stream] failed to create request: %s", err))
}
return err
}
@ -169,12 +217,20 @@ func EventSource(method string, uri string, headers map[string]string, body inte
res, err := client.Do(req)
if err != nil {
if globals.DebugMode {
globals.Debug(fmt.Sprintf("[http-stream] failed to send request: %s", err))
}
return err
}
defer res.Body.Close()
if res.StatusCode >= 400 {
if globals.DebugMode {
globals.Debug(fmt.Sprintf("[http-stream] request failed with status: %s\nresponse: %s", res.Status, res.Body))
}
if content, err := io.ReadAll(res.Body); err == nil {
if form, err := Unmarshal[map[string]interface{}](content); err == nil {
data := MarshalWithIndent(form, 2)
@ -197,6 +253,10 @@ func EventSource(method string, uri string, headers map[string]string, body inte
data := string(buf[:n])
for _, item := range strings.Split(data, "\n") {
if globals.DebugMode {
globals.Debug(fmt.Sprintf("[http-stream] response: %s", item))
}
segment := strings.TrimSpace(item)
if len(segment) > 0 {
if err := callback(segment); err != nil {

View File

@ -45,9 +45,17 @@ func EventScanner(props *EventScannerProps, config ...globals.ProxyConfig) *Even
}
}()
if globals.DebugMode {
globals.Debug(fmt.Sprintf("[sse] event source: %s %s\nheaders: %v\nbody: %v", props.Method, props.Uri, Marshal(props.Headers), Marshal(props.Body)))
}
client := newClient(config)
req, err := http.NewRequest(props.Method, props.Uri, ConvertBody(props.Body))
if err != nil {
if globals.DebugMode {
globals.Debug(fmt.Sprintf("[sse] failed to create request: %s", err))
}
return &EventScannerError{Error: err}
}
@ -55,6 +63,10 @@ func EventScanner(props *EventScannerProps, config ...globals.ProxyConfig) *Even
resp, err := client.Do(req)
if err != nil {
if globals.DebugMode {
globals.Debug(fmt.Sprintf("[sse] failed to send request: %s", err))
}
return &EventScannerError{Error: err}
}
@ -62,9 +74,14 @@ func EventScanner(props *EventScannerProps, config ...globals.ProxyConfig) *Even
if resp.StatusCode >= 400 {
// for error response
body := getErrorBody(resp)
if globals.DebugMode {
globals.Debug(fmt.Sprintf("[sse] request failed with status: %s\nresponse: %s", resp.Status, body))
}
return &EventScannerError{
Error: fmt.Errorf("request failed with status code: %d", resp.StatusCode),
Body: getErrorBody(resp),
Body: body,
}
}
@ -91,11 +108,16 @@ func EventScanner(props *EventScannerProps, config ...globals.ProxyConfig) *Even
for scanner.Scan() {
raw := scanner.Text()
if len(raw) <= 5 || !strings.HasPrefix(raw, "data:") {
// for only `data:` partial raw or unexpected chunk
continue
}
if globals.DebugMode {
globals.Debug(fmt.Sprintf("[sse] chunk: %s", raw))
}
chunk := strings.TrimSpace(strings.TrimPrefix(raw, "data:"))
if chunk == "[DONE]" || strings.HasPrefix(chunk, "[DONE]") {
// for done signal

View File

@ -2,6 +2,7 @@ package utils
import (
"chat/globals"
"fmt"
"github.com/pkoukk/tiktoken-go"
"strings"
)
@ -55,6 +56,9 @@ func NumTokensFromMessages(messages []globals.Message, model string) (tokens int
// return len([]rune(data)) * weight
// use the recall method instead (default encoder model is gpt-3.5-turbo-0613)
if globals.DebugMode {
globals.Debug(fmt.Sprintf("[tiktoken] error encoding messages: %s (model: %s), using default model instead", err, model))
}
return NumTokensFromMessages(messages, globals.GPT3Turbo0613)
}
@ -65,6 +69,10 @@ func NumTokensFromMessages(messages []globals.Message, model string) (tokens int
tokensPerMessage
}
tokens += 3 // every reply is primed with <|start|>assistant<|message|>
if globals.DebugMode {
globals.Debug(fmt.Sprintf("[tiktoken] num tokens from messages: %d (tokens per message: %d, model: %s)", tokens, tokensPerMessage, model))
}
return tokens
}