feat: Phase 2 — chat templates, reasoning parser, collapsible TUI, mouse support
- internal/llama/template.go: ChatML/Llama3 template engine with stop tokens - internal/agent/reasoning.go: parse <think>/<reasoning> tags from LLM output - companion/promptbuilder.go: output structured BuildResult, add date/time context - agent/agent.go: wire templates + reasoning split into message loop - storage: add reasoning TEXT column to messages table + migration - channel/tui.go: collapsible reasoning (tab toggle), mouse scroll, polished dimmed style - config: CHAT_TEMPLATE env var (chatml|llama3|none) - plan.md: mark Phase 2 complete, add themes TODO
This commit is contained in:
@@ -72,7 +72,7 @@ func (a *Agent) HandleMessage(ctx context.Context, content string) (string, erro
|
||||
slog.Warn("memory search failed", "error", err)
|
||||
}
|
||||
|
||||
prompt, err := a.companion.BuildPrompt(ctx, &companion.PromptInput{
|
||||
buildResult, err := a.companion.BuildPrompt(ctx, &companion.PromptInput{
|
||||
UserMessage: content,
|
||||
RecentMessages: a.messages,
|
||||
Memories: memories,
|
||||
@@ -81,11 +81,30 @@ func (a *Agent) HandleMessage(ctx context.Context, content string) (string, erro
|
||||
return "", fmt.Errorf("build prompt: %w", err)
|
||||
}
|
||||
|
||||
segments := []types.PromptSegment{
|
||||
{Type: types.SegSystem, Content: buildResult.SystemContent},
|
||||
}
|
||||
start := 0
|
||||
if len(a.messages) > 10 {
|
||||
start = len(a.messages) - 10
|
||||
}
|
||||
for _, msg := range a.messages[start:] {
|
||||
segType := types.SegUser
|
||||
if msg.Role == types.RoleAssistant {
|
||||
segType = types.SegAssistant
|
||||
}
|
||||
segments = append(segments, types.PromptSegment{Type: segType, Content: msg.Content})
|
||||
}
|
||||
|
||||
tmpl := llama.NewTemplateEngine(a.cfg.ChatTemplate)
|
||||
prompt := tmpl.Build(segments)
|
||||
|
||||
resp, err := a.llm.Complete(&llama.CompletionRequest{
|
||||
Prompt: prompt,
|
||||
Temperature: a.cfg.Temperature,
|
||||
TopP: a.cfg.TopP,
|
||||
MaxTokens: a.cfg.MaxTokens,
|
||||
Stop: tmpl.StopTokens(),
|
||||
Stream: false,
|
||||
CachePrompt: true,
|
||||
})
|
||||
@@ -93,10 +112,13 @@ func (a *Agent) HandleMessage(ctx context.Context, content string) (string, erro
|
||||
return "", fmt.Errorf("llm complete: %w", err)
|
||||
}
|
||||
|
||||
parsed := ParseResponse(resp.Content)
|
||||
|
||||
assistantMsg := types.Message{
|
||||
ID: fmt.Sprintf("%x", time.Now().UnixNano()),
|
||||
Role: types.RoleAssistant,
|
||||
Content: resp.Content,
|
||||
Content: parsed.Content,
|
||||
Reasoning: parsed.Reasoning,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
a.messages = append(a.messages, assistantMsg)
|
||||
@@ -108,7 +130,7 @@ func (a *Agent) HandleMessage(ctx context.Context, content string) (string, erro
|
||||
}
|
||||
}()
|
||||
|
||||
return resp.Content, nil
|
||||
return parsed.Content, nil
|
||||
}
|
||||
|
||||
func (a *Agent) Messages() []types.Message {
|
||||
@@ -130,8 +152,8 @@ func (a *Agent) saveMessage(msg types.Message) {
|
||||
toolCallJSON = string(b)
|
||||
}
|
||||
err := a.store.Exec(
|
||||
"INSERT INTO messages (id, conversation_id, role, content, tool_call, created_at) VALUES (?, ?, ?, ?, ?, ?)",
|
||||
msg.ID, a.conversationID, string(msg.Role), msg.Content, toolCallJSON, msg.CreatedAt.Format(time.RFC3339),
|
||||
"INSERT INTO messages (id, conversation_id, role, content, reasoning, tool_call, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
||||
msg.ID, a.conversationID, string(msg.Role), msg.Content, msg.Reasoning, toolCallJSON, msg.CreatedAt.Format(time.RFC3339),
|
||||
)
|
||||
if err != nil {
|
||||
slog.Warn("failed to save message", "error", err)
|
||||
|
||||
Reference in New Issue
Block a user