From 53da13e006e6a987975a5233c2839aa4006cb0af Mon Sep 17 00:00:00 2001 From: yokowu <18836617@qq.com> Date: Mon, 28 Jul 2025 11:13:52 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=85=BC=E5=AE=B9=E9=9D=9E=20openai=20?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E7=9A=84=E6=A8=A1=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/proxy/proxy.go | 22 +++++++++++++---- backend/internal/proxy/resplog.go | 39 +++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 backend/internal/proxy/resplog.go diff --git a/backend/internal/proxy/proxy.go b/backend/internal/proxy/proxy.go index 29cb16f..2f895fe 100644 --- a/backend/internal/proxy/proxy.go +++ b/backend/internal/proxy/proxy.go @@ -7,6 +7,7 @@ import ( "net/http" "net/http/httputil" "net/url" + "strings" "time" "github.com/chaitin/MonkeyCode/backend/config" @@ -86,22 +87,30 @@ func (l *LLMProxy) rewrite(r *httputil.ProxyRequest) { l.logger.DebugContext(r.In.Context(), "rewrite request", slog.String("path", r.In.URL.Path)) mt, ok := modelType[r.In.URL.Path] if !ok { - l.logger.Error("model type not found", slog.String("path", r.In.URL.Path)) + l.logger.ErrorContext(r.In.Context(), "model type not found", slog.String("path", r.In.URL.Path)) return } m, err := l.usecase.SelectModelWithLoadBalancing("", mt) if err != nil { - l.logger.Error("select model with load balancing failed", slog.String("path", r.In.URL.Path), slog.Any("err", err)) + l.logger.ErrorContext(r.In.Context(), "select model with load balancing failed", slog.String("path", r.In.URL.Path), slog.Any("err", err)) + return + } + ul, err := url.Parse(m.APIBase) + if err != nil { + l.logger.ErrorContext(r.In.Context(), "parse model api base failed", slog.String("path", r.In.URL.Path), slog.Any("err", err)) return } + path := r.In.URL.Path + path = strings.ReplaceAll(path, "/v1", "") + path = ul.Path + path if r.In.ContentLength > 0 { tee := tee.NewReqTeeWithMaxSize(r.In.Body, 10*1024*1024) r.Out.Body = tee ctx := context.WithValue(r.In.Context(), CtxKey{}, &ProxyCtx{ ctx: r.In.Context(), - Path: r.In.URL.Path, + Path: path, Model: m, ReqTee: tee, RequestID: r.In.Context().Value(logger.RequestIDKey{}).(string), @@ -119,7 +128,7 @@ func (l *LLMProxy) rewrite(r *httputil.ProxyRequest) { r.Out.URL.Scheme = u.Scheme r.Out.URL.Host = u.Host - r.Out.URL.Path = r.In.URL.Path + r.Out.URL.Path = path r.Out.Header.Set("Authorization", "Bearer "+m.APIKey) r.SetXForwarded() r.Out.Host = u.Host @@ -127,6 +136,11 @@ func (l *LLMProxy) rewrite(r *httputil.ProxyRequest) { } func (l *LLMProxy) modifyResponse(resp *http.Response) error { + if resp.StatusCode != http.StatusOK { + resp.Body = NewRespLog(resp.Request.Context(), l.logger, resp.Body) + return nil + } + ctx := resp.Request.Context() if pctx, ok := ctx.Value(CtxKey{}).(*ProxyCtx); ok { pctx.ctx = ctx diff --git a/backend/internal/proxy/resplog.go b/backend/internal/proxy/resplog.go new file mode 100644 index 0000000..8e2c7c2 --- /dev/null +++ b/backend/internal/proxy/resplog.go @@ -0,0 +1,39 @@ +package proxy + +import ( + "bytes" + "context" + "io" + "log/slog" +) + +type RespLog struct { + ctx context.Context + logger *slog.Logger + src io.ReadCloser +} + +var _ io.ReadCloser = &RespLog{} + +func NewRespLog(ctx context.Context, logger *slog.Logger, src io.ReadCloser) *RespLog { + return &RespLog{ctx: ctx, logger: logger, src: src} +} + +// Close implements io.ReadCloser. +func (r *RespLog) Close() error { + return r.src.Close() +} + +// Read implements io.ReadCloser. +func (r *RespLog) Read(p []byte) (n int, err error) { + buf := bytes.NewBuffer([]byte("")) + n, err = r.src.Read(p) + if n > 0 { + buf.Write(p[:n]) + } + if err != nil { + r.logger.ErrorContext(r.ctx, "read response failed", "error", err, "response", buf.String()) + return + } + return +}