Merge pull request #2 from VictorVargas/feat/consoel-buffer
feat: add history messages, reasoning content, and adaptive language support
This commit is contained in:
commit
2cde90d8f7
8 changed files with 46 additions and 31 deletions
|
|
@ -206,8 +206,8 @@ Bucle iterativo entre LLM y ejecución de tools. Es el "cerebro" que orquesta to
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type Loop interface {
|
type Loop interface {
|
||||||
Run(ctx context.Context, input string) (Response, error)
|
Run(ctx context.Context, input string, history ...Message) (Response, error)
|
||||||
RunStream(ctx context.Context, input string) iter.Seq2[Chunk, error]
|
RunStream(ctx context.Context, input string, history ...Message) iter.Seq2[Chunk, error]
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
|
|
||||||
|
|
@ -206,8 +206,8 @@ Iterative loop between LLM and tool execution. It's the "brain" that orchestrate
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type Loop interface {
|
type Loop interface {
|
||||||
Run(ctx context.Context, input string) (Response, error)
|
Run(ctx context.Context, input string, history ...Message) (Response, error)
|
||||||
RunStream(ctx context.Context, input string) iter.Seq2[Chunk, error]
|
RunStream(ctx context.Context, input string, history ...Message) iter.Seq2[Chunk, error]
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,8 @@ while iteration < MaxIterations:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type Loop interface {
|
type Loop interface {
|
||||||
Run(ctx context.Context, input string) (Response, error)
|
Run(ctx context.Context, input string, history ...llm.Message) (Response, error)
|
||||||
RunStream(ctx context.Context, input string) iter.Seq2[Chunk, error]
|
RunStream(ctx context.Context, input string, history ...llm.Message) iter.Seq2[Chunk, error]
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,8 @@ while iteration < MaxIterations:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type Loop interface {
|
type Loop interface {
|
||||||
Run(ctx context.Context, input string) (Response, error)
|
Run(ctx context.Context, input string, history ...llm.Message) (Response, error)
|
||||||
RunStream(ctx context.Context, input string) iter.Seq2[Chunk, error]
|
RunStream(ctx context.Context, input string, history ...llm.Message) iter.Seq2[Chunk, error]
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
|
|
||||||
|
|
@ -75,10 +75,12 @@ func New(cfg Config) *Loop {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run executes the agent loop and returns the final response.
|
// Run executes the agent loop and returns the final response.
|
||||||
func (l *Loop) Run(ctx context.Context, input string) (Response, error) {
|
// Optional history messages are appended after the system prompt and before
|
||||||
|
// the new user input.
|
||||||
|
func (l *Loop) Run(ctx context.Context, input string, history ...llm.Message) (Response, error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
messages := l.buildInitialMessages(input)
|
messages := l.buildInitialMessages(input, history)
|
||||||
var finalContent string
|
var finalContent string
|
||||||
var allToolCalls []llm.ToolCall
|
var allToolCalls []llm.ToolCall
|
||||||
var totalUsage llm.TokenUsage
|
var totalUsage llm.TokenUsage
|
||||||
|
|
@ -137,9 +139,11 @@ func (l *Loop) Run(ctx context.Context, input string) (Response, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunStream executes the agent loop with streaming output.
|
// RunStream executes the agent loop with streaming output.
|
||||||
func (l *Loop) RunStream(ctx context.Context, input string) iter.Seq2[llm.StreamChunk, error] {
|
// Optional history messages are appended after the system prompt and before
|
||||||
|
// the new user input.
|
||||||
|
func (l *Loop) RunStream(ctx context.Context, input string, history ...llm.Message) iter.Seq2[llm.StreamChunk, error] {
|
||||||
return func(yield func(llm.StreamChunk, error) bool) {
|
return func(yield func(llm.StreamChunk, error) bool) {
|
||||||
messages := l.buildInitialMessages(input)
|
messages := l.buildInitialMessages(input, history)
|
||||||
iterations := 0
|
iterations := 0
|
||||||
|
|
||||||
for iterations < l.cfg.MaxIters {
|
for iterations < l.cfg.MaxIters {
|
||||||
|
|
@ -177,7 +181,7 @@ func (l *Loop) RunStream(ctx context.Context, input string) iter.Seq2[llm.Stream
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !hasToolCalls && chunk.Delta != "" {
|
if !hasToolCalls && (chunk.Delta != "" || chunk.ReasoningDelta != "") {
|
||||||
responseBuilder.WriteString(chunk.Delta)
|
responseBuilder.WriteString(chunk.Delta)
|
||||||
if !yield(chunk, nil) {
|
if !yield(chunk, nil) {
|
||||||
return
|
return
|
||||||
|
|
@ -194,12 +198,13 @@ func (l *Loop) RunStream(ctx context.Context, input string) iter.Seq2[llm.Stream
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Loop) buildInitialMessages(input string) []llm.Message {
|
func (l *Loop) buildInitialMessages(input string, history []llm.Message) []llm.Message {
|
||||||
systemPrompt := persona.AssembleSystemPrompt(l.cfg.Persona, "")
|
systemPrompt := persona.AssembleSystemPrompt(l.cfg.Persona, "")
|
||||||
return []llm.Message{
|
messages := make([]llm.Message, 0, len(history)+2)
|
||||||
{Role: llm.RoleSystem, Content: systemPrompt},
|
messages = append(messages, llm.Message{Role: llm.RoleSystem, Content: systemPrompt})
|
||||||
{Role: llm.RoleUser, Content: input},
|
messages = append(messages, history...)
|
||||||
}
|
messages = append(messages, llm.Message{Role: llm.RoleUser, Content: input})
|
||||||
|
return messages
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Loop) getToolSchemas() []json.RawMessage {
|
func (l *Loop) getToolSchemas() []json.RawMessage {
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,8 @@ func (c *Client) Stream(ctx context.Context, req llm.CompletionRequest) iter.Seq
|
||||||
|
|
||||||
for _, choice := range event.Choices {
|
for _, choice := range event.Choices {
|
||||||
chunk := llm.StreamChunk{
|
chunk := llm.StreamChunk{
|
||||||
Delta: choice.Delta.Content,
|
Delta: choice.Delta.Content,
|
||||||
|
ReasoningDelta: choice.Delta.ReasoningContent,
|
||||||
}
|
}
|
||||||
if choice.FinishReason != "" {
|
if choice.FinishReason != "" {
|
||||||
chunk.FinishReason = choice.FinishReason
|
chunk.FinishReason = choice.FinishReason
|
||||||
|
|
@ -210,6 +211,7 @@ func (c *Client) toResponse(resp llamaChatResponse) llm.CompletionResponse {
|
||||||
ID: resp.ID,
|
ID: resp.ID,
|
||||||
Model: resp.Model,
|
Model: resp.Model,
|
||||||
Content: choice.Message.Content,
|
Content: choice.Message.Content,
|
||||||
|
Reasoning: choice.Message.ReasoningContent,
|
||||||
StopReason: choice.FinishReason,
|
StopReason: choice.FinishReason,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -270,9 +272,10 @@ type llamaChoice struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type llamaMessageResult struct {
|
type llamaMessageResult struct {
|
||||||
Role string `json:"role"`
|
Role string `json:"role"`
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
ToolCalls []llamaToolCall `json:"tool_calls"`
|
ReasoningContent string `json:"reasoning_content"`
|
||||||
|
ToolCalls []llamaToolCall `json:"tool_calls"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type llamaToolCall struct {
|
type llamaToolCall struct {
|
||||||
|
|
@ -306,9 +309,10 @@ type llamaStreamChoice struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type llamaStreamDelta struct {
|
type llamaStreamDelta struct {
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
Role string `json:"role"`
|
ReasoningContent string `json:"reasoning_content"`
|
||||||
ToolCalls []llamaStreamToolCall `json:"tool_calls"`
|
Role string `json:"role"`
|
||||||
|
ToolCalls []llamaStreamToolCall `json:"tool_calls"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type llamaStreamToolCall struct {
|
type llamaStreamToolCall struct {
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,7 @@ type CompletionResponse struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Model string `json:"model"`
|
Model string `json:"model"`
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
|
Reasoning string `json:"reasoning,omitempty"`
|
||||||
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
|
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
|
||||||
StopReason string `json:"stop_reason"`
|
StopReason string `json:"stop_reason"`
|
||||||
Usage TokenUsage `json:"usage,omitempty"`
|
Usage TokenUsage `json:"usage,omitempty"`
|
||||||
|
|
@ -105,10 +106,11 @@ type ToolCall struct {
|
||||||
|
|
||||||
// StreamChunk is emitted by the iterator returned from Stream().
|
// StreamChunk is emitted by the iterator returned from Stream().
|
||||||
type StreamChunk struct {
|
type StreamChunk struct {
|
||||||
Delta string `json:"delta"`
|
Delta string `json:"delta"`
|
||||||
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
|
ReasoningDelta string `json:"reasoning_delta,omitempty"`
|
||||||
FinishReason string `json:"finish_reason,omitempty"`
|
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
|
||||||
Usage TokenUsage `json:"usage,omitempty"` // only present on final chunk
|
FinishReason string `json:"finish_reason,omitempty"`
|
||||||
|
Usage TokenUsage `json:"usage,omitempty"` // only present on final chunk
|
||||||
}
|
}
|
||||||
|
|
||||||
// LLMClient is the interface that every provider implements.
|
// LLMClient is the interface that every provider implements.
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ func DefaultPersona() Persona {
|
||||||
Name: "Rony",
|
Name: "Rony",
|
||||||
Tone: "professional and helpful",
|
Tone: "professional and helpful",
|
||||||
Style: "clear and concise",
|
Style: "clear and concise",
|
||||||
Language: "en",
|
Language: "the user's language",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,7 +59,11 @@ func AssembleSystemPrompt(p Persona, agentsMD string) string {
|
||||||
parts = append(parts, fmt.Sprintf("Write in a %s style.", p.Style))
|
parts = append(parts, fmt.Sprintf("Write in a %s style.", p.Style))
|
||||||
}
|
}
|
||||||
if p.Language != "" {
|
if p.Language != "" {
|
||||||
parts = append(parts, fmt.Sprintf("Respond in %s.", p.Language))
|
if strings.EqualFold(p.Language, "the user's language") {
|
||||||
|
parts = append(parts, "Respond in the same language as the user's messages.")
|
||||||
|
} else {
|
||||||
|
parts = append(parts, fmt.Sprintf("Respond in %s.", p.Language))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for _, c := range p.Constraints {
|
for _, c := range p.Constraints {
|
||||||
parts = append(parts, fmt.Sprintf("CONSTRAINT: %s", c))
|
parts = append(parts, fmt.Sprintf("CONSTRAINT: %s", c))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue