Skip to content

Commit bf0e654

Browse files
committed
# cmd Package Changes
## main.go - Agent Reconciler Factory Pattern Implementation ### 1. Problem to Solve The original controller initialization pattern used direct struct initialization which made dependency injection difficult and error handling inconsistent. This created tight coupling between the main function and controller internal structure, making testing harder and reducing code maintainability. ### 2. User-Facing Changes - **Improved Error Handling**: More descriptive error messages during agent controller initialization - **Better Startup Reliability**: Proper validation of controller dependencies before startup - **Enhanced Logging**: Clearer error messages when agent reconciler creation fails ### 3. Implementation Details The change replaces direct struct initialization with a factory method pattern: ```diff - if err = (&agent.AgentReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - MCPManager: mcpManagerInstance, - }).SetupWithManager(mgr); err != nil { + agentReconciler, err := agent.NewAgentReconcilerForManager(mgr) + if err != nil { + setupLog.Error(err, "unable to create agent reconciler") + os.Exit(1) + } + if err = agentReconciler.SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Agent") os.Exit(1) } ``` The factory method `NewAgentReconcilerForManager()` encapsulates: - Dependency validation and injection - Error handling during initialization - Proper setup of internal reconciler state ### 4. How to Verify ```bash # Check that the ACP manager starts successfully kubectl logs deployment/acp-controller-manager -n acp-system # Look for successful controller registration messages: # "Starting Controller" controller="Agent" # "Starting workers" controller="Agent" worker count=1 # Verify agent controller is responsive kubectl get agents -o wide # Should show READY=true for any existing agents # Test agent creation to verify controller functionality kubectl apply -f - <<EOF apiVersion: acp.humanlayer.dev/v1alpha1 kind: Agent metadata: name: test-agent spec: system: "Test agent" llmRef: name: existing-llm EOF # Check agent status kubectl describe agent test-agent # Should show successful validation events without controller errors ```# internal/controller Package Changes ## agent Package - State Machine Architecture Refactor ### 1. Problem to Solve The original agent controller had monolithic validation logic embedded directly in the reconcile loop, making it difficult to test, debug, and maintain. Error handling was inconsistent and the controller lacked clear separation between validation and reconciliation phases. ### 2. User-Facing Changes - **Better Error Messages**: More descriptive status messages when agent validation fails - **Improved Status Reporting**: Clear indication of which dependencies are missing or not ready - **Faster Recovery**: Better handling of dependency resolution when sub-resources become available - **Enhanced Debugging**: Clearer event logging for troubleshooting agent issues ### 3. Implementation Details Major refactoring to implement state machine pattern with separated concerns: ```diff // agent_controller.go - Before: Monolithic validation -func (r *AgentReconciler) validateLLM(ctx context.Context, agent *acp.Agent) error { - llm := &acp.LLM{} - err := r.Get(ctx, client.ObjectKey{ - Namespace: agent.Namespace, - Name: agent.Spec.LLMRef.Name, - }, llm) - if err != nil { - return fmt.Errorf("failed to get LLM %q: %w", agent.Spec.LLMRef.Name, err) - } - - if llm.Status.Status != StatusReady { - return fmt.Errorf("LLM %q is not ready", agent.Spec.LLMRef.Name) - } - - return nil -} // After: State machine with clear phases +type AgentReconciler struct { + client.Client + Scheme *runtime.Scheme + recorder record.EventRecorder +} + +// NewAgentReconcilerForManager creates an AgentReconciler with proper dependency injection +func NewAgentReconcilerForManager(mgr ctrl.Manager) (*AgentReconciler, error) { + return &AgentReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + recorder: mgr.GetEventRecorderFor("agent-controller"), + }, nil +} ``` ### 4. How to Verify ```bash # Check agent controller logs for state machine transitions kubectl logs deployment/acp-controller-manager -n acp-system | grep "agent-controller" # Create an agent with missing dependencies to test error handling kubectl apply -f - <<EOF apiVersion: acp.humanlayer.dev/v1alpha1 kind: Agent metadata: name: test-agent-missing-llm spec: system: "Test agent" llmRef: name: nonexistent-llm EOF # Check agent status shows clear error message kubectl describe agent test-agent-missing-llm # Should show events like: "waiting for LLM nonexistent-llm (not found)" # Verify agent becomes ready when dependencies are satisfied kubectl apply -f - <<EOF apiVersion: acp.humanlayer.dev/v1alpha1 kind: LLM metadata: name: nonexistent-llm spec: provider: openai parameters: model: gpt-4 EOF # Agent should automatically reconcile and become ready kubectl get agent test-agent-missing-llm -o wide # Should show READY=true after LLM becomes available ``` ## task Package - State Machine and Interface Refactoring ### 1. Problem to Solve The task controller had become a monolithic controller with over 900 lines of complex reconciliation logic. Dependencies were tightly coupled, making testing difficult and the code hard to maintain. Tool collection and execution logic was embedded in the main controller. ### 2. User-Facing Changes - **Improved Task Reliability**: Better error handling and recovery from failed tool calls - **Enhanced Observability**: Clearer task phase transitions and status reporting - **Better Timeout Handling**: Configurable timeouts for LLM requests and human approvals - **Improved Tool Integration**: More reliable MCP server tool discovery and execution ### 3. Implementation Details Massive refactoring to extract state machine logic and introduce dependency injection: ```diff // task_controller.go - Before: Monolithic controller -// TaskReconciler reconciles a Task object -type TaskReconciler struct { - client.Client - Scheme *runtime.Scheme - recorder record.EventRecorder - newLLMClient func(ctx context.Context, llm acp.LLM, apiKey string) (llmclient.LLMClient, error) - MCPManager *mcpmanager.MCPServerManager - Tracer trace.Tracer -} // After: Clean interfaces and dependency injection +const ( + DefaultRequeueDelay = 5 * time.Second + HumanLayerAPITimeout = 10 * time.Second + LLMRequestTimeout = 30 * time.Second +) + +// MCPManager defines the interface for managing MCP servers and tools +type MCPManager interface { + GetTools(serverName string) ([]acp.MCPTool, bool) +} + +// LLMClientFactory defines the interface for creating LLM clients +type LLMClientFactory interface { + CreateClient(ctx context.Context, llm acp.LLM, apiKey string) (llmclient.LLMClient, error) +} ``` State machine logic moved to separate file with clear phase transitions: - `state_machine.go`: 858 lines of extracted reconciliation logic - `task_helpers.go`: 81 lines of utility functions - `types/update_types.go`: 62 lines of type definitions ### 4. How to Verify ```bash # Test task creation with state machine transitions kubectl apply -f - <<EOF apiVersion: acp.humanlayer.dev/v1alpha1 kind: Task metadata: name: test-task-state-machine spec: agentRef: name: existing-agent userMessage: "What is 2+2?" EOF # Monitor task phase transitions kubectl get task test-task-state-machine -w # Should show phases: Initializing -> Validating -> SendingToLLM -> FinalAnswer # Check detailed task status kubectl describe task test-task-state-machine # Should show clear phase transitions in events # Test tool call creation kubectl apply -f - <<EOF apiVersion: acp.humanlayer.dev/v1alpha1 kind: Task metadata: name: test-task-with-tools spec: agentRef: name: agent-with-mcp-tools userMessage: "Use the weather tool to check the weather in SF" EOF # Verify tool calls are created properly kubectl get toolcalls -l task=test-task-with-tools # Should show created toolcall resources with proper status ``` ## toolcall Package - Executor Pattern Implementation ### 1. Problem to Solve Tool call execution logic was embedded in a massive 1100+ line controller file, making it nearly impossible to test individual execution paths. Different tool types (MCP, HumanLayer, SubAgent) had inconsistent execution patterns. ### 2. User-Facing Changes - **Reliable Tool Execution**: Better error handling and retry logic for tool calls - **Consistent Tool Behavior**: Unified execution patterns across all tool types - **Improved Debugging**: Clearer error messages and execution status reporting - **Better Timeout Management**: Proper handling of long-running tool operations ### 3. Implementation Details Complete refactoring to extract execution logic into separate executor: ```diff // Before: Monolithic controller (1165 lines) -func (r *ToolCallReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - // 1100+ lines of mixed concerns - // Validation, execution, status updates all mixed together -} // After: Clean separation with executor pattern +// executor.go - 288 lines of focused execution logic +type ToolCallExecutor struct { + client client.Client + mcpManager *mcpmanager.MCPServerManager + humanLayerClientFactory func(baseURL string) (humanlayer.HumanLayerClientWrapper, error) +} + +func (e *ToolCallExecutor) ExecuteToolCall(ctx context.Context, toolCall *acp.ToolCall) error { + switch toolCall.Spec.Type { + case acp.ToolCallTypeMCP: + return e.executeMCPToolCall(ctx, toolCall) + case acp.ToolCallTypeHumanLayer: + return e.executeHumanLayerToolCall(ctx, toolCall) + case acp.ToolCallTypeSubAgent: + return e.executeSubAgentToolCall(ctx, toolCall) + default: + return fmt.Errorf("unknown tool call type: %s", toolCall.Spec.Type) + } +} ``` State machine moved to `state_machine.go` (352 lines) with clear phase management. ### 4. How to Verify ```bash # Test MCP tool call execution kubectl apply -f - <<EOF apiVersion: acp.humanlayer.dev/v1alpha1 kind: ToolCall metadata: name: test-mcp-toolcall spec: type: mcp mcpServerName: test-server functionName: get_weather arguments: '{"location": "San Francisco"}' EOF # Monitor toolcall execution phases kubectl get toolcall test-mcp-toolcall -w # Should show: Pending -> Executing -> Completed # Test human approval tool call kubectl apply -f - <<EOF apiVersion: acp.humanlayer.dev/v1alpha1 kind: ToolCall metadata: name: test-human-toolcall spec: type: humanlayer contactChannelRef: name: test-channel humanLayerFunctionName: request_approval arguments: '{"message": "Please approve this action"}' EOF # Check toolcall status for human interaction kubectl describe toolcall test-human-toolcall # Should show "Waiting for human response" in status detail # Verify executor error handling kubectl logs deployment/acp-controller-manager -n acp-system | grep "toolcall-controller" # Should show clear execution logs without stack traces ```# internal/humanlayer Package Changes ## hlclient.go - Request Approval ID Generation Fix ### 1. Problem to Solve The HumanLayer API requires a non-empty `call_id` field for approval requests, and the combination of `run_id + call_id` must be ≤ 64 bytes. The original implementation was using an empty `callID` which caused API validation failures when requesting human approvals. ### 2. User-Facing Changes - **Reliable Human Approvals**: Human approval requests now work correctly without API validation errors - **Better Error Messages**: Clear error messages when random ID generation fails - **Consistent Behavior**: All approval requests get unique, valid identifiers ### 3. Implementation Details Added secure random ID generation for approval requests: ```diff // hlclient.go - Before: Empty callID causing API failures - functionCallInput := humanlayerapi.NewFunctionCallInput(h.runID, h.callID, *h.functionCallSpecInput) // After: Generate unique callID for approval requests + // For initial approval requests, generate a short unique callID since the API requires it to be non-empty + // and the combination of run_id + call_id must be <= 64 bytes + randomBytes := make([]byte, 8) + if _, err := rand.Read(randomBytes); err != nil { + return nil, 0, fmt.Errorf("failed to generate random call ID: %w", err) + } + callID := hex.EncodeToString(randomBytes) // 16 character hex string + functionCallInput := humanlayerapi.NewFunctionCallInput(h.runID, callID, *h.functionCallSpecInput) ``` The fix: - Generates 8 random bytes and converts to 16-character hex string - Ensures `run_id + call_id` stays under 64 byte limit - Provides proper error handling for random generation failures - Makes each approval request uniquely identifiable ### 4. How to Verify ```bash # Create a task that requires human approval kubectl apply -f - <<EOF apiVersion: acp.humanlayer.dev/v1alpha1 kind: Task metadata: name: test-human-approval spec: agentRef: name: agent-with-human-tools userMessage: "Please ask for approval before proceeding" EOF # Check that toolcalls are created without API errors kubectl get toolcalls -l task=test-human-approval # Should show toolcall in "Pending" or "WaitingForHuman" status, not "Failed" # Check toolcall details for proper callID generation kubectl describe toolcall $(kubectl get toolcalls -l task=test-human-approval -o name | head -1) # Should show status without "API validation failed" errors # Verify in controller logs that approval requests succeed kubectl logs deployment/acp-controller-manager -n acp-system | grep -A5 -B5 "RequestApproval" # Should show successful API calls without "empty call_id" errors # Test multiple approval requests have unique IDs kubectl apply -f - <<EOF apiVersion: acp.humanlayer.dev/v1alpha1 kind: Task metadata: name: test-multiple-approvals spec: agentRef: name: agent-with-human-tools userMessage: "Ask for approval twice" EOF # Check that multiple toolcalls have different generated IDs kubectl get toolcalls -l task=test-multiple-approvals -o yaml | grep callID # Should show different hex strings for each toolcall ``` ## mock_hlclient.go - Human Contact Mock Implementation ### 1. Problem to Solve The mock implementation for `RequestHumanContact` was incomplete, always returning nil and causing test failures. This made it impossible to properly test human contact functionality without hitting real HumanLayer APIs. ### 2. User-Facing Changes - **Reliable Testing**: Human contact functionality can be tested without external dependencies - **Better Test Coverage**: Enables comprehensive testing of human contact workflows - **Consistent Mock Behavior**: Mock responses match real API response structure ### 3. Implementation Details Enhanced mock to return proper success responses: ```diff // mock_hlclient.go - Before: Incomplete mock implementation -func (m *MockHumanLayerClientWrapper) RequestHumanContact(ctx context.Context, userMsg string) (*humanlayerapi.HumanContactOutput, int, error) { - return nil, m.parent.StatusCode, m.parent.ReturnError -} // After: Complete mock with proper response structure +func (m *MockHumanLayerClientWrapper) RequestHumanContact(ctx context.Context, userMsg string) (*humanlayerapi.HumanContactOutput, int, error) { + if m.parent.ShouldFail { + return nil, m.parent.StatusCode, m.parent.ReturnError + } + + // Return a successful mock response + output := humanlayerapi.NewHumanContactOutput(m.runID, m.callID, *humanlayerapi.NewHumanContactSpecOutput(userMsg)) + return output, m.parent.StatusCode, nil +} ``` The enhancement: - Respects the `ShouldFail` test configuration for error simulation - Returns properly structured `HumanContactOutput` for success cases - Includes the original user message in the response - Maintains consistency with other mock methods ### 4. How to Verify ```bash # Run unit tests that use human contact mocks cd acp && make test # Look for successful test execution without mock-related failures # Tests should pass for human contact functionality # Verify mock behavior in test logs cd acp && go test -v ./internal/humanlayer/ -run TestMockHumanContact # Should show successful mock responses without nil pointer errors # Test that both success and failure scenarios work cd acp && go test -v ./internal/controller/toolcall/ -run TestHumanContact # Should test both ShouldFail=true and ShouldFail=false cases # Verify integration tests work with mocks cd acp && make test | grep -i "human.*contact" # Should show passing tests for human contact functionality ```# internal/llmclient Package Changes ## Mock Generation Infrastructure Replacement ### 1. Problem to Solve The previous mock client implementation was manually written and became outdated as the LLM client interface evolved. Manual mocks are error-prone, hard to maintain, and often don't match the actual interface signatures, leading to test failures and reduced confidence in testing. ### 2. User-Facing Changes - **Better Test Reliability**: Tests using LLM clients are more reliable and catch interface changes - **Improved Development Workflow**: Developers can run tests without LLM API keys - **Enhanced CI/CD**: Automated testing without external dependencies or API rate limits ### 3. Implementation Details Replaced manual mock with generated mock infrastructure: ```diff // Before: Manual mock implementation (54 lines of hand-written code) -package llmclient - -import ( - "context" - acp "github.com/humanlayer/agentcontrolplane/acp/api/v1alpha1" -) - -// MockLLMClient is a mock implementation of LLMClient for testing -type MockLLMClient struct { - Response *acp.Message - Error error - Calls []MockCall - ValidateTools func(tools []Tool) error - ValidateContextWindow func(contextWindow []acp.Message) error -} // After: Generated mock infrastructure in Makefile +.PHONY: mocks +mocks: mockgen ## Generate all mocks using mockgen + @echo "Generating mocks..." + $(MOCKGEN) -source=internal/llmclient/llm_client.go -destination=internal/llmclient/mocks/mock_llm_client.go -package=mocks + @echo "Mock generation complete" + +.PHONY: clean-mocks +clean-mocks: ## Remove all generated mock files + @echo "Cleaning mocks..." + rm -rf internal/llmclient/mocks/ + @echo "Mock cleanup complete" ``` The new approach: - Uses `go.uber.org/mock/mockgen` for automatic generation - Generates mocks from actual interface definitions - Ensures type safety and interface compliance - Supports complex method signatures and return types - Automatically updates when interfaces change ### 4. How to Verify ```bash # Generate fresh mocks cd acp && make clean-mocks && make mocks # Verify mock files are generated correctly ls -la acp/internal/llmclient/mocks/ # Should show mock_llm_client.go with recent timestamp # Run tests that use LLM client mocks cd acp && go test -v ./internal/controller/task/ -run TestLLMClient # Should pass with generated mocks # Verify mock generation is included in build process cd acp && make deps mocks build # Should complete without errors # Check that mocks implement the correct interface cd acp && go test -compile-only ./internal/llmclient/mocks/ # Should compile without interface compliance errors # Test mock flexibility in unit tests cd acp && go test -v ./internal/controller/ -run TestMock # Should show various mock configurations working correctly ```# internal/mcpmanager Package Changes ## MCP Manager Interface Extraction and Testing Enhancement ### 1. Problem to Solve The MCP manager had tightly coupled implementations making it difficult to test controllers that depend on MCP functionality. Environment variable handling was complex and error-prone without proper validation. ### 2. User-Facing Changes - **Improved MCP Server Reliability**: Better validation of MCP server configurations - **Enhanced Error Messages**: Clearer error reporting when MCP servers fail to start - **Better Tool Discovery**: More reliable tool collection from MCP servers ### 3. Implementation Details Extracted interfaces and enhanced testing: ```diff // mcpmanager.go - Better interface design +type MCPServerManager interface { + GetTools(serverName string) ([]acp.MCPTool, bool) + ConnectToServer(ctx context.Context, server *acp.MCPServer) error + DisconnectFromServer(serverName string) error +} // envvar_test.go - Enhanced environment variable testing +func TestEnvironmentVariableExpansion(t *testing.T) { + tests := []struct { + name string + envVars []acp.EnvVar + expected []string + wantErr bool + }{ + // Test cases for various env var scenarios + } +} ``` ### 4. How to Verify ```bash # Test MCP server connection with environment variables kubectl apply -f - <<EOF apiVersion: acp.humanlayer.dev/v1alpha1 kind: MCPServer metadata: name: test-mcp-env spec: transport: stdio command: python3 args: ["-m", "mcp_server"] env: - name: API_KEY value: "test-key" - name: HOST value: "localhost" EOF # Check MCP server status kubectl describe mcpserver test-mcp-env # Should show successful connection with proper env var handling # Run MCP manager tests cd acp && go test -v ./internal/mcpmanager/ # Should pass all environment variable and connection tests ```# General Project Changes ## Development Infrastructure Modernization ### 1. Problem to Solve The project lacked consistent development tooling, had outdated documentation, and missing automation for common development tasks. Developers faced inconsistent setup experiences and manual processes that were error-prone. ### 2. User-Facing Changes - **Streamlined Development Setup**: New developers can get started faster with clear tooling - **Better Documentation**: Updated guides reflect current development practices - **Improved Build Process**: Automated dependency management and mock generation - **Enhanced IDE Support**: Better code completion and linting integration ### 3. Implementation Details Comprehensive infrastructure updates: ```diff // Makefile - Before: Basic build targets -build: manifests generate fmt vet - go build -o bin/manager cmd/main.go // After: Comprehensive development workflow +.PHONY: deps +deps: ## Install dependencies + go mod tidy + go mod download + go mod verify + +.PHONY: mocks +mocks: mockgen ## Generate all mocks using mockgen + @echo "Generating mocks..." + $(MOCKGEN) -source=internal/humanlayer/hlclient.go -destination=internal/humanlayer/mocks/mock_hlclient.go -package=mocks + $(MOCKGEN) -source=internal/llmclient/llm_client.go -destination=internal/llmclient/mocks/mock_llm_client.go -package=mocks + $(MOCKGEN) -source=internal/mcpmanager/mcpmanager.go -destination=internal/mcpmanager/mocks/mock_mcpmanager.go -package=mocks + @echo "Mock generation complete" +.PHONY: clean-mocks +clean-mocks: ## Remove all generated mock files + @echo "Cleaning mocks..." + rm -rf internal/humanlayer/mocks/ + rm -rf internal/llmclient/mocks/ + rm -rf internal/mcpmanager/mocks/ + @echo "Mock cleanup complete" ``` Key improvements: - Added mock generation infrastructure with `mockgen` - Enhanced gitignore patterns for generated files - Updated dependency management workflows - Improved local development deployment options - Added comprehensive tooling documentation ### 4. How to Verify ```bash # Test complete development setup from scratch git clone <repo> && cd agentcontrolplane # Verify dependency installation cd acp && make deps # Should complete without errors # Test mock generation cd acp && make mocks # Should generate mock files in internal/*/mocks/ directories # Test build process cd acp && make build # Should compile successfully # Test local deployment cd acp && make deploy-local-kind # Should deploy to local kind cluster # Verify gitignore effectiveness git status # Should not show generated files as untracked # Test cleanup cd acp && make clean-mocks # Should remove all generated mock files ``` ## Documentation and Project Structure Updates ### 1. Problem to Solve Outdated documentation, inconsistent project structure, and missing developer onboarding materials made it difficult for new contributors to understand and work with the codebase. ### 2. User-Facing Changes - **Better Getting Started Experience**: Updated guides with current examples - **Clearer Project Structure**: Organized documentation and examples - **Enhanced Development Workflow**: Clear instructions for common tasks ### 3. Implementation Details Major documentation and structure updates: ```diff // Before: Outdated README and scattered docs -README.md (630 lines of potentially outdated content) -CONTRIBUTING.md (23 lines of basic info) -cli.md (76 lines of CLI documentation) // After: Organized structure +acp/docs/getting-started.md (comprehensive guide) +acp/README.md (focused on ACP specifics) +developer-todo-list.md (task tracking) +hack/agent-*.md (specialized documentation) +CLAUDE.md (AI assistant instructions) ``` Changes include: - Moved main README content to `acp/docs/getting-started.md` - Removed outdated `CONTRIBUTING.md` - Added developer-focused documentation in `hack/` directory - Created task tracking and development guides - Updated configuration files for better local development ### 4. How to Verify ```bash # Check documentation accessibility ls -la acp/docs/ # Should show getting-started.md with recent updates # Verify developer docs ls -la hack/agent-*.md # Should show specialized documentation files # Test getting started guide follow instructions in acp/docs/getting-started.md # Should successfully create and deploy example agents # Check configuration updates kubectl apply -f acp/config/localdev/ # Should deploy with updated configurations ```
1 parent 81e6ffa commit bf0e654

77 files changed

Lines changed: 8138 additions & 4376 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.cursorrules

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CLAUDE.md

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@
99
# Generated files
1010
acp_commands.sh
1111

12+
13+
acp/config/tmp/

CLAUDE.md

Lines changed: 0 additions & 135 deletions
This file was deleted.

CONTRIBUTING.md

Lines changed: 0 additions & 23 deletions
This file was deleted.

Makefile

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,44 @@ example-%: ## Run any acp-example Makefile target: make example-<target>
2727

2828
build: acp-build ## Build acp components
2929

30+
31+
branchname := $(shell git branch --show-current)
32+
dirname := $(shell basename ${PWD})
33+
setup:
34+
@echo "BRANCH: ${branchname}"
35+
@echo "DIRNAME: ${dirname}"
36+
37+
$(MAKE) -C $(ACP_DIR) mocks deps
38+
39+
worktree-cluster:
40+
# replicated cluster create --distribution kind --instance-type r1.small --disk 50 --version 1.33.1 --wait 5m --name ${dirname}
41+
# replicated cluster kuebconfig ${dirname} --output ./kubeconfig
42+
# kubectl --kubeconfig ./kubeconfig get node
43+
# kubectl --kubeconfig ./kubeconfig create secret generic openai --from-literal=OPENAI_API_KEY=${OPENAI_API_KEY}
44+
# kubectl --kubeconfig ./kubeconfig create secret generic anthropic --from-literal=ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
45+
# kubectl --kubeconfig ./kubeconfig create secret generic humanlayer --from-literal=HUMANLAYER_API_KEY=${HUMANLAYER_API_KEY}
46+
# KUBECONFIG=./kubeconfig $(MAKE) -C $(ACP_DIR) generate deploy-local-kind
47+
48+
check:
49+
# $(MAKE) -C $(ACP_DIR) fmt vet lint test generate
50+
3051
test: acp-test ## Run tests for acp components
3152

53+
check-keys-set:
54+
@if [ -z "${HUMANLAYER_API_KEY}" ]; then \
55+
echo "HUMANLAYER_API_KEY is not set"; \
56+
exit 1; \
57+
fi
58+
@if [ -z "${OPENAI_API_KEY}" ]; then \
59+
echo "OPENAI_API_KEY is not set"; \
60+
exit 1; \
61+
fi
62+
@if [ -z "${ANTHROPIC_API_KEY}" ]; then \
63+
echo "ANTHROPIC_API_KEY is not set"; \
64+
exit 1; \
65+
fi
66+
@echo "Keys are set"
67+
3268
##@ Cluster Management
3369

3470
cluster-up: ## Create the Kind cluster

acp/.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,11 @@ go.work
3232
# macOS system files
3333
.DS_Store
3434
**/.DS_Store
35+
36+
# Generated mock files
37+
**/mocks/
38+
internal/**/mocks/
39+
internal/controller/toolcall/services/mocks/
40+
internal/humanlayer/mocks/
41+
internal/llmclient/mocks/
42+
internal/mcpmanager/mocks/

acp/Makefile

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,29 @@ lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes
9393
lint-config: golangci-lint ## Verify golangci-lint linter configuration
9494
$(GOLANGCI_LINT) config verify
9595

96-
.PHONY: ask-dex
97-
ask-dex: ## Ask Dex a question about the project
98-
cd hack/ask-dex && bun ask-dex.js $(filter-out $@,$(MAKECMDGOALS))
99-
96+
.PHONY: mocks
97+
mocks: mockgen ## Generate all mocks using mockgen
98+
@echo "Generating mocks..."
99+
$(MOCKGEN) -source=internal/humanlayer/hlclient.go -destination=internal/humanlayer/mocks/mock_hlclient.go -package=mocks
100+
$(MOCKGEN) -source=internal/llmclient/llm_client.go -destination=internal/llmclient/mocks/mock_llm_client.go -package=mocks
101+
$(MOCKGEN) -source=internal/mcpmanager/mcpmanager.go -destination=internal/mcpmanager/mocks/mock_mcpmanager.go -package=mocks
102+
@echo "Mock generation complete"
103+
104+
.PHONY: clean-mocks
105+
clean-mocks: ## Remove all generated mock files
106+
@echo "Cleaning mocks..."
107+
rm -rf internal/humanlayer/mocks/
108+
rm -rf internal/llmclient/mocks/
109+
rm -rf internal/mcpmanager/mocks/
110+
@echo "Mock cleanup complete"
100111
##@ Build
101112

113+
.PHONY: deps
114+
deps: ## Install dependencies
115+
go mod tidy
116+
go mod download
117+
go mod verify
118+
102119
.PHONY: build
103120
build: manifests generate fmt vet ## Build manager binary.
104121
go build -o bin/manager cmd/main.go
@@ -192,10 +209,11 @@ deploy: manifests docker-build kustomize ## Deploy controller to the K8s cluster
192209
cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
193210
$(KUSTOMIZE) build config/default | $(KUBECTL) apply -f -
194211

212+
namespace ?= default
195213
.PHONY: deploy-local-kind
196214
deploy-local-kind: manifests docker-build docker-load-kind kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config.
197215
cd config/localdev && $(KUSTOMIZE) edit set image controller=${IMG}
198-
$(KUSTOMIZE) build config/localdev | $(KUBECTL) apply -f -
216+
$(KUSTOMIZE) build config/localdev | $(KUBECTL) apply -f - --namespace=$(namespace)
199217

200218
.PHONY: deploy-samples
201219
deploy-samples: kustomize ## Deploy samples to the K8s cluster specified in ~/.kube/config.
@@ -232,6 +250,7 @@ KUSTOMIZE ?= $(LOCALBIN)/kustomize
232250
CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
233251
ENVTEST ?= $(LOCALBIN)/setup-envtest
234252
GOLANGCI_LINT = $(LOCALBIN)/golangci-lint
253+
MOCKGEN = $(LOCALBIN)/mockgen
235254

236255
## Tool Versions
237256
KUSTOMIZE_VERSION ?= v5.5.0
@@ -241,6 +260,7 @@ ENVTEST_VERSION ?= $(shell go list -m -f "{{ .Version }}" sigs.k8s.io/controller
241260
#ENVTEST_K8S_VERSION is the version of Kubernetes to use for setting up ENVTEST binaries (i.e. 1.31)
242261
ENVTEST_K8S_VERSION ?= $(shell go list -m -f "{{ .Version }}" k8s.io/api | awk -F'[v.]' '{printf "1.%d", $$3}')
243262
GOLANGCI_LINT_VERSION ?= v1.63.4
263+
MOCKGEN_VERSION ?= v0.5.0
244264

245265
.PHONY: kustomize
246266
kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary.
@@ -270,6 +290,11 @@ golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
270290
$(GOLANGCI_LINT): $(LOCALBIN)
271291
$(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION))
272292

293+
.PHONY: mockgen
294+
mockgen: $(MOCKGEN) ## Download mockgen locally if necessary.
295+
$(MOCKGEN): $(LOCALBIN)
296+
$(call go-install-tool,$(MOCKGEN),go.uber.org/mock/mockgen,$(MOCKGEN_VERSION))
297+
273298
# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist
274299
# $1 - target path with name of binary
275300
# $2 - package url which can be installed
@@ -282,6 +307,7 @@ echo "Downloading $${package}" ;\
282307
rm -f $(1) || true ;\
283308
GOBIN=$(LOCALBIN) go install $${package} ;\
284309
mv $(1) $(1)-$(3) ;\
310+
chmod 755 $(1)-$(3) ;\
285311
} ;\
286312
ln -sf $(1)-$(3) $(1)
287313
endef

acp/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,21 @@ Run `make help` for more information on all potential `make` targets. Common tar
132132
- `make manifests` - Generate WebhookConfiguration, ClusterRole, and CustomResourceDefinition objects
133133
- `make generate` - Generate code (DeepCopy methods)
134134
- `make test` - Run tests
135+
- `make mocks` - Generate mock implementations for testing (not committed to git)
135136
- `make docker-build` - Build the Docker image
136137

138+
#### Mock Generation
139+
140+
The project uses generated mocks for testing interfaces. Mock files are automatically generated via `make mocks` and are **not committed to version control**. They are recreated locally as needed for testing.
141+
142+
```sh
143+
# Generate all mock files
144+
make mocks
145+
146+
# Clean and regenerate mocks
147+
make clean-mocks && make mocks
148+
```
149+
137150
### Resources
138151

139152
- [Kubebuilder Book](https://book.kubebuilder.io/introduction.html) - Official Kubebuilder documentation

acp/cmd/main.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -240,11 +240,12 @@ func main() {
240240
// Create a shared MCPManager that all controllers will use
241241
mcpManagerInstance := mcpmanager.NewMCPServerManagerWithClient(mgr.GetClient())
242242

243-
if err = (&agent.AgentReconciler{
244-
Client: mgr.GetClient(),
245-
Scheme: mgr.GetScheme(),
246-
MCPManager: mcpManagerInstance,
247-
}).SetupWithManager(mgr); err != nil {
243+
agentReconciler, err := agent.NewAgentReconcilerForManager(mgr)
244+
if err != nil {
245+
setupLog.Error(err, "unable to create agent reconciler")
246+
os.Exit(1)
247+
}
248+
if err = agentReconciler.SetupWithManager(mgr); err != nil {
248249
setupLog.Error(err, "unable to create controller", "controller", "Agent")
249250
os.Exit(1)
250251
}

acp/config/localdev/kustomization.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,4 @@ patches:
2626
images:
2727
- name: controller
2828
newName: controller
29-
newTag: "202505211432"
29+
newTag: "202506131406"

0 commit comments

Comments
 (0)