update non tool access
This commit is contained in:
parent
b9a66248d8
commit
7b44498b84
9 changed files with 274 additions and 5 deletions
|
|
@ -6,7 +6,8 @@
|
|||
"model": "gpt4",
|
||||
"max_tokens": 8192,
|
||||
"temperature": 0.7,
|
||||
"max_tool_iterations": 20
|
||||
"max_tool_iterations": 20,
|
||||
"enable_tools": true
|
||||
}
|
||||
},
|
||||
"model_list": [
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ type AgentInstance struct {
|
|||
MaxTokens int
|
||||
Temperature float64
|
||||
ContextWindow int
|
||||
EnableTools bool
|
||||
Provider providers.LLMProvider
|
||||
Sessions *session.SessionManager
|
||||
ContextBuilder *ContextBuilder
|
||||
|
|
@ -45,6 +46,7 @@ func NewAgentInstance(
|
|||
|
||||
model := resolveAgentModel(agentCfg, defaults)
|
||||
fallbacks := resolveAgentFallbacks(agentCfg, defaults)
|
||||
enableTools := resolveAgentEnableTools(agentCfg, defaults)
|
||||
|
||||
restrict := defaults.RestrictToWorkspace
|
||||
toolsRegistry := tools.NewToolRegistry()
|
||||
|
|
@ -105,6 +107,7 @@ func NewAgentInstance(
|
|||
MaxTokens: maxTokens,
|
||||
Temperature: temperature,
|
||||
ContextWindow: maxTokens,
|
||||
EnableTools: enableTools,
|
||||
Provider: provider,
|
||||
Sessions: sessionsManager,
|
||||
ContextBuilder: contextBuilder,
|
||||
|
|
@ -144,6 +147,18 @@ func resolveAgentFallbacks(agentCfg *config.AgentConfig, defaults *config.AgentD
|
|||
return defaults.ModelFallbacks
|
||||
}
|
||||
|
||||
// resolveAgentEnableTools resolves whether tool calling is enabled for an agent.
|
||||
func resolveAgentEnableTools(agentCfg *config.AgentConfig, defaults *config.AgentDefaults) bool {
|
||||
enabled := true
|
||||
if defaults != nil && defaults.EnableTools != nil {
|
||||
enabled = *defaults.EnableTools
|
||||
}
|
||||
if agentCfg != nil && agentCfg.EnableTools != nil {
|
||||
enabled = *agentCfg.EnableTools
|
||||
}
|
||||
return enabled
|
||||
}
|
||||
|
||||
func expandHome(path string) string {
|
||||
if path == "" {
|
||||
return path
|
||||
|
|
|
|||
|
|
@ -93,3 +93,92 @@ func TestNewAgentInstance_DefaultsTemperatureWhenUnset(t *testing.T) {
|
|||
t.Fatalf("Temperature = %f, want %f", agent.Temperature, 0.7)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewAgentInstance_EnableToolsDefaultsToTrue(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "agent-instance-test-*")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
cfg := &config.Config{
|
||||
Agents: config.AgentsConfig{
|
||||
Defaults: config.AgentDefaults{
|
||||
Workspace: tmpDir,
|
||||
Model: "test-model",
|
||||
MaxTokens: 1234,
|
||||
MaxToolIterations: 5,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
provider := &mockProvider{}
|
||||
agent := NewAgentInstance(nil, &cfg.Agents.Defaults, cfg, provider)
|
||||
|
||||
if !agent.EnableTools {
|
||||
t.Fatal("EnableTools = false, want true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewAgentInstance_EnableToolsFromDefaults(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "agent-instance-test-*")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
disabled := false
|
||||
cfg := &config.Config{
|
||||
Agents: config.AgentsConfig{
|
||||
Defaults: config.AgentDefaults{
|
||||
Workspace: tmpDir,
|
||||
Model: "test-model",
|
||||
MaxTokens: 1234,
|
||||
MaxToolIterations: 5,
|
||||
EnableTools: &disabled,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
provider := &mockProvider{}
|
||||
agent := NewAgentInstance(nil, &cfg.Agents.Defaults, cfg, provider)
|
||||
|
||||
if agent.EnableTools {
|
||||
t.Fatal("EnableTools = true, want false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewAgentInstance_EnableToolsAgentOverride(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "agent-instance-test-*")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
disabled := false
|
||||
enabled := true
|
||||
cfg := &config.Config{
|
||||
Agents: config.AgentsConfig{
|
||||
Defaults: config.AgentDefaults{
|
||||
Workspace: tmpDir,
|
||||
Model: "test-model",
|
||||
MaxTokens: 1234,
|
||||
MaxToolIterations: 5,
|
||||
EnableTools: &disabled,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
agentCfg := &config.AgentConfig{
|
||||
ID: "main",
|
||||
Default: true,
|
||||
EnableTools: &enabled,
|
||||
}
|
||||
|
||||
provider := &mockProvider{}
|
||||
agent := NewAgentInstance(agentCfg, &cfg.Agents.Defaults, cfg, provider)
|
||||
|
||||
if !agent.EnableTools {
|
||||
t.Fatal("EnableTools = false, want true")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -486,8 +486,11 @@ func (al *AgentLoop) runLLMIteration(
|
|||
"max": agent.MaxIterations,
|
||||
})
|
||||
|
||||
// Build tool definitions
|
||||
providerToolDefs := agent.Tools.ToProviderDefs()
|
||||
// Build tool definitions (chat-only mode omits tools entirely).
|
||||
var providerToolDefs []providers.ToolDefinition
|
||||
if agent.EnableTools {
|
||||
providerToolDefs = agent.Tools.ToProviderDefs()
|
||||
}
|
||||
|
||||
// Log LLM request details
|
||||
logger.DebugCF("agent", "LLM request",
|
||||
|
|
@ -497,6 +500,7 @@ func (al *AgentLoop) runLLMIteration(
|
|||
"model": agent.Model,
|
||||
"messages_count": len(messages),
|
||||
"tools_count": len(providerToolDefs),
|
||||
"tools_enabled": agent.EnableTools,
|
||||
"max_tokens": agent.MaxTokens,
|
||||
"temperature": agent.Temperature,
|
||||
"system_prompt_len": len(messages[0].Content),
|
||||
|
|
@ -590,6 +594,25 @@ func (al *AgentLoop) runLLMIteration(
|
|||
return "", iteration, fmt.Errorf("LLM call failed after retries: %w", err)
|
||||
}
|
||||
|
||||
if !agent.EnableTools {
|
||||
if len(response.ToolCalls) > 0 {
|
||||
logger.WarnCF("agent", "Ignoring tool calls in chat-only mode",
|
||||
map[string]any{
|
||||
"agent_id": agent.ID,
|
||||
"iteration": iteration,
|
||||
"tool_calls_received": len(response.ToolCalls),
|
||||
})
|
||||
}
|
||||
finalContent = response.Content
|
||||
logger.InfoCF("agent", "LLM response in chat-only mode",
|
||||
map[string]any{
|
||||
"agent_id": agent.ID,
|
||||
"iteration": iteration,
|
||||
"content_chars": len(finalContent),
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
// Check if no tool calls - we're done
|
||||
if len(response.ToolCalls) == 0 {
|
||||
finalContent = response.Content
|
||||
|
|
|
|||
|
|
@ -366,6 +366,29 @@ func (m *simpleMockProvider) GetDefaultModel() string {
|
|||
return "mock-model"
|
||||
}
|
||||
|
||||
type captureToolsProvider struct {
|
||||
response string
|
||||
lastToolsCount int
|
||||
}
|
||||
|
||||
func (m *captureToolsProvider) Chat(
|
||||
ctx context.Context,
|
||||
messages []providers.Message,
|
||||
tools []providers.ToolDefinition,
|
||||
model string,
|
||||
opts map[string]any,
|
||||
) (*providers.LLMResponse, error) {
|
||||
m.lastToolsCount = len(tools)
|
||||
return &providers.LLMResponse{
|
||||
Content: m.response,
|
||||
ToolCalls: []providers.ToolCall{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *captureToolsProvider) GetDefaultModel() string {
|
||||
return "mock-capture-model"
|
||||
}
|
||||
|
||||
// mockCustomTool is a simple mock tool for registration testing
|
||||
type mockCustomTool struct{}
|
||||
|
||||
|
|
@ -437,6 +460,92 @@ func (h testHelper) executeAndGetResponse(tb testing.TB, ctx context.Context, ms
|
|||
|
||||
const responseTimeout = 3 * time.Second
|
||||
|
||||
func TestAgentLoop_ChatOnlyMode_DoesNotPassTools(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "agent-test-*")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
disabled := false
|
||||
cfg := &config.Config{
|
||||
Agents: config.AgentsConfig{
|
||||
Defaults: config.AgentDefaults{
|
||||
Workspace: tmpDir,
|
||||
Model: "test-model",
|
||||
MaxTokens: 4096,
|
||||
MaxToolIterations: 10,
|
||||
EnableTools: &disabled,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
msgBus := bus.NewMessageBus()
|
||||
provider := &captureToolsProvider{response: "chat-only"}
|
||||
al := NewAgentLoop(cfg, msgBus, provider)
|
||||
|
||||
response, err := al.ProcessDirectWithChannel(
|
||||
context.Background(),
|
||||
"hello",
|
||||
"chat-only-session",
|
||||
"test",
|
||||
"chat",
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("ProcessDirectWithChannel() error = %v", err)
|
||||
}
|
||||
|
||||
if response != "chat-only" {
|
||||
t.Fatalf("response = %q, want %q", response, "chat-only")
|
||||
}
|
||||
if provider.lastToolsCount != 0 {
|
||||
t.Fatalf("lastToolsCount = %d, want 0", provider.lastToolsCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentLoop_ToolsEnabled_PassesTools(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "agent-test-*")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
enabled := true
|
||||
cfg := &config.Config{
|
||||
Agents: config.AgentsConfig{
|
||||
Defaults: config.AgentDefaults{
|
||||
Workspace: tmpDir,
|
||||
Model: "test-model",
|
||||
MaxTokens: 4096,
|
||||
MaxToolIterations: 10,
|
||||
EnableTools: &enabled,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
msgBus := bus.NewMessageBus()
|
||||
provider := &captureToolsProvider{response: "with-tools"}
|
||||
al := NewAgentLoop(cfg, msgBus, provider)
|
||||
|
||||
response, err := al.ProcessDirectWithChannel(
|
||||
context.Background(),
|
||||
"hello",
|
||||
"with-tools-session",
|
||||
"test",
|
||||
"chat",
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("ProcessDirectWithChannel() error = %v", err)
|
||||
}
|
||||
|
||||
if response != "with-tools" {
|
||||
t.Fatalf("response = %q, want %q", response, "with-tools")
|
||||
}
|
||||
if provider.lastToolsCount == 0 {
|
||||
t.Fatal("lastToolsCount = 0, want > 0")
|
||||
}
|
||||
}
|
||||
|
||||
// TestToolResult_SilentToolDoesNotSendUserMessage verifies silent tools don't trigger outbound
|
||||
func TestToolResult_SilentToolDoesNotSendUserMessage(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "agent-test-*")
|
||||
|
|
|
|||
|
|
@ -134,6 +134,7 @@ type AgentConfig struct {
|
|||
Name string `json:"name,omitempty"`
|
||||
Workspace string `json:"workspace,omitempty"`
|
||||
Model *AgentModelConfig `json:"model,omitempty"`
|
||||
EnableTools *bool `json:"enable_tools,omitempty"`
|
||||
Skills []string `json:"skills,omitempty"`
|
||||
Subagents *SubagentsConfig `json:"subagents,omitempty"`
|
||||
}
|
||||
|
|
@ -177,6 +178,7 @@ type AgentDefaults struct {
|
|||
MaxTokens int `json:"max_tokens" env:"PICOCLAW_AGENTS_DEFAULTS_MAX_TOKENS"`
|
||||
Temperature *float64 `json:"temperature,omitempty" env:"PICOCLAW_AGENTS_DEFAULTS_TEMPERATURE"`
|
||||
MaxToolIterations int `json:"max_tool_iterations" env:"PICOCLAW_AGENTS_DEFAULTS_MAX_TOOL_ITERATIONS"`
|
||||
EnableTools *bool `json:"enable_tools,omitempty" env:"PICOCLAW_AGENTS_DEFAULTS_ENABLE_TOOLS"`
|
||||
}
|
||||
|
||||
type ChannelsConfig struct {
|
||||
|
|
|
|||
|
|
@ -69,14 +69,16 @@ func TestAgentConfig_FullParse(t *testing.T) {
|
|||
"workspace": "~/.picoclaw/workspace",
|
||||
"model": "glm-4.7",
|
||||
"max_tokens": 8192,
|
||||
"max_tool_iterations": 20
|
||||
"max_tool_iterations": 20,
|
||||
"enable_tools": true
|
||||
},
|
||||
"list": [
|
||||
{
|
||||
"id": "sales",
|
||||
"default": true,
|
||||
"name": "Sales Bot",
|
||||
"model": "gpt-4"
|
||||
"model": "gpt-4",
|
||||
"enable_tools": false
|
||||
},
|
||||
{
|
||||
"id": "support",
|
||||
|
|
@ -125,6 +127,9 @@ func TestAgentConfig_FullParse(t *testing.T) {
|
|||
if sales.Model == nil || sales.Model.Primary != "gpt-4" {
|
||||
t.Errorf("sales.Model = %+v", sales.Model)
|
||||
}
|
||||
if sales.EnableTools == nil || *sales.EnableTools {
|
||||
t.Errorf("sales.EnableTools = %v, want false", sales.EnableTools)
|
||||
}
|
||||
|
||||
support := cfg.Agents.List[1]
|
||||
if support.ID != "support" || support.Name != "Support Bot" {
|
||||
|
|
@ -161,6 +166,9 @@ func TestAgentConfig_FullParse(t *testing.T) {
|
|||
if len(links) != 2 {
|
||||
t.Errorf("john links = %v", links)
|
||||
}
|
||||
if cfg.Agents.Defaults.EnableTools == nil || !*cfg.Agents.Defaults.EnableTools {
|
||||
t.Errorf("Agents.Defaults.EnableTools = %v, want true", cfg.Agents.Defaults.EnableTools)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_BackwardCompat_NoAgentsList(t *testing.T) {
|
||||
|
|
@ -186,6 +194,9 @@ func TestConfig_BackwardCompat_NoAgentsList(t *testing.T) {
|
|||
if len(cfg.Bindings) != 0 {
|
||||
t.Errorf("bindings should be empty, got %d", len(cfg.Bindings))
|
||||
}
|
||||
if cfg.Agents.Defaults.EnableTools == nil || !*cfg.Agents.Defaults.EnableTools {
|
||||
t.Errorf("Agents.Defaults.EnableTools = %v, want true", cfg.Agents.Defaults.EnableTools)
|
||||
}
|
||||
}
|
||||
|
||||
// TestDefaultConfig_HeartbeatEnabled verifies heartbeat is enabled by default
|
||||
|
|
@ -233,6 +244,17 @@ func TestDefaultConfig_MaxToolIterations(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestDefaultConfig_EnableTools(t *testing.T) {
|
||||
cfg := DefaultConfig()
|
||||
|
||||
if cfg.Agents.Defaults.EnableTools == nil {
|
||||
t.Fatal("EnableTools should not be nil")
|
||||
}
|
||||
if !*cfg.Agents.Defaults.EnableTools {
|
||||
t.Error("EnableTools should default to true")
|
||||
}
|
||||
}
|
||||
|
||||
// TestDefaultConfig_Temperature verifies temperature has default value
|
||||
func TestDefaultConfig_Temperature(t *testing.T) {
|
||||
cfg := DefaultConfig()
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ func DefaultConfig() *Config {
|
|||
MaxTokens: 8192,
|
||||
Temperature: nil, // nil means use provider default
|
||||
MaxToolIterations: 20,
|
||||
EnableTools: boolPtr(true),
|
||||
},
|
||||
},
|
||||
Bindings: []AgentBinding{},
|
||||
|
|
@ -314,3 +315,7 @@ func DefaultConfig() *Config {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
func boolPtr(v bool) *bool {
|
||||
return &v
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,6 +84,9 @@ func ConvertConfig(data map[string]any) (*config.Config, []string, error) {
|
|||
if v, ok := getFloat(defaults, "max_tool_iterations"); ok {
|
||||
cfg.Agents.Defaults.MaxToolIterations = int(v)
|
||||
}
|
||||
if v, ok := getBool(defaults, "enable_tools"); ok {
|
||||
cfg.Agents.Defaults.EnableTools = &v
|
||||
}
|
||||
if v, ok := getString(defaults, "workspace"); ok {
|
||||
cfg.Agents.Defaults.Workspace = rewriteWorkspacePath(v)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue