mirror of
https://github.com/coaidev/coai.git
synced 2025-05-20 05:20:15 +09:00
feat: support detail debugging (#118)
This commit is contained in:
parent
14dc10025f
commit
92fcbab39e
@ -95,7 +95,7 @@ _🚀 **Next Generation AI One-Stop Solution**_
|
|||||||
- [x] Chat Completions (support *vision*, *tools_calling* and *function_calling*)
|
- [x] Chat Completions (support *vision*, *tools_calling* and *function_calling*)
|
||||||
- [x] Image Generation
|
- [x] Image Generation
|
||||||
- [x] Azure OpenAI
|
- [x] Azure OpenAI
|
||||||
- [x] Anthropic Claude (claude-2, claude-2.1, claude-instant)
|
- [x] Anthropic Claude (support *vision*)
|
||||||
- [x] Slack Claude (deprecated)
|
- [x] Slack Claude (deprecated)
|
||||||
- [x] Sparkdesk (support *function_calling*)
|
- [x] Sparkdesk (support *function_calling*)
|
||||||
- [x] Google Gemini (PaLM2)
|
- [x] Google Gemini (PaLM2)
|
||||||
|
@ -15,6 +15,7 @@ export type GeneralState = {
|
|||||||
docs: string;
|
docs: string;
|
||||||
file: string;
|
file: string;
|
||||||
pwa_manifest: string;
|
pwa_manifest: string;
|
||||||
|
debug_mode: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MailState = {
|
export type MailState = {
|
||||||
@ -122,6 +123,7 @@ export const initialSystemState: SystemProps = {
|
|||||||
docs: "",
|
docs: "",
|
||||||
file: "",
|
file: "",
|
||||||
pwa_manifest: "",
|
pwa_manifest: "",
|
||||||
|
debug_mode: false,
|
||||||
},
|
},
|
||||||
site: {
|
site: {
|
||||||
close_register: false,
|
close_register: false,
|
||||||
|
@ -692,6 +692,8 @@
|
|||||||
"backend": "后端域名",
|
"backend": "后端域名",
|
||||||
"backendTip": "后端域名(docker 安装默认路径为 /api),用于接收回调和存储等,默认为空",
|
"backendTip": "后端域名(docker 安装默认路径为 /api),用于接收回调和存储等,默认为空",
|
||||||
"backendPlaceholder": "后端回调域名,默认为空,接受回调必填(如 {{backend}})",
|
"backendPlaceholder": "后端回调域名,默认为空,接受回调必填(如 {{backend}})",
|
||||||
|
"debugMode": "调试模式",
|
||||||
|
"debugModeTip": "调试模式,开启后日志将输出详细的请求参数等的日志,用于排查问题",
|
||||||
"mailHost": "发件域名",
|
"mailHost": "发件域名",
|
||||||
"mailPort": "SMTP 端口",
|
"mailPort": "SMTP 端口",
|
||||||
"mailUser": "用户名",
|
"mailUser": "用户名",
|
||||||
|
@ -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_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",
|
"image_storeNoBackend": "No backend domain configured, cannot enable image storage",
|
||||||
"closeRelay": "Turn off Staging API",
|
"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",
|
"user": "Users",
|
||||||
"invitation-code": "Invitation Code",
|
"invitation-code": "Invitation Code",
|
||||||
|
@ -570,7 +570,9 @@
|
|||||||
"image_storeTip": "OpenAIチャンネルDALL - Eによって生成された画像は、画像の無効化を防ぐためにサーバーに保存されます",
|
"image_storeTip": "OpenAIチャンネルDALL - Eによって生成された画像は、画像の無効化を防ぐためにサーバーに保存されます",
|
||||||
"image_storeNoBackend": "バックエンドドメインが設定されていません。画像ストレージを有効にできません",
|
"image_storeNoBackend": "バックエンドドメインが設定されていません。画像ストレージを有効にできません",
|
||||||
"closeRelay": "ステージングAPIをオフにする",
|
"closeRelay": "ステージングAPIをオフにする",
|
||||||
"closeRelayTip": "ステージングAPIをオフにすると、オフにするとステージングAPIは使用できなくなります"
|
"closeRelayTip": "ステージングAPIをオフにすると、オフにするとステージングAPIは使用できなくなります",
|
||||||
|
"debugMode": "試験調整モード",
|
||||||
|
"debugModeTip": "デバッグモード、オンにすると、ログは詳細な要求パラメータとトラブルシューティングのための他のログを出力します"
|
||||||
},
|
},
|
||||||
"user": "ユーザー管理",
|
"user": "ユーザー管理",
|
||||||
"invitation-code": "招待コード",
|
"invitation-code": "招待コード",
|
||||||
|
@ -570,7 +570,9 @@
|
|||||||
"image_storeTip": "Изображения, сгенерированные каналом OpenAI DALL-E, будут храниться на сервере, чтобы предотвратить недействительность изображений",
|
"image_storeTip": "Изображения, сгенерированные каналом OpenAI DALL-E, будут храниться на сервере, чтобы предотвратить недействительность изображений",
|
||||||
"image_storeNoBackend": "Нет настроенного внутреннего домена, невозможно включить хранение изображений",
|
"image_storeNoBackend": "Нет настроенного внутреннего домена, невозможно включить хранение изображений",
|
||||||
"closeRelay": "Отключить Staging API",
|
"closeRelay": "Отключить Staging API",
|
||||||
"closeRelayTip": "Отключите промежуточный API, промежуточный API будет недоступен после отключения"
|
"closeRelayTip": "Отключите промежуточный API, промежуточный API будет недоступен после отключения",
|
||||||
|
"debugMode": "Режим отладки",
|
||||||
|
"debugModeTip": "Режим отладки, после включения журнал выведет подробные параметры запроса и другие журналы для устранения неполадок"
|
||||||
},
|
},
|
||||||
"user": "Управление пользователями",
|
"user": "Управление пользователями",
|
||||||
"invitation-code": "Код приглашения",
|
"invitation-code": "Код приглашения",
|
||||||
|
@ -236,6 +236,22 @@ function General({ data, dispatch, onChange }: CompProps<GeneralState>) {
|
|||||||
</Button>
|
</Button>
|
||||||
</JSONEditorProvider>
|
</JSONEditorProvider>
|
||||||
</ParagraphItem>
|
</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>
|
<ParagraphFooter>
|
||||||
<div className={`grow`} />
|
<div className={`grow`} />
|
||||||
<RootDialog />
|
<RootDialog />
|
||||||
|
@ -31,6 +31,7 @@ type generalState struct {
|
|||||||
File string `json:"file" mapstructure:"file"`
|
File string `json:"file" mapstructure:"file"`
|
||||||
Docs string `json:"docs" mapstructure:"docs"`
|
Docs string `json:"docs" mapstructure:"docs"`
|
||||||
PWAManifest string `json:"pwa_manifest" mapstructure:"pwamanifest"`
|
PWAManifest string `json:"pwa_manifest" mapstructure:"pwamanifest"`
|
||||||
|
DebugMode bool `json:"debug_mode" mapstructure:"debugmode"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type siteState struct {
|
type siteState struct {
|
||||||
@ -94,6 +95,7 @@ func NewSystemConfig() *SystemConfig {
|
|||||||
|
|
||||||
func (c *SystemConfig) Load() {
|
func (c *SystemConfig) Load() {
|
||||||
globals.NotifyUrl = c.GetBackend()
|
globals.NotifyUrl = c.GetBackend()
|
||||||
|
globals.DebugMode = c.General.DebugMode
|
||||||
|
|
||||||
globals.CloseRegistration = c.Site.CloseRegister
|
globals.CloseRegistration = c.Site.CloseRegister
|
||||||
globals.CloseRelay = c.Site.CloseRelay
|
globals.CloseRelay = c.Site.CloseRelay
|
||||||
|
@ -11,6 +11,7 @@ const AnonymousMaxThread = 1
|
|||||||
|
|
||||||
var AllowedOrigins []string
|
var AllowedOrigins []string
|
||||||
|
|
||||||
|
var DebugMode bool
|
||||||
var NotifyUrl = ""
|
var NotifyUrl = ""
|
||||||
var ArticlePermissionGroup []string
|
var ArticlePermissionGroup []string
|
||||||
var GenerationPermissionGroup []string
|
var GenerationPermissionGroup []string
|
||||||
|
60
utils/net.go
60
utils/net.go
@ -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) {
|
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)
|
req, err := http.NewRequest(method, uri, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if globals.DebugMode {
|
||||||
|
globals.Debug(fmt.Sprintf("[http] failed to create request: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fillHeaders(req, headers)
|
fillHeaders(req, headers)
|
||||||
@ -83,20 +91,40 @@ func Http(uri string, method string, ptr interface{}, headers map[string]string,
|
|||||||
client := newClient(config)
|
client := newClient(config)
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if globals.DebugMode {
|
||||||
|
globals.Debug(fmt.Sprintf("[http] failed to send request: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if err = json.NewDecoder(resp.Body).Decode(ptr); err != nil {
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if globals.DebugMode {
|
||||||
|
globals.Debug(fmt.Sprintf("[http] response: %s", Marshal(ptr)))
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func HttpRaw(uri string, method string, headers map[string]string, body io.Reader, config []globals.ProxyConfig) (data []byte, err error) {
|
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)
|
req, err := http.NewRequest(method, uri, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if globals.DebugMode {
|
||||||
|
globals.Debug(fmt.Sprintf("[http] failed to create request: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
fillHeaders(req, headers)
|
fillHeaders(req, headers)
|
||||||
@ -104,14 +132,26 @@ func HttpRaw(uri string, method string, headers map[string]string, body io.Reade
|
|||||||
client := newClient(config)
|
client := newClient(config)
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if globals.DebugMode {
|
||||||
|
globals.Debug(fmt.Sprintf("[http] failed to send request: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if data, err = io.ReadAll(resp.Body); err != nil {
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if globals.DebugMode {
|
||||||
|
globals.Debug(fmt.Sprintf("[http] response: %s", string(data)))
|
||||||
|
}
|
||||||
return data, nil
|
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}
|
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)
|
client := newClient(config)
|
||||||
req, err := http.NewRequest(method, uri, ConvertBody(body))
|
req, err := http.NewRequest(method, uri, ConvertBody(body))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if globals.DebugMode {
|
||||||
|
globals.Debug(fmt.Sprintf("[http-stream] failed to create request: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,12 +217,20 @@ func EventSource(method string, uri string, headers map[string]string, body inte
|
|||||||
|
|
||||||
res, err := client.Do(req)
|
res, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if globals.DebugMode {
|
||||||
|
globals.Debug(fmt.Sprintf("[http-stream] failed to send request: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
|
|
||||||
if res.StatusCode >= 400 {
|
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 content, err := io.ReadAll(res.Body); err == nil {
|
||||||
if form, err := Unmarshal[map[string]interface{}](content); err == nil {
|
if form, err := Unmarshal[map[string]interface{}](content); err == nil {
|
||||||
data := MarshalWithIndent(form, 2)
|
data := MarshalWithIndent(form, 2)
|
||||||
@ -197,6 +253,10 @@ func EventSource(method string, uri string, headers map[string]string, body inte
|
|||||||
|
|
||||||
data := string(buf[:n])
|
data := string(buf[:n])
|
||||||
for _, item := range strings.Split(data, "\n") {
|
for _, item := range strings.Split(data, "\n") {
|
||||||
|
if globals.DebugMode {
|
||||||
|
globals.Debug(fmt.Sprintf("[http-stream] response: %s", item))
|
||||||
|
}
|
||||||
|
|
||||||
segment := strings.TrimSpace(item)
|
segment := strings.TrimSpace(item)
|
||||||
if len(segment) > 0 {
|
if len(segment) > 0 {
|
||||||
if err := callback(segment); err != nil {
|
if err := callback(segment); err != nil {
|
||||||
|
@ -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)
|
client := newClient(config)
|
||||||
req, err := http.NewRequest(props.Method, props.Uri, ConvertBody(props.Body))
|
req, err := http.NewRequest(props.Method, props.Uri, ConvertBody(props.Body))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if globals.DebugMode {
|
||||||
|
globals.Debug(fmt.Sprintf("[sse] failed to create request: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
return &EventScannerError{Error: err}
|
return &EventScannerError{Error: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,6 +63,10 @@ func EventScanner(props *EventScannerProps, config ...globals.ProxyConfig) *Even
|
|||||||
|
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if globals.DebugMode {
|
||||||
|
globals.Debug(fmt.Sprintf("[sse] failed to send request: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
return &EventScannerError{Error: err}
|
return &EventScannerError{Error: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,9 +74,14 @@ func EventScanner(props *EventScannerProps, config ...globals.ProxyConfig) *Even
|
|||||||
|
|
||||||
if resp.StatusCode >= 400 {
|
if resp.StatusCode >= 400 {
|
||||||
// for error response
|
// 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{
|
return &EventScannerError{
|
||||||
Error: fmt.Errorf("request failed with status code: %d", resp.StatusCode),
|
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() {
|
for scanner.Scan() {
|
||||||
raw := scanner.Text()
|
raw := scanner.Text()
|
||||||
|
|
||||||
if len(raw) <= 5 || !strings.HasPrefix(raw, "data:") {
|
if len(raw) <= 5 || !strings.HasPrefix(raw, "data:") {
|
||||||
// for only `data:` partial raw or unexpected chunk
|
// for only `data:` partial raw or unexpected chunk
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if globals.DebugMode {
|
||||||
|
globals.Debug(fmt.Sprintf("[sse] chunk: %s", raw))
|
||||||
|
}
|
||||||
|
|
||||||
chunk := strings.TrimSpace(strings.TrimPrefix(raw, "data:"))
|
chunk := strings.TrimSpace(strings.TrimPrefix(raw, "data:"))
|
||||||
if chunk == "[DONE]" || strings.HasPrefix(chunk, "[DONE]") {
|
if chunk == "[DONE]" || strings.HasPrefix(chunk, "[DONE]") {
|
||||||
// for done signal
|
// for done signal
|
||||||
|
@ -2,6 +2,7 @@ package utils
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"chat/globals"
|
"chat/globals"
|
||||||
|
"fmt"
|
||||||
"github.com/pkoukk/tiktoken-go"
|
"github.com/pkoukk/tiktoken-go"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -55,6 +56,9 @@ func NumTokensFromMessages(messages []globals.Message, model string) (tokens int
|
|||||||
// return len([]rune(data)) * weight
|
// return len([]rune(data)) * weight
|
||||||
|
|
||||||
// use the recall method instead (default encoder model is gpt-3.5-turbo-0613)
|
// 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)
|
return NumTokensFromMessages(messages, globals.GPT3Turbo0613)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,6 +69,10 @@ func NumTokensFromMessages(messages []globals.Message, model string) (tokens int
|
|||||||
tokensPerMessage
|
tokensPerMessage
|
||||||
}
|
}
|
||||||
tokens += 3 // every reply is primed with <|start|>assistant<|message|>
|
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
|
return tokens
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user