X-Git-Url: https://git.giacomolli.uk/onechat/blobdiff_plain/4343ca5fb6cc86eb887951f4fe16063bab29a238..0cb73c3dde36d0078f3c1aa4200f2edcb5ceed38:/prompt.go?ds=sidebyside diff --git a/prompt.go b/prompt.go index df6f17d..31f57cb 100644 --- a/prompt.go +++ b/prompt.go @@ -34,9 +34,6 @@ const ( promptInstructionsHeading = "\n\n# The user's instructions\n\n" ) -// promptEfforts are the reasoning efforts that spawn can give a subagent. -var promptEfforts = []string{"low", "medium", "high", "xhigh"} - // promptText is the system prompt of the reference, nearly word for word, with // one added paragraph on the user's instructions file. The reference names its // own agent, so promptText has the name of the agent and the kind reply in its @@ -154,7 +151,9 @@ Pass area messages from mixed chats to the right child with sender, date, and words. Never send a WhatsApp reply drafted by a child without the user's agreement. Propose routing a source that talks only about one area to its child. -Set or remove a route only after the user agrees.` +Set or remove a route only after the user agrees. +Use image_show or whatsapp_send with an image only on the user's request. +Pass an image to a child with an [image FILE] line in tell.` // promptSubagentText is the system prompt of a subagent, from the subagent // prompt of the reference design: its head, then the doc of the view, without @@ -195,18 +194,8 @@ date(id) gives the date and time of message id. zoom("Name") gives an agent's whole chat. An [image FILE] line names a stored image. Use zoom(id, 1) to see its pixels.` -// prompt returns the system prompt of the reference with one added paragraph on -// the user's instructions file at instructionsPath. It uses the agent name and -// ends with the user's instructions. Without instructions it has neither them -// nor the clause that points to them, but keeps the file paragraph. Turns and -// compactions send the same prompt and tools, so compactions read the prompt -// cache of the turns. -func prompt(name, instructionsPath, instructions string) string { - text := strings.NewReplacer(promptName, name, promptInstructionsPath, instructionsPath).Replace(promptText) - text = strings.Replace(text, "\n\n# Compactions", promptChildrenText+"\n\n# Compactions", 1) - - return promptWithInstructions(text, promptTaskSentence, promptInstructionsClause, instructions) -} +// promptEfforts are the reasoning efforts that spawn can give a subagent. +var promptEfforts = []string{"low", "medium", "high", "xhigh"} // promptChild returns the prompt of a child with its area and reporting rules. // Its instructions file belongs to the child, not to the parent. @@ -221,11 +210,39 @@ message outside your area. Your final reply is your report to the parent. End your turn with an empty reply when nothing needs the parent or the user. Send nothing to WhatsApp. Draft replies in your report instead, so the parent can ask the user before sending.`, 1) + text = strings.Replace(text, promptChildrenText, "", 1) return promptWithInstructions(text, "Do the tasks of your area yourself, with your tools.", promptInstructionsClause, instructions) } +// prompt returns the system prompt of the reference with one added paragraph on +// the user's instructions file at instructionsPath. It uses the agent name and +// ends with the user's instructions. Without instructions it has neither them +// nor the clause that points to them, but keeps the file paragraph. Turns and +// compactions send the same prompt and tools, so compactions read the prompt +// cache of the turns. +func prompt(name, instructionsPath, instructions string) string { + text := strings.NewReplacer(promptName, name, promptInstructionsPath, instructionsPath).Replace(promptText) + text = strings.Replace(text, "\n\n# Compactions", promptChildrenText+"\n\n# Compactions", 1) + + return promptWithInstructions(text, promptTaskSentence, promptInstructionsClause, instructions) +} + +// promptWithInstructions returns text with the instructions of the user at its +// end, after the heading. With instructions, sentence gets clause, which points +// to them. Without instructions the prompt has neither them nor the clause. +func promptWithInstructions(text, sentence, clause, instructions string) string { + instructions = strings.TrimSpace(instructions) + if instructions == "" { + return text + } + + text = strings.Replace(text, sentence, strings.TrimSuffix(sentence, ".")+clause+".", 1) + + return text + promptInstructionsHeading + instructions +} + // promptSubagent returns the system prompt of a subagent of the agent name, // with the instructions of the user at its end, as prompt does. func promptSubagent(name, instructions string) string { @@ -274,6 +291,15 @@ func promptTools() []ModelTool { "message": promptStringSchema("The message."), }), Strict: true, + }, ModelTool{ + Type: "function", + Name: "image_show", + Description: "Show a stored image to the user, only when the user asks to see it. Give its stored filename, not a path.", + Parameters: promptObjectSchema(map[string]any{ + "image": promptStringSchema("The stored image filename from an [image FILE] line."), + "caption": promptStringSchema("The caption, or an empty string."), + }), + Strict: true, }, ModelTool{ Type: "function", Name: "whatsapp_find", @@ -285,11 +311,12 @@ func promptTools() []ModelTool { }, ModelTool{ Type: "function", Name: "whatsapp_send", - Description: "Send a text message to a WhatsApp chat. Send only a message that the user asked you to send, to the chat that the user named. Give a new id for each message: a call with an id that was used gives the answer of the first call and sends nothing.", + Description: "Send text or a stored image with text as its caption to a WhatsApp chat. Send only a message that the user asked you to send, to the chat that the user named. Give a new id for each message: a call with an id that was used gives the answer of the first call and sends nothing.", Parameters: promptObjectSchema(map[string]any{ - "id": promptStringSchema("A new id for this message, such as a short random word."), - "chat": promptStringSchema("The ID of the chat, from whatsapp_find or from a [WhatsApp] message."), - "text": promptStringSchema("The text of the message."), + "id": promptStringSchema("A new id for this message, such as a short random word."), + "chat": promptStringSchema("The ID of the chat, from whatsapp_find or from a [WhatsApp] message."), + "text": promptStringSchema("The text, or the caption when sending an image. Can be empty with an image."), + "image": promptNullableStringSchema("The stored image filename from an [image FILE] line, or null for text only."), }), Strict: true, }, ModelTool{ @@ -408,20 +435,6 @@ func promptSubagentTools() []ModelTool { } } -// promptWithInstructions returns text with the instructions of the user at its -// end, after the heading. With instructions, sentence gets clause, which points -// to them. Without instructions the prompt has neither them nor the clause. -func promptWithInstructions(text, sentence, clause, instructions string) string { - instructions = strings.TrimSpace(instructions) - if instructions == "" { - return text - } - - text = strings.Replace(text, sentence, strings.TrimSuffix(sentence, ".")+clause+".", 1) - - return text + promptInstructionsHeading + instructions -} - // promptObjectSchema returns a strict JSON schema of an object whose properties // are all required. func promptObjectSchema(properties map[string]any) map[string]any { @@ -440,8 +453,8 @@ func promptObjectSchema(properties map[string]any) map[string]any { } } -// promptEffortEnum returns the values of the effort of spawn: promptEfforts -// and null. +// promptEffortEnum returns the values of the effort of spawn: promptEfforts and +// null. func promptEffortEnum() []any { values := make([]any, 0, len(promptEfforts)+1) for _, effort := range promptEfforts { @@ -451,10 +464,10 @@ func promptEffortEnum() []any { return append(values, nil) } -// promptIntegerSchema returns the JSON schema of an integer. -func promptIntegerSchema(description string) map[string]any { +// promptStringSchema returns the JSON schema of a string. +func promptStringSchema(description string) map[string]any { return map[string]any{ - "type": "integer", + "type": "string", "description": description, } } @@ -467,10 +480,10 @@ func promptNullableStringSchema(description string) map[string]any { } } -// promptStringSchema returns the JSON schema of a string. -func promptStringSchema(description string) map[string]any { +// promptIntegerSchema returns the JSON schema of an integer. +func promptIntegerSchema(description string) map[string]any { return map[string]any{ - "type": "string", + "type": "integer", "description": description, } }