mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
6421f146a9
This reverts commit e556a816e4.
97 lines
3.1 KiB
Go
97 lines
3.1 KiB
Go
// PicoClaw - Ultra-lightweight personal AI agent
|
|
// License: MIT
|
|
//
|
|
// Copyright (c) 2026 PicoClaw contributors
|
|
|
|
package cliprovider
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// buildCLIToolsPrompt creates the tool definitions section for a CLI provider system prompt.
|
|
func buildCLIToolsPrompt(tools []ToolDefinition) string {
|
|
var sb strings.Builder
|
|
|
|
sb.WriteString("## Available Tools\n\n")
|
|
sb.WriteString("When you need to use a tool, respond with ONLY a JSON object:\n\n")
|
|
sb.WriteString("```json\n")
|
|
sb.WriteString(
|
|
`{"tool_calls":[{"id":"call_xxx","type":"function","function":{"name":"tool_name","arguments":"{...}"}}]}`,
|
|
)
|
|
sb.WriteString("\n```\n\n")
|
|
sb.WriteString("CRITICAL: The 'arguments' field MUST be a JSON-encoded STRING.\n\n")
|
|
sb.WriteString("Escaping rules (what to type in `function.arguments`):\n")
|
|
sb.WriteString("- Use `\\n` to represent a real newline character.\n")
|
|
sb.WriteString("- Use `\\\\n` to represent a literal backslash+n sequence (`\\n`).\n")
|
|
sb.WriteString(
|
|
"- `function.arguments` is a JSON-encoded string, so quotes/backslashes must be escaped in the outer payload.\n\n",
|
|
)
|
|
sb.WriteString("### Tool Definitions:\n\n")
|
|
|
|
for _, tool := range tools {
|
|
if tool.Type != "function" {
|
|
continue
|
|
}
|
|
sb.WriteString(fmt.Sprintf("#### %s\n", tool.Function.Name))
|
|
if tool.Function.Description != "" {
|
|
sb.WriteString(fmt.Sprintf("Description: %s\n", tool.Function.Description))
|
|
}
|
|
if len(tool.Function.Parameters) > 0 {
|
|
paramsJSON, _ := json.Marshal(tool.Function.Parameters)
|
|
sb.WriteString(fmt.Sprintf("Parameters:\n```json\n%s\n```\n", string(paramsJSON)))
|
|
}
|
|
sb.WriteString("\n")
|
|
}
|
|
|
|
return sb.String()
|
|
}
|
|
|
|
// NormalizeToolCall normalizes a ToolCall to ensure all fields are properly populated.
|
|
// It handles cases where Name/Arguments might be in different locations (top-level vs Function)
|
|
// and ensures both are populated consistently.
|
|
func NormalizeToolCall(tc ToolCall) ToolCall {
|
|
normalized := tc
|
|
|
|
// Ensure Name is populated from Function if not set
|
|
if normalized.Name == "" && normalized.Function != nil {
|
|
normalized.Name = normalized.Function.Name
|
|
}
|
|
|
|
// Ensure Arguments is not nil
|
|
if normalized.Arguments == nil {
|
|
normalized.Arguments = map[string]any{}
|
|
}
|
|
|
|
// Parse Arguments from Function.Arguments if not already set
|
|
if len(normalized.Arguments) == 0 && normalized.Function != nil && normalized.Function.Arguments != "" {
|
|
var parsed map[string]any
|
|
if err := json.Unmarshal([]byte(normalized.Function.Arguments), &parsed); err == nil && parsed != nil {
|
|
normalized.Arguments = parsed
|
|
}
|
|
}
|
|
|
|
// Ensure Function is populated with consistent values
|
|
argsJSON, _ := json.Marshal(normalized.Arguments)
|
|
if normalized.Function == nil {
|
|
normalized.Function = &FunctionCall{
|
|
Name: normalized.Name,
|
|
Arguments: string(argsJSON),
|
|
}
|
|
} else {
|
|
if normalized.Function.Name == "" {
|
|
normalized.Function.Name = normalized.Name
|
|
}
|
|
if normalized.Name == "" {
|
|
normalized.Name = normalized.Function.Name
|
|
}
|
|
if normalized.Function.Arguments == "" {
|
|
normalized.Function.Arguments = string(argsJSON)
|
|
}
|
|
}
|
|
|
|
return normalized
|
|
}
|