Skip to content

Commit 3995067

Browse files
committed
chore: req logging
Signed-off-by: Danny Kopping <[email protected]>
1 parent fa62391 commit 3995067

9 files changed

+108
-48
lines changed

config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package aibridge
22

33
type ProviderConfig struct {
44
BaseURL, Key string
5+
// EnableUpstreamLogging enables logging of upstream API requests and responses to /tmp/$provider.log
6+
EnableUpstreamLogging bool
57
}
68

79
type Config struct {

intercept_anthropic_messages_base.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ type AnthropicMessagesInterceptionBase struct {
1414
id uuid.UUID
1515
req *MessageNewParamsWrapper
1616

17-
baseURL, key string
18-
logger slog.Logger
17+
cfg ProviderConfig
18+
logger slog.Logger
1919

2020
recorder Recorder
2121
mcpProxy mcp.ServerProxier

intercept_anthropic_messages_blocking.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,11 @@ type AnthropicMessagesBlockingInterception struct {
2222
AnthropicMessagesInterceptionBase
2323
}
2424

25-
func NewAnthropicMessagesBlockingInterception(id uuid.UUID, req *MessageNewParamsWrapper, baseURL, key string) *AnthropicMessagesBlockingInterception {
25+
func NewAnthropicMessagesBlockingInterception(id uuid.UUID, req *MessageNewParamsWrapper, cfg ProviderConfig) *AnthropicMessagesBlockingInterception {
2626
return &AnthropicMessagesBlockingInterception{AnthropicMessagesInterceptionBase: AnthropicMessagesInterceptionBase{
27-
id: id,
28-
req: req,
29-
baseURL: baseURL,
30-
key: key,
27+
id: id,
28+
req: req,
29+
cfg: cfg,
3130
}}
3231
}
3332

@@ -58,7 +57,7 @@ func (i *AnthropicMessagesBlockingInterception) ProcessRequest(w http.ResponseWr
5857

5958
opts := []option.RequestOption{option.WithRequestTimeout(time.Second * 60)} // TODO: configurable timeout
6059

61-
client := newAnthropicClient(i.baseURL, i.key, opts...)
60+
client := newAnthropicClient(i.cfg, i.id.String(), opts...)
6261
messages := i.req.MessageNewParams
6362
logger := i.logger.With(slog.F("model", i.req.Model))
6463

intercept_anthropic_messages_streaming.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,11 @@ type AnthropicMessagesStreamingInterception struct {
2525
AnthropicMessagesInterceptionBase
2626
}
2727

28-
func NewAnthropicMessagesStreamingInterception(id uuid.UUID, req *MessageNewParamsWrapper, baseURL, key string) *AnthropicMessagesStreamingInterception {
28+
func NewAnthropicMessagesStreamingInterception(id uuid.UUID, req *MessageNewParamsWrapper, cfg ProviderConfig) *AnthropicMessagesStreamingInterception {
2929
return &AnthropicMessagesStreamingInterception{AnthropicMessagesInterceptionBase: AnthropicMessagesInterceptionBase{
30-
id: id,
31-
req: req,
32-
baseURL: baseURL,
33-
key: key,
30+
id: id,
31+
req: req,
32+
cfg: cfg,
3433
}}
3534
}
3635

@@ -95,7 +94,7 @@ func (i *AnthropicMessagesStreamingInterception) ProcessRequest(w http.ResponseW
9594
_ = events.Shutdown(streamCtx) // Catch-all in case it doesn't get shutdown after stream completes.
9695
}()
9796

98-
client := newAnthropicClient(i.baseURL, i.key)
97+
client := newAnthropicClient(i.cfg, i.id.String())
9998
messages := i.req.MessageNewParams
10099

101100
// Accumulate usage across the entire streaming interaction (including tool reinvocations).

intercept_openai_chat_base.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ type OpenAIChatInterceptionBase struct {
1717
id uuid.UUID
1818
req *ChatCompletionNewParamsWrapper
1919

20-
baseURL, key string
21-
logger slog.Logger
20+
cfg ProviderConfig
21+
logger slog.Logger
2222

2323
recorder Recorder
2424
mcpProxy mcp.ServerProxier

intercept_openai_chat_blocking.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,11 @@ type OpenAIBlockingChatInterception struct {
2323
OpenAIChatInterceptionBase
2424
}
2525

26-
func NewOpenAIBlockingChatInterception(id uuid.UUID, req *ChatCompletionNewParamsWrapper, baseURL, key string) *OpenAIBlockingChatInterception {
26+
func NewOpenAIBlockingChatInterception(id uuid.UUID, req *ChatCompletionNewParamsWrapper, cfg ProviderConfig) *OpenAIBlockingChatInterception {
2727
return &OpenAIBlockingChatInterception{OpenAIChatInterceptionBase: OpenAIChatInterceptionBase{
28-
id: id,
29-
req: req,
30-
baseURL: baseURL,
31-
key: key,
28+
id: id,
29+
req: req,
30+
cfg: cfg,
3231
}}
3332
}
3433

@@ -42,7 +41,7 @@ func (i *OpenAIBlockingChatInterception) ProcessRequest(w http.ResponseWriter, r
4241
}
4342

4443
ctx := r.Context()
45-
client := newOpenAIClient(i.baseURL, i.key)
44+
client := newOpenAIClient(i.cfg, i.id.String())
4645
logger := i.logger.With(slog.F("model", i.req.Model))
4746

4847
var (

intercept_openai_chat_streaming.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,11 @@ type OpenAIStreamingChatInterception struct {
2424
OpenAIChatInterceptionBase
2525
}
2626

27-
func NewOpenAIStreamingChatInterception(id uuid.UUID, req *ChatCompletionNewParamsWrapper, baseURL, key string) *OpenAIStreamingChatInterception {
27+
func NewOpenAIStreamingChatInterception(id uuid.UUID, req *ChatCompletionNewParamsWrapper, cfg ProviderConfig) *OpenAIStreamingChatInterception {
2828
return &OpenAIStreamingChatInterception{OpenAIChatInterceptionBase: OpenAIChatInterceptionBase{
29-
id: id,
30-
req: req,
31-
baseURL: baseURL,
32-
key: key,
29+
id: id,
30+
req: req,
31+
cfg: cfg,
3332
}}
3433
}
3534

@@ -64,7 +63,7 @@ func (i *OpenAIStreamingChatInterception) ProcessRequest(w http.ResponseWriter,
6463
defer cancel()
6564
r = r.WithContext(ctx) // Rewire context for SSE cancellation.
6665

67-
client := newOpenAIClient(i.baseURL, i.key)
66+
client := newOpenAIClient(i.cfg, i.id.String())
6867
logger := i.logger.With(slog.F("model", i.req.Model))
6968

7069
streamCtx, streamCancel := context.WithCancelCause(ctx)

provider_anthropic.go

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import (
55
"errors"
66
"fmt"
77
"io"
8+
"log"
89
"net/http"
10+
"net/http/httputil"
911
"os"
1012

1113
"github.com/anthropics/anthropic-sdk-go"
@@ -19,7 +21,7 @@ var _ Provider = &AnthropicProvider{}
1921

2022
// AnthropicProvider allows for interactions with the Anthropic API.
2123
type AnthropicProvider struct {
22-
baseURL, key string
24+
cfg ProviderConfig
2325
}
2426

2527
const (
@@ -37,8 +39,7 @@ func NewAnthropicProvider(cfg ProviderConfig) *AnthropicProvider {
3739
}
3840

3941
return &AnthropicProvider{
40-
baseURL: cfg.BaseURL,
41-
key: cfg.Key,
42+
cfg: cfg,
4243
}
4344
}
4445

@@ -74,17 +75,17 @@ func (p *AnthropicProvider) CreateInterceptor(w http.ResponseWriter, r *http.Req
7475
}
7576

7677
if req.Stream {
77-
return NewAnthropicMessagesStreamingInterception(id, &req, p.baseURL, p.key), nil
78+
return NewAnthropicMessagesStreamingInterception(id, &req, p.cfg), nil
7879
}
7980

80-
return NewAnthropicMessagesBlockingInterception(id, &req, p.baseURL, p.key), nil
81+
return NewAnthropicMessagesBlockingInterception(id, &req, p.cfg), nil
8182
}
8283

8384
return nil, UnknownRoute
8485
}
8586

8687
func (p *AnthropicProvider) BaseURL() string {
87-
return p.baseURL
88+
return p.cfg.BaseURL
8889
}
8990

9091
func (p *AnthropicProvider) AuthHeader() string {
@@ -96,12 +97,42 @@ func (p *AnthropicProvider) InjectAuthHeader(headers *http.Header) {
9697
headers = &http.Header{}
9798
}
9899

99-
headers.Set(p.AuthHeader(), p.key)
100+
headers.Set(p.AuthHeader(), p.cfg.Key)
100101
}
101102

102-
func newAnthropicClient(baseURL, key string, opts ...option.RequestOption) anthropic.Client {
103-
opts = append(opts, option.WithAPIKey(key))
104-
opts = append(opts, option.WithBaseURL(baseURL))
103+
func newAnthropicClient(cfg ProviderConfig, id string, opts ...option.RequestOption) anthropic.Client {
104+
opts = append(opts, option.WithAPIKey(cfg.Key))
105+
opts = append(opts, option.WithBaseURL(cfg.BaseURL))
106+
107+
if cfg.EnableUpstreamLogging {
108+
reqLogFile, err := os.OpenFile("/tmp/anthropic-req.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
109+
if err == nil {
110+
reqLogger := log.New(reqLogFile, "", log.LstdFlags)
111+
112+
resLogFile, err := os.OpenFile("/tmp/anthropic-res.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
113+
if err == nil {
114+
resLogger := log.New(resLogFile, "", log.LstdFlags)
115+
116+
opts = append(opts, option.WithMiddleware(func(req *http.Request, next option.MiddlewareNext) (*http.Response, error) {
117+
if reqDump, err := httputil.DumpRequest(req, true); err == nil {
118+
reqLogger.Printf("[req] [%s] %s", id, reqDump)
119+
}
120+
121+
resp, err := next(req)
122+
if err != nil {
123+
resLogger.Printf("[res] [%s] Error: %v", id, err)
124+
return resp, err
125+
}
126+
127+
if respDump, err := httputil.DumpResponse(resp, true); err == nil {
128+
resLogger.Printf("[res] [%s] %s", id, respDump)
129+
}
130+
131+
return resp, err
132+
}))
133+
}
134+
}
135+
}
105136

106137
return anthropic.NewClient(opts...)
107138
}

provider_openai.go

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import (
44
"encoding/json"
55
"fmt"
66
"io"
7+
"log"
78
"net/http"
9+
"net/http/httputil"
810
"os"
911

1012
"github.com/google/uuid"
@@ -16,7 +18,7 @@ var _ Provider = &OpenAIProvider{}
1618

1719
// OpenAIProvider allows for interactions with the OpenAI API.
1820
type OpenAIProvider struct {
19-
baseURL, key string
21+
cfg ProviderConfig
2022
}
2123

2224
const (
@@ -35,8 +37,7 @@ func NewOpenAIProvider(cfg ProviderConfig) *OpenAIProvider {
3537
}
3638

3739
return &OpenAIProvider{
38-
baseURL: cfg.BaseURL,
39-
key: cfg.Key,
40+
cfg: cfg,
4041
}
4142
}
4243

@@ -76,17 +77,17 @@ func (p *OpenAIProvider) CreateInterceptor(w http.ResponseWriter, r *http.Reques
7677
}
7778

7879
if req.Stream {
79-
return NewOpenAIStreamingChatInterception(id, &req, p.baseURL, p.key), nil
80+
return NewOpenAIStreamingChatInterception(id, &req, p.cfg), nil
8081
} else {
81-
return NewOpenAIBlockingChatInterception(id, &req, p.baseURL, p.key), nil
82+
return NewOpenAIBlockingChatInterception(id, &req, p.cfg), nil
8283
}
8384
}
8485

8586
return nil, UnknownRoute
8687
}
8788

8889
func (p *OpenAIProvider) BaseURL() string {
89-
return p.baseURL
90+
return p.cfg.BaseURL
9091
}
9192

9293
func (p *OpenAIProvider) AuthHeader() string {
@@ -98,13 +99,43 @@ func (p *OpenAIProvider) InjectAuthHeader(headers *http.Header) {
9899
headers = &http.Header{}
99100
}
100101

101-
headers.Set(p.AuthHeader(), "Bearer "+p.key)
102+
headers.Set(p.AuthHeader(), "Bearer "+p.cfg.Key)
102103
}
103104

104-
func newOpenAIClient(baseURL, key string) openai.Client {
105+
func newOpenAIClient(cfg ProviderConfig, id string) openai.Client {
105106
var opts []option.RequestOption
106-
opts = append(opts, option.WithAPIKey(key))
107-
opts = append(opts, option.WithBaseURL(baseURL))
107+
opts = append(opts, option.WithAPIKey(cfg.Key))
108+
opts = append(opts, option.WithBaseURL(cfg.BaseURL))
109+
110+
if cfg.EnableUpstreamLogging {
111+
reqLogFile, err := os.OpenFile("/tmp/openai-req.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
112+
if err == nil {
113+
reqLogger := log.New(reqLogFile, "", log.LstdFlags)
114+
115+
resLogFile, err := os.OpenFile("/tmp/openai-res.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
116+
if err == nil {
117+
resLogger := log.New(resLogFile, "", log.LstdFlags)
118+
119+
opts = append(opts, option.WithMiddleware(func(req *http.Request, next option.MiddlewareNext) (*http.Response, error) {
120+
if reqDump, err := httputil.DumpRequest(req, true); err == nil {
121+
reqLogger.Printf("[req] [%s] %s", id, reqDump)
122+
}
123+
124+
resp, err := next(req)
125+
if err != nil {
126+
resLogger.Printf("[res] [%s] Error: %v", id, err)
127+
return resp, err
128+
}
129+
130+
if respDump, err := httputil.DumpResponse(resp, true); err == nil {
131+
resLogger.Printf("[res] [%s] %s", id, respDump)
132+
}
133+
134+
return resp, err
135+
}))
136+
}
137+
}
138+
}
108139

109140
return openai.NewClient(opts...)
110141
}

0 commit comments

Comments
 (0)