Skip to content

Commit 822e4eb

Browse files
refactor anthropic request adapter
1 parent 3029147 commit 822e4eb

2 files changed

Lines changed: 93 additions & 101 deletions

File tree

llms/anthropic/adapter_test.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ func TestRequestAdapter(t *testing.T) {
1616
req := llmberjack.NewUntypedRequest().
1717
WithModel("claude-3-5-sonnet-20241022")
1818

19-
messages, _, err := p.adaptRequest(llm, req, RequestOptions{})
19+
params, err := p.adaptRequest(llm, req, "claude-3-5-sonnet-20241022", RequestOptions{})
2020

2121
assert.Nil(t, err)
22-
assert.NotNil(t, messages)
22+
assert.NotNil(t, params)
23+
assert.NotNil(t, params.Messages)
2324
})
2425

2526
t.Run("with user prompts", func(t *testing.T) {
@@ -28,10 +29,10 @@ func TestRequestAdapter(t *testing.T) {
2829
WithText(llmberjack.RoleUser, "user prompt", "user prompt 2").
2930
WithTextReader(llmberjack.RoleUser, strings.NewReader("user prompt 3"))
3031

31-
messages, _, err := p.adaptRequest(llm, req, RequestOptions{})
32+
params, err := p.adaptRequest(llm, req, "claude-3-5-sonnet-20241022", RequestOptions{})
3233

3334
assert.Nil(t, err)
34-
assert.Greater(t, len(messages), 0)
35+
assert.Greater(t, len(params.Messages), 0)
3536
})
3637
}
3738

llms/anthropic/anthropic.go

Lines changed: 88 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -119,68 +119,12 @@ func (p *Anthropic) ChatCompletion(ctx context.Context, adapter internal.Adapter
119119

120120
opts := internal.CastProviderOptions[RequestOptions](requester.ProviderRequestOptions(p))
121121

122-
messages, params, err := p.adaptRequest(adapter, requester, opts)
122+
params, err := p.adaptRequest(adapter, requester, *model, opts)
123123
if err != nil {
124124
return nil, errors.Wrap(err, "could not adapt request")
125125
}
126126

127-
messageParams := anthropic.MessageNewParams{
128-
Model: *model,
129-
Messages: messages,
130-
}
131-
132-
// Add max tokens only if explicitly provided (Vertex AI requires > 0)
133-
if r.MaxTokens != nil {
134-
messageParams.MaxTokens = int64(*r.MaxTokens)
135-
} else if p.backend == BackendVertexAI {
136-
messageParams.MaxTokens = BackendVertexAiDefaultMaxTokens // Set a default max tokens for Vertex AI if not provided
137-
}
138-
139-
// Add system message
140-
if params.System.Text != "" {
141-
messageParams.System = []anthropic.TextBlockParam{params.System}
142-
}
143-
144-
// Add tools
145-
if len(params.Tools) > 0 {
146-
toolUnions := make([]anthropic.ToolUnionParam, len(params.Tools))
147-
for i, tool := range params.Tools {
148-
toolUnions[i] = anthropic.ToolUnionParamOfTool(
149-
tool.InputSchema,
150-
tool.Name,
151-
)
152-
// Description is already set in tool if provided
153-
toolUnions[i].OfTool.Description = tool.Description
154-
}
155-
messageParams.Tools = toolUnions
156-
}
157-
158-
// Add tool choice
159-
if params.ToolChoice != nil {
160-
messageParams.ToolChoice = *params.ToolChoice
161-
}
162-
163-
// Add temperature
164-
if r.Temperature != nil {
165-
messageParams.Temperature = anthropic.Float(*r.Temperature)
166-
}
167-
168-
// Add top_p
169-
if r.TopP != nil {
170-
messageParams.TopP = anthropic.Float(*r.TopP)
171-
}
172-
173-
// Add top_k
174-
if params.TopK != nil {
175-
messageParams.TopK = anthropic.Int(int64(*params.TopK))
176-
}
177-
178-
// Add thinking
179-
if params.Thinking != nil && *params.Thinking > 0 {
180-
messageParams.Thinking = anthropic.ThinkingConfigParamOfEnabled(int64(*params.Thinking))
181-
}
182-
183-
response, err := p.client.Messages.New(ctx, messageParams)
127+
response, err := p.client.Messages.New(ctx, *params)
184128
if err != nil {
185129
return nil, errors.Wrap(err, "LLM provider failed to generate content")
186130
}
@@ -193,36 +137,25 @@ func (p *Anthropic) ChatCompletion(ctx context.Context, adapter internal.Adapter
193137
return responseAdapter, nil
194138
}
195139

196-
type requestParams struct {
197-
System anthropic.TextBlockParam
198-
Tools []anthropic.ToolParam
199-
ToolChoice *anthropic.ToolChoiceUnionParam
200-
TopK *int
201-
Thinking *int
202-
}
203-
204-
func (p *Anthropic) adaptRequest(_ internal.Adapter, requester llmberjack.Requester, opts RequestOptions) ([]anthropic.MessageParam, *requestParams, error) {
140+
func (p *Anthropic) adaptRequest(_ internal.Adapter, requester llmberjack.Requester, model string, opts RequestOptions) (*anthropic.MessageNewParams, error) {
205141
r := requester.ToRequest()
206142
messages := make([]anthropic.MessageParam, 0, len(r.Messages))
207143

208144
if r.ThreadId != nil {
209145
messages = append(messages, p.history.Load(r.ThreadId)...)
210146
}
211147

212-
params := &requestParams{
213-
Tools: make([]anthropic.ToolParam, 0, len(r.Tools)),
214-
}
215-
216-
// Add tools
148+
// Build tools
149+
tools := make([]anthropic.ToolParam, 0, len(r.Tools))
217150
for _, tool := range r.Tools {
218151
paramsJson, err := json.Marshal(tool.Parameters)
219152
if err != nil {
220-
return nil, nil, errors.Wrap(err, "failed to encode tool parameters")
153+
return nil, errors.Wrap(err, "failed to encode tool parameters")
221154
}
222155

223156
var toolParams map[string]any
224157
if err := json.Unmarshal(paramsJson, &toolParams); err != nil {
225-
return nil, nil, errors.Wrap(err, "failed to decode tool parameters")
158+
return nil, errors.Wrap(err, "failed to decode tool parameters")
226159
}
227160

228161
toolParam := anthropic.ToolParam{
@@ -235,37 +168,44 @@ func (p *Anthropic) adaptRequest(_ internal.Adapter, requester llmberjack.Reques
235168
if tool.Description != "" {
236169
toolParam.Description = anthropic.String(tool.Description)
237170
}
238-
params.Tools = append(params.Tools, toolParam)
171+
tools = append(tools, toolParam)
239172
}
240173

241-
// System messages are handled as separate messages with RoleSystem
242-
243-
// Resolve thinking configuration:
244-
// - WithThinking(false) explicitly disables thinking
245-
// - WithThinking(true) enables with BudgetTokens if set, otherwise default
246-
// - BudgetTokens alone (without WithThinking) enables thinking
247-
if r.Thinking != nil && !*r.Thinking {
248-
// Explicitly disabled, do not set thinking
249-
} else if opts.BudgetTokens != nil && *opts.BudgetTokens > 0 {
250-
params.Thinking = opts.BudgetTokens
251-
} else if r.Thinking != nil && *r.Thinking {
252-
params.Thinking = lo.ToPtr(DefaultThinkingBudgetTokens)
174+
// Handle system messages
175+
var systemText string
176+
for _, msg := range r.Messages {
177+
if msg.Role == llmberjack.RoleSystem {
178+
for _, part := range msg.Parts {
179+
if seeker, ok := part.(io.ReadSeeker); ok {
180+
seeker.Seek(0, io.SeekStart)
181+
}
182+
buf, _ := io.ReadAll(part)
183+
if systemText != "" {
184+
systemText += "\n"
185+
}
186+
systemText += string(buf)
187+
}
188+
}
253189
}
254190

255191
// Process messages
256192
for _, msg := range r.Messages {
193+
if msg.Role == llmberjack.RoleSystem {
194+
continue // Already handled above
195+
}
196+
257197
parts := make([]anthropic.ContentBlockParamUnion, 0, len(msg.Parts))
258198

259199
for _, part := range msg.Parts {
260200
if seeker, ok := part.(io.ReadSeeker); ok {
261201
if _, err := seeker.Seek(0, io.SeekStart); err != nil {
262-
return nil, nil, err
202+
return nil, err
263203
}
264204
}
265205

266206
buf, err := io.ReadAll(part)
267207
if err != nil {
268-
return nil, nil, errors.Wrap(err, "could not read content part")
208+
return nil, errors.Wrap(err, "could not read content part")
269209
}
270210

271211
switch msg.Type {
@@ -289,26 +229,22 @@ func (p *Anthropic) adaptRequest(_ internal.Adapter, requester llmberjack.Reques
289229
Content: parts,
290230
}
291231

292-
case llmberjack.RoleSystem:
293-
// System messages are handled separately in Anthropic SDK
294-
continue
295-
296232
case llmberjack.RoleTool:
297233
if msg.Tool == nil {
298-
return nil, nil, errors.New("sent a tool response when no tool was invoked")
234+
return nil, errors.New("sent a tool response when no tool was invoked")
299235
}
300236

301237
var toolContent string
302238
for _, part := range msg.Parts {
303239
if seeker, ok := part.(io.ReadSeeker); ok {
304240
_, err := seeker.Seek(0, io.SeekStart)
305241
if err != nil {
306-
return nil, nil, errors.Wrap(err, "could not seek content part")
242+
return nil, errors.Wrap(err, "could not seek content part")
307243
}
308244
}
309245
buf, err := io.ReadAll(part)
310246
if err != nil {
311-
return nil, nil, errors.Wrap(err, "could not read content part")
247+
return nil, errors.Wrap(err, "could not read content part")
312248
}
313249
toolContent = string(buf)
314250
}
@@ -328,7 +264,62 @@ func (p *Anthropic) adaptRequest(_ internal.Adapter, requester llmberjack.Reques
328264
messages = append(messages, msgParam)
329265
}
330266

331-
return messages, params, nil
267+
// Build the complete MessageNewParams
268+
params := &anthropic.MessageNewParams{
269+
Model: model,
270+
Messages: messages,
271+
}
272+
273+
// Add system message if present
274+
if systemText != "" {
275+
params.System = []anthropic.TextBlockParam{
276+
{Text: systemText},
277+
}
278+
}
279+
280+
// Add tools if present
281+
if len(tools) > 0 {
282+
toolUnions := make([]anthropic.ToolUnionParam, len(tools))
283+
for i, tool := range tools {
284+
toolUnions[i] = anthropic.ToolUnionParamOfTool(
285+
tool.InputSchema,
286+
tool.Name,
287+
)
288+
toolUnions[i].OfTool.Description = tool.Description
289+
}
290+
params.Tools = toolUnions
291+
}
292+
293+
// Add max tokens only if explicitly provided (Vertex AI requires > 0)
294+
if r.MaxTokens != nil {
295+
params.MaxTokens = int64(*r.MaxTokens)
296+
} else if p.backend == BackendVertexAI {
297+
params.MaxTokens = BackendVertexAiDefaultMaxTokens
298+
}
299+
300+
// Add temperature
301+
if r.Temperature != nil {
302+
params.Temperature = anthropic.Float(*r.Temperature)
303+
}
304+
305+
// Add top_p
306+
if r.TopP != nil {
307+
params.TopP = anthropic.Float(*r.TopP)
308+
}
309+
310+
// Resolve thinking configuration
311+
if r.Thinking != nil && !*r.Thinking {
312+
// Explicitly disabled, do not set thinking
313+
disabled := anthropic.NewThinkingConfigDisabledParam()
314+
params.Thinking = anthropic.ThinkingConfigParamUnion{OfDisabled: &disabled}
315+
} else if opts.BudgetTokens != nil && *opts.BudgetTokens > 0 {
316+
params.Thinking = anthropic.ThinkingConfigParamOfEnabled(int64(*opts.BudgetTokens))
317+
} else if r.Thinking != nil && *r.Thinking {
318+
adaptative := anthropic.ThinkingConfigAdaptiveParam{Display: "summarized", Type: "adaptive"}
319+
params.Thinking = anthropic.ThinkingConfigParamUnion{OfAdaptive: &adaptative}
320+
}
321+
322+
return params, nil
332323
}
333324

334325
func (p *Anthropic) adaptResponse(_ internal.Adapter, response *anthropic.Message, requester llmberjack.Requester) (*llmberjack.InnerResponse, error) {

0 commit comments

Comments
 (0)