package anthropic import ( "bytes" "fmt" "strings" "text/template" "git.mlow.ca/mlow/lmcli/pkg/api" ) const FUNCTION_STOP_SEQUENCE = "" const TOOL_PREAMBLE = `You have access to the following tools when replying. You may call them like this: $TOOL_NAME <$PARAMETER_NAME>$PARAMETER_VALUE ... Here are the tools available:` const TOOL_PREAMBLE_FOOTER = `Recognize the utility of these tools in a broad range of different applications, and the power they give you to solve a wide range of different problems. However, ensure that the tools are used judiciously and only when clearly relevant to the user's request. Specifically: 1. Only use a tool if the user has explicitly requested or provided information that warrants its use. Do not make assumptions about files or data existing without the user mentioning them. 2. If there is ambiguity about whether using a tool is appropriate, ask a clarifying question to the user before proceeding. Confirm your understanding of their request and intent. 3. Prioritize providing direct responses and explanations based on your own knowledge and understanding. Use tools to supplement and enhance your responses when clearly applicable, but not as a default action.` type XMLTools struct { XMLName struct{} `xml:"tools"` ToolDescriptions []XMLToolDescription `xml:"tool_description"` } type XMLToolDescription struct { ToolName string `xml:"tool_name"` Description string `xml:"description"` Parameters []XMLToolParameter `xml:"parameters>parameter"` } type XMLToolParameter struct { Name string `xml:"name"` Type string `xml:"type"` Description string `xml:"description"` } type XMLFunctionCalls struct { XMLName struct{} `xml:"function_calls"` Invoke []XMLFunctionInvoke `xml:"invoke"` } type XMLFunctionInvoke struct { ToolName string `xml:"tool_name"` Parameters XMLFunctionInvokeParameters `xml:"parameters"` } type XMLFunctionInvokeParameters struct { String string `xml:",innerxml"` } type XMLFunctionResults struct { XMLName struct{} `xml:"function_results"` Result []XMLFunctionResult `xml:"result"` } type XMLFunctionResult struct { ToolName string `xml:"tool_name"` Stdout string `xml:"stdout"` } // accepts raw XML from XMLFunctionInvokeParameters.String, returns map of // parameters name to value func parseFunctionParametersXML(params string) map[string]interface{} { lines := strings.Split(params, "\n") ret := make(map[string]interface{}, len(lines)) for _, line := range lines { i := strings.Index(line, ">") if i == -1 { continue } j := strings.Index(line, " to get parameter name, // then chop after > to first %v\n", key, value, key) } params.String = paramXML converted[i] = XMLFunctionInvoke{ ToolName: toolCall.Name, Parameters: params, } } return XMLFunctionCalls{ Invoke: converted, } } func convertToolResultsToXMLFunctionResult(toolResults []api.ToolResult) XMLFunctionResults { converted := make([]XMLFunctionResult, len(toolResults)) for i, result := range toolResults { converted[i].ToolName = result.ToolName converted[i].Stdout = result.Result } return XMLFunctionResults{ Result: converted, } } func buildToolsSystemPrompt(tools []api.ToolSpec) string { xmlTools := convertToolsToXMLTools(tools) xmlToolsString, err := xmlTools.XMLString() if err != nil { panic("Could not serialize []api.Tool to XMLTools") } return TOOL_PREAMBLE + "\n\n" + xmlToolsString + "\n\n" + TOOL_PREAMBLE_FOOTER } func (x XMLTools) XMLString() (string, error) { tmpl, err := template.New("tools").Parse(` {{range .ToolDescriptions}} {{.ToolName}} {{.Description}} {{range .Parameters}} {{.Name}} {{.Type}} {{.Description}} {{end}} {{end}}`) if err != nil { return "", err } var buf bytes.Buffer if err := tmpl.Execute(&buf, x); err != nil { return "", err } return buf.String(), nil } func (x XMLFunctionResults) XMLString() (string, error) { tmpl, err := template.New("function_results").Parse(` {{range .Result}} {{.ToolName}} {{.Stdout}} {{end}}`) if err != nil { return "", err } var buf bytes.Buffer if err := tmpl.Execute(&buf, x); err != nil { return "", err } return buf.String(), nil } func (x XMLFunctionCalls) XMLString() (string, error) { tmpl, err := template.New("function_calls").Parse(` {{range .Invoke}} {{.ToolName}} {{.Parameters.String}} {{end}}`) if err != nil { return "", err } var buf bytes.Buffer if err := tmpl.Execute(&buf, x); err != nil { return "", err } return buf.String(), nil }