Compare commits

..

5 Commits

Author SHA1 Message Date
3f765234de tui: ability to cancel request in flight 2024-03-12 20:41:34 +00:00
21411c2732 tui: add focus switching between input/messages view 2024-03-12 20:40:49 +00:00
99794addee tui: removed confirm before send, dynamic footer
footer now rendered based on model data, instead of being set to a fixed
string
2024-03-12 20:40:49 +00:00
a47c1a76b4 tui: use ctx chroma highlighter 2024-03-12 20:40:49 +00:00
96fdae982e Add initial TUI 2024-03-12 20:40:44 +00:00
2 changed files with 13 additions and 61 deletions

View File

@ -11,7 +11,6 @@ import (
"strings" "strings"
"git.mlow.ca/mlow/lmcli/pkg/lmcli/model" "git.mlow.ca/mlow/lmcli/pkg/lmcli/model"
"git.mlow.ca/mlow/lmcli/pkg/lmcli/provider"
"git.mlow.ca/mlow/lmcli/pkg/lmcli/tools" "git.mlow.ca/mlow/lmcli/pkg/lmcli/tools"
) )
@ -21,7 +20,7 @@ type AnthropicClient struct {
type Message struct { type Message struct {
Role string `json:"role"` Role string `json:"role"`
Content string `json:"content"` OriginalContent string `json:"content"`
} }
type Request struct { type Request struct {
@ -42,10 +41,10 @@ type OriginalContent struct {
} }
type Response struct { type Response struct {
Id string `json:"id"` Id string `json:"id"`
Type string `json:"type"` Type string `json:"type"`
Role string `json:"role"` Role string `json:"role"`
Content []OriginalContent `json:"content"` OriginalContent []OriginalContent `json:"content"`
} }
const FUNCTION_STOP_SEQUENCE = "</function_calls>" const FUNCTION_STOP_SEQUENCE = "</function_calls>"
@ -66,7 +65,7 @@ func buildRequest(params model.RequestParameters, messages []model.Message) Requ
} }
startIdx := 0 startIdx := 0
if len(messages) > 0 && messages[0].Role == model.MessageRoleSystem { if messages[0].Role == model.MessageRoleSystem {
requestBody.System = messages[0].Content requestBody.System = messages[0].Content
requestBody.Messages = requestBody.Messages[:len(messages)-1] requestBody.Messages = requestBody.Messages[:len(messages)-1]
startIdx = 1 startIdx = 1
@ -86,15 +85,8 @@ func buildRequest(params model.RequestParameters, messages []model.Message) Requ
switch msg.Role { switch msg.Role {
case model.MessageRoleToolCall: case model.MessageRoleToolCall:
message.Role = "assistant" message.Role = "assistant"
if msg.Content != "" { message.OriginalContent = msg.Content
message.Content = msg.Content //message.ToolCalls = convertToolCallToOpenAI(m.ToolCalls)
}
xmlFuncCalls := convertToolCallsToXMLFunctionCalls(msg.ToolCalls)
xmlString, err := xmlFuncCalls.XMLString()
if err != nil {
panic("Could not serialize []ToolCall to XMLFunctionCall")
}
message.Content += xmlString
case model.MessageRoleToolResult: case model.MessageRoleToolResult:
xmlFuncResults := convertToolResultsToXMLFunctionResult(msg.ToolResults) xmlFuncResults := convertToolResultsToXMLFunctionResult(msg.ToolResults)
xmlString, err := xmlFuncResults.XMLString() xmlString, err := xmlFuncResults.XMLString()
@ -102,10 +94,10 @@ func buildRequest(params model.RequestParameters, messages []model.Message) Requ
panic("Could not serialize []ToolResult to XMLFunctionResults") panic("Could not serialize []ToolResult to XMLFunctionResults")
} }
message.Role = "user" message.Role = "user"
message.Content = xmlString message.OriginalContent = xmlString
default: default:
message.Role = string(msg.Role) message.Role = string(msg.Role)
message.Content = msg.Content message.OriginalContent = msg.Content
} }
} }
return requestBody return requestBody
@ -158,7 +150,7 @@ func (c *AnthropicClient) CreateChatCompletion(
} }
sb := strings.Builder{} sb := strings.Builder{}
for _, content := range response.Content { for _, content := range response.OriginalContent {
var reply model.Message var reply model.Message
switch content.Type { switch content.Type {
case "text": case "text":
@ -286,9 +278,8 @@ func (c *AnthropicClient) CreateChatCompletionStream(
// Execute function calls // Execute function calls
toolCall := model.Message{ toolCall := model.Message{
Role: model.MessageRoleToolCall, Role: model.MessageRoleToolCall,
// xml stripped from content Content: content,
Content: content[:start],
ToolCalls: convertXMLFunctionCallsToToolCalls(functionCalls), ToolCalls: convertXMLFunctionCallsToToolCalls(functionCalls),
} }

View File

@ -2,7 +2,6 @@ package anthropic
import ( import (
"bytes" "bytes"
"fmt"
"strings" "strings"
"text/template" "text/template"
@ -115,25 +114,6 @@ func convertXMLFunctionCallsToToolCalls(functionCalls XMLFunctionCalls) []model.
return toolCalls return toolCalls
} }
func convertToolCallsToXMLFunctionCalls(toolCalls []model.ToolCall) XMLFunctionCalls {
converted := make([]XMLFunctionInvoke, len(toolCalls))
for i, toolCall := range toolCalls {
var params XMLFunctionInvokeParameters
var paramXML string
for key, value := range toolCall.Parameters {
paramXML += fmt.Sprintf("<%s>%v</%s>\n", key, value, key)
}
params.String = paramXML
converted[i] = XMLFunctionInvoke{
ToolName: toolCall.Name,
Parameters: params,
}
}
return XMLFunctionCalls{
Invoke: converted,
}
}
func convertToolResultsToXMLFunctionResult(toolResults []model.ToolResult) XMLFunctionResults { func convertToolResultsToXMLFunctionResult(toolResults []model.ToolResult) XMLFunctionResults {
converted := make([]XMLFunctionResult, len(toolResults)) converted := make([]XMLFunctionResult, len(toolResults))
for i, result := range toolResults { for i, result := range toolResults {
@ -200,22 +180,3 @@ func (x XMLFunctionResults) XMLString() (string, error) {
return buf.String(), nil return buf.String(), nil
} }
func (x XMLFunctionCalls) XMLString() (string, error) {
tmpl, err := template.New("function_calls").Parse(`<function_calls>
{{range .Invoke}}<invoke>
<tool_name>{{.ToolName}}</tool_name>
<parameters>{{.Parameters.String}}</parameters>
</invoke>
{{end}}</function_calls>`)
if err != nil {
return "", err
}
var buf bytes.Buffer
if err := tmpl.Execute(&buf, x); err != nil {
return "", err
}
return buf.String(), nil
}