mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-08-01 01:26:19 +00:00
241 lines
6.6 KiB
Go
241 lines
6.6 KiB
Go
package agent
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/sipeed/picoclaw/pkg/config"
|
|
"github.com/sipeed/picoclaw/pkg/providers"
|
|
)
|
|
|
|
func promptBuildRequestForTurn(
|
|
ts *turnState,
|
|
history []providers.Message,
|
|
summary string,
|
|
currentMessage string,
|
|
media []string,
|
|
cfg *config.Config,
|
|
) PromptBuildRequest {
|
|
req := PromptBuildRequest{
|
|
History: history,
|
|
Summary: summary,
|
|
CurrentMessage: currentMessage,
|
|
Media: append([]string(nil), media...),
|
|
Channel: ts.channel,
|
|
ChatID: ts.chatID,
|
|
SenderID: ts.opts.Dispatch.SenderID(),
|
|
SenderDisplayName: ts.opts.SenderDisplayName,
|
|
ActiveSkills: activeSkillNames(ts.agent, ts.opts),
|
|
Overlays: promptOverlaysForOptions(ts.opts),
|
|
}
|
|
hasCallableTools := true
|
|
if ts.profile.Enabled {
|
|
hasCallableTools = turnProfileHasCallableTools(ts.profile, ts.agent.Tools.ToProviderDefs()) ||
|
|
turnProfileNativeSearchCallable(cfg, ts.profile, ts.agent)
|
|
}
|
|
if turnProfileSystemPromptOff(ts.profile) {
|
|
req.SuppressDefaultSystemPrompt = true
|
|
req.SuppressSkillContext = true
|
|
req.ToolUseFallback = hasCallableTools
|
|
}
|
|
if ts.profile.Enabled && !hasCallableTools {
|
|
req.SuppressToolUseRule = true
|
|
}
|
|
if turnProfileSkillsOff(ts.profile) {
|
|
req.SuppressSkillContext = true
|
|
}
|
|
if turnProfileCustomSkills(ts.profile) {
|
|
req.AllowedSkills = append([]string(nil), ts.profile.AllowedSkills...)
|
|
}
|
|
if ts.profile.Enabled && ts.profile.ToolsMode == config.TurnProfileModeCustom {
|
|
req.AllowedTools = append([]string(nil), ts.profile.AllowedTools...)
|
|
}
|
|
return req
|
|
}
|
|
|
|
func turnProfileNativeSearchCallable(
|
|
cfg *config.Config,
|
|
profile config.EffectiveTurnProfile,
|
|
agent *AgentInstance,
|
|
) bool {
|
|
if cfg == nil || agent == nil {
|
|
return false
|
|
}
|
|
if !cfg.Tools.IsToolEnabled("web") || !cfg.Tools.Web.PreferNative {
|
|
return false
|
|
}
|
|
if !turnProfileToolAllowed(profile, "web_search") {
|
|
return false
|
|
}
|
|
nativeProvider, ok := agent.Provider.(providers.NativeSearchCapable)
|
|
return ok && nativeProvider.SupportsNativeSearch()
|
|
}
|
|
|
|
func promptBuildRequestForProcessOptions(
|
|
agent *AgentInstance,
|
|
opts processOptions,
|
|
history []providers.Message,
|
|
summary string,
|
|
currentMessage string,
|
|
media []string,
|
|
) PromptBuildRequest {
|
|
req := PromptBuildRequest{
|
|
History: history,
|
|
Summary: summary,
|
|
CurrentMessage: currentMessage,
|
|
Media: append([]string(nil), media...),
|
|
Channel: opts.Channel,
|
|
ChatID: opts.ChatID,
|
|
SenderID: opts.SenderID,
|
|
SenderDisplayName: opts.SenderDisplayName,
|
|
ActiveSkills: activeSkillNames(agent, opts),
|
|
Overlays: promptOverlaysForOptions(opts),
|
|
}
|
|
profile := opts.TurnProfile
|
|
hasCallableTools := true
|
|
if profile.Enabled && agent != nil {
|
|
hasCallableTools = turnProfileHasCallableTools(profile, agent.Tools.ToProviderDefs())
|
|
}
|
|
if turnProfileSystemPromptOff(profile) {
|
|
req.SuppressDefaultSystemPrompt = true
|
|
req.SuppressSkillContext = true
|
|
req.ToolUseFallback = hasCallableTools
|
|
}
|
|
if profile.Enabled && !hasCallableTools {
|
|
req.SuppressToolUseRule = true
|
|
}
|
|
if turnProfileSkillsOff(profile) {
|
|
req.SuppressSkillContext = true
|
|
}
|
|
if turnProfileCustomSkills(profile) {
|
|
req.AllowedSkills = append([]string(nil), profile.AllowedSkills...)
|
|
}
|
|
if profile.Enabled && profile.ToolsMode == config.TurnProfileModeCustom {
|
|
req.AllowedTools = append([]string(nil), profile.AllowedTools...)
|
|
}
|
|
return req
|
|
}
|
|
|
|
func promptOverlaysForOptions(opts processOptions) []PromptPart {
|
|
systemPrompt := strings.TrimSpace(opts.SystemPromptOverride)
|
|
if systemPrompt == "" {
|
|
return nil
|
|
}
|
|
|
|
return []PromptPart{
|
|
{
|
|
ID: "instruction.subturn_profile",
|
|
Layer: PromptLayerInstruction,
|
|
Slot: PromptSlotWorkspace,
|
|
Source: PromptSource{ID: PromptSourceSubTurnProfile, Name: "subturn.profile"},
|
|
Title: "SubTurn System Instructions",
|
|
Content: systemPrompt,
|
|
Stable: false,
|
|
Cache: PromptCacheNone,
|
|
},
|
|
}
|
|
}
|
|
|
|
func promptContentBlock(part PromptPart, cache *providers.CacheControl) providers.ContentBlock {
|
|
if cache == nil {
|
|
cache = cacheControlForPromptPart(part)
|
|
}
|
|
return providers.ContentBlock{
|
|
Type: "text",
|
|
Text: part.Content,
|
|
CacheControl: cache,
|
|
PromptLayer: string(part.Layer),
|
|
PromptSlot: string(part.Slot),
|
|
PromptSource: string(part.Source.ID),
|
|
}
|
|
}
|
|
|
|
func cacheControlForPromptPart(part PromptPart) *providers.CacheControl {
|
|
switch part.Cache {
|
|
case PromptCacheEphemeral:
|
|
return &providers.CacheControl{Type: "ephemeral"}
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func promptMessageWithMetadata(
|
|
msg providers.Message,
|
|
layer PromptLayer,
|
|
slot PromptSlot,
|
|
source PromptSourceID,
|
|
) providers.Message {
|
|
msg.PromptLayer = string(layer)
|
|
msg.PromptSlot = string(slot)
|
|
msg.PromptSource = string(source)
|
|
return msg
|
|
}
|
|
|
|
func promptMessageWithDefaultMetadata(
|
|
msg providers.Message,
|
|
layer PromptLayer,
|
|
slot PromptSlot,
|
|
source PromptSourceID,
|
|
) providers.Message {
|
|
if strings.TrimSpace(msg.PromptSource) != "" {
|
|
return msg
|
|
}
|
|
return promptMessageWithMetadata(msg, layer, slot, source)
|
|
}
|
|
|
|
func userPromptMessage(content string, media []string) providers.Message {
|
|
msg := providers.Message{
|
|
Role: "user",
|
|
Content: content,
|
|
}
|
|
if len(media) > 0 {
|
|
msg.Media = append([]string(nil), media...)
|
|
}
|
|
return promptMessageWithMetadata(msg, PromptLayerTurn, PromptSlotMessage, PromptSourceUserMessage)
|
|
}
|
|
|
|
func toolResultPromptMessage(content, toolCallID string, media []string) providers.Message {
|
|
msg := providers.Message{
|
|
Role: "tool",
|
|
Content: content,
|
|
ToolCallID: toolCallID,
|
|
}
|
|
if len(media) > 0 {
|
|
msg.Media = append([]string(nil), media...)
|
|
}
|
|
return promptMessageWithMetadata(msg, PromptLayerTurn, PromptSlotToolResult, PromptSourceToolResult)
|
|
}
|
|
|
|
func toolImageFollowUpPromptMessage(media []string) providers.Message {
|
|
msg := providers.Message{
|
|
Role: "user",
|
|
Content: "[Loaded image from tool result above]",
|
|
}
|
|
if len(media) > 0 {
|
|
msg.Media = append([]string(nil), media...)
|
|
}
|
|
return promptMessageWithMetadata(msg, PromptLayerTurn, PromptSlotToolResult, PromptSourceToolResult)
|
|
}
|
|
|
|
func steeringPromptMessage(msg providers.Message) providers.Message {
|
|
return promptMessageWithDefaultMetadata(msg, PromptLayerTurn, PromptSlotSteering, PromptSourceSteering)
|
|
}
|
|
|
|
func subTurnResultPromptMessage(content string) providers.Message {
|
|
return promptMessageWithMetadata(
|
|
providers.Message{Role: "user", Content: fmt.Sprintf("[SubTurn Result] %s", content)},
|
|
PromptLayerTurn,
|
|
PromptSlotSubTurn,
|
|
PromptSourceSubTurnResult,
|
|
)
|
|
}
|
|
|
|
func interruptPromptMessage(content string) providers.Message {
|
|
return promptMessageWithMetadata(
|
|
providers.Message{Role: "user", Content: content},
|
|
PromptLayerTurn,
|
|
PromptSlotInterrupt,
|
|
PromptSourceInterrupt,
|
|
)
|
|
}
|