feat(llm): add ChatTemplateKwargs to CompletionRequest for provider-specific template params
- Add ChatTemplateKwargs field to llm.CompletionRequest - Propagate kwargs through agent loop in both Run() and RunStream() - Pass kwargs to llama.cpp client chat request - Fix tool schema marshaling to include type/function wrapper - Fix stream indentation logic in RunStream with responseBuilder - Remove indirect marker from uuid dependency
This commit is contained in:
parent
0054ca793c
commit
1a8f1557f6
4 changed files with 58 additions and 42 deletions
2
go.mod
2
go.mod
|
|
@ -4,4 +4,4 @@ go 1.26
|
||||||
|
|
||||||
require gopkg.in/yaml.v3 v3.0.1
|
require gopkg.in/yaml.v3 v3.0.1
|
||||||
|
|
||||||
require github.com/google/uuid v1.6.0 // indirect
|
require github.com/google/uuid v1.6.0
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"iter"
|
"iter"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/VictorVargas/rony-llm-agent/pkg/llm"
|
"github.com/VictorVargas/rony-llm-agent/pkg/llm"
|
||||||
|
|
@ -32,14 +33,15 @@ type OnIterationHook func(Iteration)
|
||||||
|
|
||||||
// Config holds the dependencies and settings for the agent loop.
|
// Config holds the dependencies and settings for the agent loop.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
LLM llm.LLMClient
|
LLM llm.LLMClient
|
||||||
Persona persona.Persona
|
Persona persona.Persona
|
||||||
Tools tools.Registry
|
Tools tools.Registry
|
||||||
Sandbox Sandbox
|
Sandbox Sandbox
|
||||||
MaxIters int
|
MaxIters int
|
||||||
Approver Approver
|
Approver Approver
|
||||||
OnIteration OnIterationHook
|
OnIteration OnIterationHook
|
||||||
ToolTimeout time.Duration
|
ToolTimeout time.Duration
|
||||||
|
ChatTemplateKwargs map[string]any // passed to the LLM provider (e.g. Qwen enable_thinking)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iteration represents a single cycle of the agent loop.
|
// Iteration represents a single cycle of the agent loop.
|
||||||
|
|
@ -86,8 +88,9 @@ func (l *Loop) Run(ctx context.Context, input string) (Response, error) {
|
||||||
iterations++
|
iterations++
|
||||||
|
|
||||||
resp, err := l.cfg.LLM.Generate(ctx, llm.CompletionRequest{
|
resp, err := l.cfg.LLM.Generate(ctx, llm.CompletionRequest{
|
||||||
Messages: messages,
|
Messages: messages,
|
||||||
Tools: l.getToolSchemas(),
|
Tools: l.getToolSchemas(),
|
||||||
|
ChatTemplateKwargs: l.cfg.ChatTemplateKwargs,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Response{}, fmt.Errorf("LLM generate failed: %w", err)
|
return Response{}, fmt.Errorf("LLM generate failed: %w", err)
|
||||||
|
|
@ -143,11 +146,13 @@ func (l *Loop) RunStream(ctx context.Context, input string) iter.Seq2[llm.Stream
|
||||||
iterations++
|
iterations++
|
||||||
|
|
||||||
stream := l.cfg.LLM.Stream(ctx, llm.CompletionRequest{
|
stream := l.cfg.LLM.Stream(ctx, llm.CompletionRequest{
|
||||||
Messages: messages,
|
Messages: messages,
|
||||||
Tools: l.getToolSchemas(),
|
Tools: l.getToolSchemas(),
|
||||||
|
ChatTemplateKwargs: l.cfg.ChatTemplateKwargs,
|
||||||
})
|
})
|
||||||
|
|
||||||
var hasToolCalls bool
|
var hasToolCalls bool
|
||||||
|
var responseBuilder strings.Builder
|
||||||
for chunk, err := range stream {
|
for chunk, err := range stream {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
yield(llm.StreamChunk{}, err)
|
yield(llm.StreamChunk{}, err)
|
||||||
|
|
@ -172,21 +177,20 @@ func (l *Loop) RunStream(ctx context.Context, input string) iter.Seq2[llm.Stream
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !hasToolCalls && chunk.Delta != "" {
|
if !hasToolCalls && chunk.Delta != "" {
|
||||||
if !yield(chunk, nil) {
|
responseBuilder.WriteString(chunk.Delta)
|
||||||
return
|
if !yield(chunk, nil) {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if !hasToolCalls {
|
if !hasToolCalls {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if iterations >= l.cfg.MaxIters {
|
yield(llm.StreamChunk{}, fmt.Errorf("max iterations (%d) reached", l.cfg.MaxIters))
|
||||||
yield(llm.StreamChunk{}, fmt.Errorf("max iterations (%d) reached", l.cfg.MaxIters))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -201,7 +205,16 @@ func (l *Loop) buildInitialMessages(input string) []llm.Message {
|
||||||
func (l *Loop) getToolSchemas() []json.RawMessage {
|
func (l *Loop) getToolSchemas() []json.RawMessage {
|
||||||
var schemas []json.RawMessage
|
var schemas []json.RawMessage
|
||||||
for _, tool := range l.cfg.Tools.List() {
|
for _, tool := range l.cfg.Tools.List() {
|
||||||
schemas = append(schemas, tool.InputSchema)
|
def := map[string]interface{}{
|
||||||
|
"type": "function",
|
||||||
|
"function": map[string]interface{}{
|
||||||
|
"name": tool.Name,
|
||||||
|
"description": tool.Description,
|
||||||
|
"parameters": json.RawMessage(tool.InputSchema),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
data, _ := json.Marshal(def)
|
||||||
|
schemas = append(schemas, data)
|
||||||
}
|
}
|
||||||
return schemas
|
return schemas
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -173,9 +173,10 @@ func (c *Client) buildRequest(req llm.CompletionRequest, stream bool) (io.Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
openReq := llamaChatRequest{
|
openReq := llamaChatRequest{
|
||||||
Model: req.Model,
|
Model: req.Model,
|
||||||
Messages: messages,
|
Messages: messages,
|
||||||
Stream: stream,
|
Stream: stream,
|
||||||
|
ChatTemplateKwargs: req.ChatTemplateKwargs,
|
||||||
}
|
}
|
||||||
if len(tools) > 0 {
|
if len(tools) > 0 {
|
||||||
openReq.Tools = tools
|
openReq.Tools = tools
|
||||||
|
|
@ -232,16 +233,17 @@ func (c *Client) toResponse(resp llamaChatResponse) llm.CompletionResponse {
|
||||||
// llama.cpp API types
|
// llama.cpp API types
|
||||||
|
|
||||||
type llamaChatRequest struct {
|
type llamaChatRequest struct {
|
||||||
Model string `json:"model"`
|
Model string `json:"model"`
|
||||||
Messages []llamaMessage `json:"messages"`
|
Messages []llamaMessage `json:"messages"`
|
||||||
Tools []llamaTool `json:"tools,omitempty"`
|
Tools []llamaTool `json:"tools,omitempty"`
|
||||||
ToolChoice interface{} `json:"tool_choice,omitempty"`
|
ToolChoice interface{} `json:"tool_choice,omitempty"`
|
||||||
Temperature float32 `json:"temperature,omitempty"`
|
Temperature float32 `json:"temperature,omitempty"`
|
||||||
MaxTokens int `json:"max_tokens,omitempty"`
|
MaxTokens int `json:"max_tokens,omitempty"`
|
||||||
TopK int `json:"top_k,omitempty"`
|
TopK int `json:"top_k,omitempty"`
|
||||||
TopP float32 `json:"top_p,omitempty"`
|
TopP float32 `json:"top_p,omitempty"`
|
||||||
Stop []string `json:"stop,omitempty"`
|
Stop []string `json:"stop,omitempty"`
|
||||||
Stream bool `json:"stream"`
|
Stream bool `json:"stream"`
|
||||||
|
ChatTemplateKwargs map[string]any `json:"chat_template_kwargs,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type llamaMessage struct {
|
type llamaMessage struct {
|
||||||
|
|
|
||||||
|
|
@ -66,14 +66,15 @@ func (t *ToolRef) MarshalJSON() ([]byte, error) {
|
||||||
|
|
||||||
// CompletionRequest is sent to an LLM provider.
|
// CompletionRequest is sent to an LLM provider.
|
||||||
type CompletionRequest struct {
|
type CompletionRequest struct {
|
||||||
Model string `json:"model"`
|
Model string `json:"model"`
|
||||||
Messages []Message `json:"messages"`
|
Messages []Message `json:"messages"`
|
||||||
Tools []json.RawMessage `json:"tools,omitempty"`
|
Tools []json.RawMessage `json:"tools,omitempty"`
|
||||||
ToolChoice interface{} `json:"tool_choice,omitempty"` // ToolChoice, ToolRef, or null
|
ToolChoice interface{} `json:"tool_choice,omitempty"` // ToolChoice, ToolRef, or null
|
||||||
Temperature *float32 `json:"temperature,omitempty"`
|
Temperature *float32 `json:"temperature,omitempty"`
|
||||||
MaxTokens *int `json:"max_tokens,omitempty"`
|
MaxTokens *int `json:"max_tokens,omitempty"`
|
||||||
Stop []string `json:"stop,omitempty"`
|
Stop []string `json:"stop,omitempty"`
|
||||||
Metadata map[string]string `json:"metadata,omitempty"`
|
Metadata map[string]string `json:"metadata,omitempty"`
|
||||||
|
ChatTemplateKwargs map[string]any `json:"chat_template_kwargs,omitempty"` // model-specific chat template params, e.g. Qwen enable_thinking
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompletionResponse is returned from an LLM provider.
|
// CompletionResponse is returned from an LLM provider.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue