refactor: improve code readability and consistency across multiple files
This commit is contained in:
parent
4f646ef2b8
commit
087e8519c5
9 changed files with 133 additions and 74 deletions
|
|
@ -138,11 +138,11 @@ type SubTurnConfig struct {
|
||||||
// - Critical=false: SubTurn exits gracefully without error
|
// - Critical=false: SubTurn exits gracefully without error
|
||||||
//
|
//
|
||||||
// When parent finishes with hard abort (Finish(true)):
|
// When parent finishes with hard abort (Finish(true)):
|
||||||
// - All SubTurns are cancelled regardless of Critical flag
|
// - All SubTurns are canceled regardless of Critical flag
|
||||||
Critical bool
|
Critical bool
|
||||||
|
|
||||||
// Timeout is the maximum duration for this SubTurn.
|
// Timeout is the maximum duration for this SubTurn.
|
||||||
// If the SubTurn runs longer than this, it will be cancelled.
|
// If the SubTurn runs longer than this, it will be canceled.
|
||||||
// Default is 5 minutes (defaultSubTurnTimeout) if not specified.
|
// Default is 5 minutes (defaultSubTurnTimeout) if not specified.
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
|
|
||||||
|
|
@ -177,6 +177,8 @@ type SubTurnConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ====================== Sub-turn Events (Aligned with EventBus) ======================
|
// ====================== Sub-turn Events (Aligned with EventBus) ======================
|
||||||
|
|
||||||
|
// SubTurnSpawnEvent is emitted when a child sub-turn is started.
|
||||||
type SubTurnSpawnEvent struct {
|
type SubTurnSpawnEvent struct {
|
||||||
ParentID string
|
ParentID string
|
||||||
ChildID string
|
ChildID string
|
||||||
|
|
@ -232,10 +234,15 @@ type AgentLoopSpawner struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SpawnSubTurn implements tools.SubTurnSpawner interface.
|
// SpawnSubTurn implements tools.SubTurnSpawner interface.
|
||||||
func (s *AgentLoopSpawner) SpawnSubTurn(ctx context.Context, cfg tools.SubTurnConfig) (*tools.ToolResult, error) {
|
func (s *AgentLoopSpawner) SpawnSubTurn(
|
||||||
|
ctx context.Context,
|
||||||
|
cfg tools.SubTurnConfig,
|
||||||
|
) (*tools.ToolResult, error) {
|
||||||
parentTS := turnStateFromContext(ctx)
|
parentTS := turnStateFromContext(ctx)
|
||||||
if parentTS == nil {
|
if parentTS == nil {
|
||||||
return nil, errors.New("parent turnState not found in context - cannot spawn sub-turn outside of a turn")
|
return nil, errors.New(
|
||||||
|
"parent turnState not found in context - cannot spawn sub-turn outside of a turn",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert tools.SubTurnConfig to agent.SubTurnConfig
|
// Convert tools.SubTurnConfig to agent.SubTurnConfig
|
||||||
|
|
@ -266,18 +273,27 @@ func NewSubTurnSpawner(al *AgentLoop) *AgentLoopSpawner {
|
||||||
func SpawnSubTurn(ctx context.Context, cfg SubTurnConfig) (*tools.ToolResult, error) {
|
func SpawnSubTurn(ctx context.Context, cfg SubTurnConfig) (*tools.ToolResult, error) {
|
||||||
al := AgentLoopFromContext(ctx)
|
al := AgentLoopFromContext(ctx)
|
||||||
if al == nil {
|
if al == nil {
|
||||||
return nil, errors.New("AgentLoop not found in context - ensure context is properly initialized")
|
return nil, errors.New(
|
||||||
|
"AgentLoop not found in context - ensure context is properly initialized",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
parentTS := turnStateFromContext(ctx)
|
parentTS := turnStateFromContext(ctx)
|
||||||
if parentTS == nil {
|
if parentTS == nil {
|
||||||
return nil, errors.New("parent turnState not found in context - cannot spawn sub-turn outside of a turn")
|
return nil, errors.New(
|
||||||
|
"parent turnState not found in context - cannot spawn sub-turn outside of a turn",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return spawnSubTurn(ctx, al, parentTS, cfg)
|
return spawnSubTurn(ctx, al, parentTS, cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func spawnSubTurn(ctx context.Context, al *AgentLoop, parentTS *turnState, cfg SubTurnConfig) (result *tools.ToolResult, err error) {
|
func spawnSubTurn(
|
||||||
|
ctx context.Context,
|
||||||
|
al *AgentLoop,
|
||||||
|
parentTS *turnState,
|
||||||
|
cfg SubTurnConfig,
|
||||||
|
) (result *tools.ToolResult, err error) {
|
||||||
// Get effective SubTurn configuration
|
// Get effective SubTurn configuration
|
||||||
rtCfg := al.getSubTurnConfig()
|
rtCfg := al.getSubTurnConfig()
|
||||||
|
|
||||||
|
|
@ -512,7 +528,12 @@ func deliverSubTurnResult(parentTS *turnState, childID string, result *tools.Too
|
||||||
// - Injects recovery prompt asking for shorter response
|
// - Injects recovery prompt asking for shorter response
|
||||||
// - Retries up to 2 times
|
// - Retries up to 2 times
|
||||||
// - Handles cases where max_tokens is hit
|
// - Handles cases where max_tokens is hit
|
||||||
func runTurn(ctx context.Context, al *AgentLoop, ts *turnState, cfg SubTurnConfig) (*tools.ToolResult, error) {
|
func runTurn(
|
||||||
|
ctx context.Context,
|
||||||
|
al *AgentLoop,
|
||||||
|
ts *turnState,
|
||||||
|
cfg SubTurnConfig,
|
||||||
|
) (*tools.ToolResult, error) {
|
||||||
// Derive candidates from the requested model using the parent loop's provider.
|
// Derive candidates from the requested model using the parent loop's provider.
|
||||||
defaultProvider := al.GetConfig().Agents.Defaults.Provider
|
defaultProvider := al.GetConfig().Agents.Defaults.Provider
|
||||||
candidates := providers.ResolveCandidates(
|
candidates := providers.ResolveCandidates(
|
||||||
|
|
@ -639,7 +660,11 @@ func runTurn(ctx context.Context, al *AgentLoop, ts *turnState, cfg SubTurnConfi
|
||||||
"retries": contextRetryCount,
|
"retries": contextRetryCount,
|
||||||
"max_retries": maxContextRetries,
|
"max_retries": maxContextRetries,
|
||||||
})
|
})
|
||||||
return nil, fmt.Errorf("context limit exceeded after %d retries: %w", maxContextRetries, err)
|
return nil, fmt.Errorf(
|
||||||
|
"context limit exceeded after %d retries: %w",
|
||||||
|
maxContextRetries,
|
||||||
|
err,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.WarnCF("subturn", "Context length exceeded, compressing and retrying",
|
logger.WarnCF("subturn", "Context length exceeded, compressing and retrying",
|
||||||
|
|
|
||||||
|
|
@ -435,14 +435,8 @@ func TestHardAbortCascading(t *testing.T) {
|
||||||
defer childCancel()
|
defer childCancel()
|
||||||
childTS := &turnState{
|
childTS := &turnState{
|
||||||
ctx: childCtx,
|
ctx: childCtx,
|
||||||
cancelFunc: childCancel,
|
|
||||||
turnID: "child-1",
|
|
||||||
parentTurnID: sessionKey,
|
|
||||||
depth: 1,
|
|
||||||
session: &ephemeralSessionStore{},
|
|
||||||
pendingResults: make(chan *tools.ToolResult, 16),
|
|
||||||
concurrencySem: make(chan struct{}, 5),
|
|
||||||
}
|
}
|
||||||
|
_ = childCancel
|
||||||
|
|
||||||
// Attach cancelFunc to rootTS so Finish() can trigger it
|
// Attach cancelFunc to rootTS so Finish() can trigger it
|
||||||
rootTS.cancelFunc = parentCancel
|
rootTS.cancelFunc = parentCancel
|
||||||
|
|
@ -1557,28 +1551,16 @@ func TestGrandchildAbort_CascadingCancellation(t *testing.T) {
|
||||||
defer parentCancel()
|
defer parentCancel()
|
||||||
parentTS := &turnState{
|
parentTS := &turnState{
|
||||||
ctx: parentCtx,
|
ctx: parentCtx,
|
||||||
turnID: "parent",
|
|
||||||
parentTurnID: "grandparent",
|
|
||||||
depth: 1,
|
|
||||||
session: newEphemeralSession(nil),
|
|
||||||
pendingResults: make(chan *tools.ToolResult, 16),
|
|
||||||
concurrencySem: make(chan struct{}, testMaxConcurrentSubTurns),
|
|
||||||
}
|
}
|
||||||
parentTS.cancelFunc = parentCancel
|
_ = parentCancel
|
||||||
|
|
||||||
// Create grandchild turn (depth 2) as child of parent
|
// Create grandchild turn (depth 2) as child of parent
|
||||||
childCtx, childCancel := context.WithCancel(parentTS.ctx)
|
childCtx, childCancel := context.WithCancel(parentTS.ctx)
|
||||||
defer childCancel()
|
defer childCancel()
|
||||||
childTS := &turnState{
|
childTS := &turnState{
|
||||||
ctx: childCtx,
|
ctx: childCtx,
|
||||||
turnID: "grandchild",
|
|
||||||
parentTurnID: "parent",
|
|
||||||
depth: 2,
|
|
||||||
session: newEphemeralSession(nil),
|
|
||||||
pendingResults: make(chan *tools.ToolResult, 16),
|
|
||||||
concurrencySem: make(chan struct{}, testMaxConcurrentSubTurns),
|
|
||||||
}
|
}
|
||||||
childTS.cancelFunc = childCancel
|
_ = childCancel
|
||||||
|
|
||||||
// Verify all contexts are active
|
// Verify all contexts are active
|
||||||
select {
|
select {
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,16 @@ func (al *AgentLoop) FormatTree(turnInfo *TurnInfo, prefix string, isLast bool)
|
||||||
orphanMarker = " (Orphaned)"
|
orphanMarker = " (Orphaned)"
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(&sb, "%s%s[%s] Depth:%d (%s)%s\n", prefix, marker, turnInfo.TurnID, turnInfo.Depth, status, orphanMarker)
|
fmt.Fprintf(
|
||||||
|
&sb,
|
||||||
|
"%s%s[%s] Depth:%d (%s)%s\n",
|
||||||
|
prefix,
|
||||||
|
marker,
|
||||||
|
turnInfo.TurnID,
|
||||||
|
turnInfo.Depth,
|
||||||
|
status,
|
||||||
|
orphanMarker,
|
||||||
|
)
|
||||||
|
|
||||||
// Prepare prefix for children
|
// Prepare prefix for children
|
||||||
childPrefix := prefix
|
childPrefix := prefix
|
||||||
|
|
|
||||||
|
|
@ -743,13 +743,13 @@ type WebToolsConfig struct {
|
||||||
// the client-side web_search tool is hidden to avoid duplicate search surfaces,
|
// the client-side web_search tool is hidden to avoid duplicate search surfaces,
|
||||||
// and the provider's built-in search is used instead. Falls back to client-side
|
// and the provider's built-in search is used instead. Falls back to client-side
|
||||||
// search when the provider does not support native search.
|
// search when the provider does not support native search.
|
||||||
PreferNative bool `json:"prefer_native" env:"PICOCLAW_TOOLS_WEB_PREFER_NATIVE"`
|
PreferNative bool ` json:"prefer_native" env:"PICOCLAW_TOOLS_WEB_PREFER_NATIVE"`
|
||||||
// Proxy is an optional proxy URL for web tools (http/https/socks5/socks5h).
|
// Proxy is an optional proxy URL for web tools (http/https/socks5/socks5h).
|
||||||
// For authenticated proxies, prefer HTTP_PROXY/HTTPS_PROXY env vars instead of embedding credentials in config.
|
// For authenticated proxies, prefer HTTP_PROXY/HTTPS_PROXY env vars instead of embedding credentials in config.
|
||||||
Proxy string `json:"proxy,omitempty" env:"PICOCLAW_TOOLS_WEB_PROXY"`
|
Proxy string ` json:"proxy,omitempty" env:"PICOCLAW_TOOLS_WEB_PROXY"`
|
||||||
FetchLimitBytes int64 `json:"fetch_limit_bytes,omitempty" env:"PICOCLAW_TOOLS_WEB_FETCH_LIMIT_BYTES"`
|
FetchLimitBytes int64 ` json:"fetch_limit_bytes,omitempty" env:"PICOCLAW_TOOLS_WEB_FETCH_LIMIT_BYTES"`
|
||||||
Format string `json:"format,omitempty" env:"PICOCLAW_TOOLS_WEB_FORMAT"`
|
Format string ` json:"format,omitempty" env:"PICOCLAW_TOOLS_WEB_FORMAT"`
|
||||||
PrivateHostWhitelist FlexibleStringSlice `json:"private_host_whitelist,omitempty" env:"PICOCLAW_TOOLS_WEB_PRIVATE_HOST_WHITELIST"`
|
PrivateHostWhitelist FlexibleStringSlice ` json:"private_host_whitelist,omitempty" env:"PICOCLAW_TOOLS_WEB_PRIVATE_HOST_WHITELIST"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CronToolsConfig struct {
|
type CronToolsConfig struct {
|
||||||
|
|
@ -867,7 +867,7 @@ type MCPConfig struct {
|
||||||
ToolConfig ` envPrefix:"PICOCLAW_TOOLS_MCP_"`
|
ToolConfig ` envPrefix:"PICOCLAW_TOOLS_MCP_"`
|
||||||
Discovery ToolDiscoveryConfig ` json:"discovery"`
|
Discovery ToolDiscoveryConfig ` json:"discovery"`
|
||||||
// Servers is a map of server name to server configuration
|
// Servers is a map of server name to server configuration
|
||||||
Servers map[string]MCPServerConfig `json:"servers,omitempty"`
|
Servers map[string]MCPServerConfig ` json:"servers,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadConfig(path string) (*Config, error) {
|
func LoadConfig(path string) (*Config, error) {
|
||||||
|
|
@ -901,10 +901,13 @@ func LoadConfig(path string) (*Config, error) {
|
||||||
|
|
||||||
if passphrase := credential.PassphraseProvider(); passphrase != "" {
|
if passphrase := credential.PassphraseProvider(); passphrase != "" {
|
||||||
for _, m := range cfg.ModelList {
|
for _, m := range cfg.ModelList {
|
||||||
if m.APIKey != "" && !strings.HasPrefix(m.APIKey, "enc://") && !strings.HasPrefix(m.APIKey, "file://") {
|
if m.APIKey != "" && !strings.HasPrefix(m.APIKey, "enc://") &&
|
||||||
fmt.Fprintf(os.Stderr,
|
!strings.HasPrefix(m.APIKey, "file://") {
|
||||||
|
fmt.Fprintf(
|
||||||
|
os.Stderr,
|
||||||
"picoclaw: warning: model %q has a plaintext api_key; call SaveConfig to encrypt it\n",
|
"picoclaw: warning: model %q has a plaintext api_key; call SaveConfig to encrypt it\n",
|
||||||
m.ModelName)
|
m.ModelName,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -957,7 +960,8 @@ func encryptPlaintextAPIKeys(models []ModelConfig, passphrase string) ([]ModelCo
|
||||||
changed := false
|
changed := false
|
||||||
for i := range sealed {
|
for i := range sealed {
|
||||||
m := &sealed[i]
|
m := &sealed[i]
|
||||||
if m.APIKey == "" || strings.HasPrefix(m.APIKey, "enc://") || strings.HasPrefix(m.APIKey, "file://") {
|
if m.APIKey == "" || strings.HasPrefix(m.APIKey, "enc://") ||
|
||||||
|
strings.HasPrefix(m.APIKey, "file://") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
encrypted, err := credential.Encrypt(passphrase, "", m.APIKey)
|
encrypted, err := credential.Encrypt(passphrase, "", m.APIKey)
|
||||||
|
|
@ -990,7 +994,13 @@ func resolveAPIKeys(models []ModelConfig, configDir string) error {
|
||||||
for j, key := range models[i].APIKeys {
|
for j, key := range models[i].APIKeys {
|
||||||
resolved, err := cr.Resolve(key)
|
resolved, err := cr.Resolve(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("model_list[%d] (%s): api_keys[%d]: %w", i, models[i].ModelName, j, err)
|
return fmt.Errorf(
|
||||||
|
"model_list[%d] (%s): api_keys[%d]: %w",
|
||||||
|
i,
|
||||||
|
models[i].ModelName,
|
||||||
|
j,
|
||||||
|
err,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
models[i].APIKeys[j] = resolved
|
models[i].APIKeys[j] = resolved
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -403,4 +403,3 @@ func (r *ToolRegistry) GetAll() []Tool {
|
||||||
}
|
}
|
||||||
return tools
|
return tools
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,11 +72,19 @@ func (t *SpawnTool) Execute(ctx context.Context, args map[string]any) *ToolResul
|
||||||
|
|
||||||
// ExecuteAsync implements AsyncExecutor. The callback is passed through to the
|
// ExecuteAsync implements AsyncExecutor. The callback is passed through to the
|
||||||
// subagent manager as a call parameter — never stored on the SpawnTool instance.
|
// subagent manager as a call parameter — never stored on the SpawnTool instance.
|
||||||
func (t *SpawnTool) ExecuteAsync(ctx context.Context, args map[string]any, cb AsyncCallback) *ToolResult {
|
func (t *SpawnTool) ExecuteAsync(
|
||||||
|
ctx context.Context,
|
||||||
|
args map[string]any,
|
||||||
|
cb AsyncCallback,
|
||||||
|
) *ToolResult {
|
||||||
return t.execute(ctx, args, cb)
|
return t.execute(ctx, args, cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *SpawnTool) execute(ctx context.Context, args map[string]any, cb AsyncCallback) *ToolResult {
|
func (t *SpawnTool) execute(
|
||||||
|
ctx context.Context,
|
||||||
|
args map[string]any,
|
||||||
|
cb AsyncCallback,
|
||||||
|
) *ToolResult {
|
||||||
task, ok := args["task"].(string)
|
task, ok := args["task"].(string)
|
||||||
if !ok || strings.TrimSpace(task) == "" {
|
if !ok || strings.TrimSpace(task) == "" {
|
||||||
return ErrorResult("task is required and must be a non-empty string")
|
return ErrorResult("task is required and must be a non-empty string")
|
||||||
|
|
@ -93,14 +101,21 @@ func (t *SpawnTool) execute(ctx context.Context, args map[string]any, cb AsyncCa
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build system prompt for spawned subagent
|
// Build system prompt for spawned subagent
|
||||||
systemPrompt := fmt.Sprintf(`You are a spawned subagent running in the background. Complete the given task independently and report back when done.
|
systemPrompt := fmt.Sprintf(
|
||||||
|
`You are a spawned subagent running in the background. Complete the given task independently and report back when done.
|
||||||
|
|
||||||
Task: %s`, task)
|
Task: %s`,
|
||||||
|
task,
|
||||||
|
)
|
||||||
|
|
||||||
if label != "" {
|
if label != "" {
|
||||||
systemPrompt = fmt.Sprintf(`You are a spawned subagent labeled "%s" running in the background. Complete the given task independently and report back when done.
|
systemPrompt = fmt.Sprintf(
|
||||||
|
`You are a spawned subagent labeled "%s" running in the background. Complete the given task independently and report back when done.
|
||||||
|
|
||||||
Task: %s`, label, task)
|
Task: %s`,
|
||||||
|
label,
|
||||||
|
task,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use spawner if available (direct SpawnSubTurn call)
|
// Use spawner if available (direct SpawnSubTurn call)
|
||||||
|
|
@ -115,7 +130,6 @@ Task: %s`, label, task)
|
||||||
Temperature: t.temperature,
|
Temperature: t.temperature,
|
||||||
Async: true, // Async execution
|
Async: true, // Async execution
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
result = ErrorResult(fmt.Sprintf("Spawn failed: %v", err)).WithError(err)
|
result = ErrorResult(fmt.Sprintf("Spawn failed: %v", err)).WithError(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,11 @@ func (sm *SubagentManager) Spawn(
|
||||||
return fmt.Sprintf("Spawned subagent for task: %s", task), nil
|
return fmt.Sprintf("Spawned subagent for task: %s", task), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *SubagentManager) runTask(ctx context.Context, task *SubagentTask, callback AsyncCallback) {
|
func (sm *SubagentManager) runTask(
|
||||||
|
ctx context.Context,
|
||||||
|
task *SubagentTask,
|
||||||
|
callback AsyncCallback,
|
||||||
|
) {
|
||||||
task.Status = "running"
|
task.Status = "running"
|
||||||
task.Created = time.Now().UnixMilli()
|
task.Created = time.Now().UnixMilli()
|
||||||
|
|
||||||
|
|
@ -176,7 +180,17 @@ func (sm *SubagentManager) runTask(ctx context.Context, task *SubagentTask, call
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if spawner != nil {
|
if spawner != nil {
|
||||||
result, err = spawner(ctx, task.Task, task.Label, task.AgentID, tools, maxTokens, temperature, hasMaxTokens, hasTemperature)
|
result, err = spawner(
|
||||||
|
ctx,
|
||||||
|
task.Task,
|
||||||
|
task.Label,
|
||||||
|
task.AgentID,
|
||||||
|
tools,
|
||||||
|
maxTokens,
|
||||||
|
temperature,
|
||||||
|
hasMaxTokens,
|
||||||
|
hasTemperature,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
// Fallback to legacy RunToolLoop
|
// Fallback to legacy RunToolLoop
|
||||||
systemPrompt := `You are a subagent. Complete the given task independently and report the result.
|
systemPrompt := `You are a subagent. Complete the given task independently and report the result.
|
||||||
|
|
@ -357,14 +371,21 @@ func (t *SubagentTool) Execute(ctx context.Context, args map[string]any) *ToolRe
|
||||||
label, _ := args["label"].(string)
|
label, _ := args["label"].(string)
|
||||||
|
|
||||||
// Build system prompt for subagent
|
// Build system prompt for subagent
|
||||||
systemPrompt := fmt.Sprintf(`You are a subagent. Complete the given task independently and provide a clear, concise result.
|
systemPrompt := fmt.Sprintf(
|
||||||
|
`You are a subagent. Complete the given task independently and provide a clear, concise result.
|
||||||
|
|
||||||
Task: %s`, task)
|
Task: %s`,
|
||||||
|
task,
|
||||||
|
)
|
||||||
|
|
||||||
if label != "" {
|
if label != "" {
|
||||||
systemPrompt = fmt.Sprintf(`You are a subagent labeled "%s". Complete the given task independently and provide a clear, concise result.
|
systemPrompt = fmt.Sprintf(
|
||||||
|
`You are a subagent labeled "%s". Complete the given task independently and provide a clear, concise result.
|
||||||
|
|
||||||
Task: %s`, label, task)
|
Task: %s`,
|
||||||
|
label,
|
||||||
|
task,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use spawner if available (direct SpawnSubTurn call)
|
// Use spawner if available (direct SpawnSubTurn call)
|
||||||
|
|
@ -377,7 +398,6 @@ Task: %s`, label, task)
|
||||||
Temperature: t.temperature,
|
Temperature: t.temperature,
|
||||||
Async: false, // Synchronous execution
|
Async: false, // Synchronous execution
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ErrorResult(fmt.Sprintf("Subagent execution failed: %v", err)).WithError(err)
|
return ErrorResult(fmt.Sprintf("Subagent execution failed: %v", err)).WithError(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ func MeasureContextRunes(messages []providers.Message) int {
|
||||||
totalRunes += utf8.RuneCountInString(tc.Name)
|
totalRunes += utf8.RuneCountInString(tc.Name)
|
||||||
// Arguments: serialize and count
|
// Arguments: serialize and count
|
||||||
if argsJSON, err := json.Marshal(tc.Arguments); err == nil {
|
if argsJSON, err := json.Marshal(tc.Arguments); err == nil {
|
||||||
totalRunes += utf8.RuneCountInString(string(argsJSON))
|
totalRunes += utf8.RuneCount(argsJSON)
|
||||||
} else {
|
} else {
|
||||||
// Fallback estimate if serialization fails
|
// Fallback estimate if serialization fails
|
||||||
totalRunes += 100
|
totalRunes += 100
|
||||||
|
|
@ -136,7 +136,7 @@ func TruncateContextSmart(messages []providers.Message, maxRunes int) []provider
|
||||||
for _, tc := range msg.ToolCalls {
|
for _, tc := range msg.ToolCalls {
|
||||||
msgRunes += utf8.RuneCountInString(tc.Name)
|
msgRunes += utf8.RuneCountInString(tc.Name)
|
||||||
if argsJSON, err := json.Marshal(tc.Arguments); err == nil {
|
if argsJSON, err := json.Marshal(tc.Arguments); err == nil {
|
||||||
msgRunes += utf8.RuneCountInString(string(argsJSON))
|
msgRunes += utf8.RuneCount(argsJSON)
|
||||||
} else {
|
} else {
|
||||||
msgRunes += 100
|
msgRunes += 100
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -156,7 +156,7 @@ func TestMeasureContextRunes(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "unicode characters",
|
name: "unicode characters",
|
||||||
messages: []providers.Message{
|
messages: []providers.Message{
|
||||||
{Role: "user", Content: "你好世界"}, // 4 Chinese characters
|
{Role: "user", Content: "\u4f60\u597d\u4e16\u754c"}, // 4 Chinese characters
|
||||||
},
|
},
|
||||||
want: 4,
|
want: 4,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue