INTRODUCTION AND PROBLEM ANALYSIS
Creating containerized applications and orchestrating them through Kubernetes requires deep expertise across multiple domains including container runtime configuration, networking, storage, security policies, and deployment strategies. Development teams often spend significant time writing Dockerfiles, Kubernetes manifests, Helm charts, and associated configuration files. An intelligent agent powered by Large Language Models can dramatically reduce this overhead by understanding natural language requirements and generating production-ready artifacts.
The core challenge involves building a system that can interpret user intentions expressed in natural language, maintain context about the application architecture, generate syntactically correct and semantically meaningful configuration files, and support iterative refinement. The agent must understand the relationships between different Kubernetes resources such as Deployments, Services, ConfigMaps, Secrets, Ingresses, and PersistentVolumeClaims. It must also generate appropriate Dockerfiles that follow best practices for layer caching, security, and minimal image sizes.
This article presents a comprehensive architecture for such an agent implemented in Go. The system supports both local and remote LLM providers, accommodates various GPU architectures including Intel, AMD ROCm, Apple Metal Performance Shaders, and Nvidia CUDA, and generates complete Docker and Kubernetes artifacts including Helm charts. The implementation follows clean architecture principles with clear separation of concerns, dependency injection, and comprehensive error handling.
ARCHITECTURAL OVERVIEW
The agent architecture consists of several key layers. The presentation layer handles user interactions through a command-line interface or API endpoints. The application layer contains the core business logic for interpreting requests, managing conversation context, and orchestrating artifact generation. The domain layer defines the entities and value objects representing Docker and Kubernetes concepts. The infrastructure layer provides adapters for different LLM providers, file system operations, and external tool integrations.
The system employs a plugin-based architecture for LLM providers, allowing seamless switching between local models running on various GPU architectures and remote API-based services. Each provider implements a common interface that abstracts the underlying communication protocol and response parsing. This design enables the agent to leverage the most appropriate model based on availability, cost, and performance requirements.
Context management is critical for maintaining coherent conversations across multiple interactions. The agent maintains a conversation history that includes user requests, generated artifacts, and validation results. This context allows the LLM to understand references to previously generated resources and make informed decisions about modifications and additions.
LLM PROVIDER ABSTRACTION
The foundation of the agent is a flexible abstraction for interacting with different LLM providers. This abstraction must handle the diversity of APIs, authentication mechanisms, and response formats while presenting a uniform interface to the application layer.
// Package llm provides abstractions for interacting with Large Language Models
package llm
import (
"context"
"time"
)
// Message represents a single message in a conversation
type Message struct {
Role string // "system", "user", or "assistant"
Content string // The message content
Timestamp time.Time // When the message was created
}
// CompletionRequest encapsulates parameters for generating a completion
type CompletionRequest struct {
Messages []Message // Conversation history
MaxTokens int // Maximum tokens to generate
Temperature float64 // Sampling temperature (0.0 to 2.0)
TopP float64 // Nucleus sampling parameter
StopSequences []string // Sequences that stop generation
PresencePenalty float64 // Penalty for token presence
Metadata map[string]string // Additional provider-specific parameters
}
// CompletionResponse contains the generated completion and metadata
type CompletionResponse struct {
Content string // Generated text
FinishReason string // Why generation stopped
TokensUsed int // Total tokens consumed
PromptTokens int // Tokens in the prompt
ModelUsed string // Actual model identifier
Metadata map[string]string // Provider-specific response data
}
// Provider defines the interface that all LLM providers must implement
type Provider interface {
// GenerateCompletion sends a request and returns the completion
GenerateCompletion(ctx context.Context, req CompletionRequest) (*CompletionResponse, error)
// GetCapabilities returns information about what this provider supports
GetCapabilities() ProviderCapabilities
// HealthCheck verifies the provider is accessible and functional
HealthCheck(ctx context.Context) error
// Close releases any resources held by the provider
Close() error
}
// ProviderCapabilities describes what a provider supports
type ProviderCapabilities struct {
SupportsStreaming bool // Can stream responses
MaxContextTokens int // Maximum context window size
SupportedModalities []string // "text", "image", "audio"
RequiresAPIKey bool // Whether authentication is needed
GPUAccelerated bool // Whether GPU acceleration is available
}
This interface provides a clean contract that all provider implementations must fulfill. The Message structure captures the conversational context, while CompletionRequest and CompletionResponse handle the request-response cycle with comprehensive metadata. The ProviderCapabilities structure allows the application layer to make informed decisions about which provider to use based on requirements.
LOCAL LLM PROVIDER IMPLEMENTATION
Supporting local LLM execution requires interfacing with model serving frameworks that can leverage different GPU architectures. The implementation uses a unified approach that detects available hardware and configures the appropriate backend.
// Package locallm implements a provider for locally-hosted LLM inference
package locallm
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"runtime"
"time"
"agent/llm"
)
// LocalProvider implements the Provider interface for local model serving
type LocalProvider struct {
baseURL string // URL of the local inference server
modelName string // Name of the loaded model
client *http.Client // HTTP client for API calls
gpuBackend GPUBackend // Detected GPU backend
maxRetries int // Number of retry attempts
retryDelay time.Duration // Delay between retries
}
// GPUBackend represents the detected GPU acceleration backend
type GPUBackend string
const (
GPUBackendCUDA GPUBackend = "cuda" // Nvidia CUDA
GPUBackendROCm GPUBackend = "rocm" // AMD ROCm
GPUBackendMPS GPUBackend = "mps" // Apple Metal Performance Shaders
GPUBackendIntel GPUBackend = "intel" // Intel GPU
GPUBackendCPU GPUBackend = "cpu" // CPU fallback
)
// LocalProviderConfig contains configuration for the local provider
type LocalProviderConfig struct {
BaseURL string
ModelName string
Timeout time.Duration
MaxRetries int
RetryDelay time.Duration
ForceBackend GPUBackend // Optional: force a specific backend
}
// NewLocalProvider creates a new local LLM provider instance
func NewLocalProvider(config LocalProviderConfig) (*LocalProvider, error) {
if config.BaseURL == "" {
return nil, fmt.Errorf("base URL is required")
}
if config.ModelName == "" {
return nil, fmt.Errorf("model name is required")
}
if config.Timeout == 0 {
config.Timeout = 120 * time.Second
}
if config.MaxRetries == 0 {
config.MaxRetries = 3
}
if config.RetryDelay == 0 {
config.RetryDelay = 2 * time.Second
}
backend := config.ForceBackend
if backend == "" {
backend = detectGPUBackend()
}
provider := &LocalProvider{
baseURL: config.BaseURL,
modelName: config.ModelName,
gpuBackend: backend,
maxRetries: config.MaxRetries,
retryDelay: config.RetryDelay,
client: &http.Client{
Timeout: config.Timeout,
},
}
return provider, nil
}
// detectGPUBackend attempts to detect the available GPU backend
func detectGPUBackend() GPUBackend {
// Check operating system and available libraries
switch runtime.GOOS {
case "darwin":
// macOS systems use Metal Performance Shaders
return GPUBackendMPS
case "linux", "windows":
// Check for CUDA availability
if checkCUDAAvailable() {
return GPUBackendCUDA
}
// Check for ROCm availability
if checkROCmAvailable() {
return GPUBackendROCm
}
// Check for Intel GPU
if checkIntelGPUAvailable() {
return GPUBackendIntel
}
}
// Fallback to CPU
return GPUBackendCPU
}
// checkCUDAAvailable checks if CUDA is available on the system
func checkCUDAAvailable() bool {
// Implementation would check for nvidia-smi or CUDA libraries
// This is a simplified version
return false // Placeholder for actual detection logic
}
// checkROCmAvailable checks if AMD ROCm is available
func checkROCmAvailable() bool {
// Implementation would check for rocm-smi or ROCm libraries
return false // Placeholder for actual detection logic
}
// checkIntelGPUAvailable checks if Intel GPU acceleration is available
func checkIntelGPUAvailable() bool {
// Implementation would check for Intel GPU drivers and libraries
return false // Placeholder for actual detection logic
}
// GenerateCompletion implements the Provider interface
func (p *LocalProvider) GenerateCompletion(ctx context.Context, req llm.CompletionRequest) (*llm.CompletionResponse, error) {
// Convert our request format to the local server's API format
apiRequest := map[string]interface{}{
"model": p.modelName,
"messages": convertMessages(req.Messages),
"max_tokens": req.MaxTokens,
"temperature": req.Temperature,
"top_p": req.TopP,
"stop": req.StopSequences,
"presence_penalty": req.PresencePenalty,
}
// Add any provider-specific metadata
for key, value := range req.Metadata {
apiRequest[key] = value
}
requestBody, err := json.Marshal(apiRequest)
if err != nil {
return nil, fmt.Errorf("failed to marshal request: %w", err)
}
// Execute the request with retry logic
var lastErr error
for attempt := 0; attempt <= p.maxRetries; attempt++ {
if attempt > 0 {
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-time.After(p.retryDelay):
}
}
response, err := p.executeRequest(ctx, requestBody)
if err == nil {
return response, nil
}
lastErr = err
}
return nil, fmt.Errorf("failed after %d attempts: %w", p.maxRetries+1, lastErr)
}
// executeRequest performs a single HTTP request to the local server
func (p *LocalProvider) executeRequest(ctx context.Context, requestBody []byte) (*llm.CompletionResponse, error) {
endpoint := fmt.Sprintf("%s/v1/chat/completions", p.baseURL)
httpReq, err := http.NewRequestWithContext(ctx, "POST", endpoint, bytes.NewReader(requestBody))
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
httpReq.Header.Set("Content-Type", "application/json")
httpResp, err := p.client.Do(httpReq)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
defer httpResp.Body.Close()
if httpResp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(httpResp.Body)
return nil, fmt.Errorf("server returned status %d: %s", httpResp.StatusCode, string(body))
}
var apiResponse struct {
Choices []struct {
Message struct {
Content string `json:"content"`
} `json:"message"`
FinishReason string `json:"finish_reason"`
} `json:"choices"`
Usage struct {
PromptTokens int `json:"prompt_tokens"`
CompletionTokens int `json:"completion_tokens"`
TotalTokens int `json:"total_tokens"`
} `json:"usage"`
Model string `json:"model"`
}
if err := json.NewDecoder(httpResp.Body).Decode(&apiResponse); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
if len(apiResponse.Choices) == 0 {
return nil, fmt.Errorf("no choices in response")
}
return &llm.CompletionResponse{
Content: apiResponse.Choices[0].Message.Content,
FinishReason: apiResponse.Choices[0].FinishReason,
TokensUsed: apiResponse.Usage.TotalTokens,
PromptTokens: apiResponse.Usage.PromptTokens,
ModelUsed: apiResponse.Model,
Metadata: map[string]string{
"gpu_backend": string(p.gpuBackend),
},
}, nil
}
// convertMessages transforms our message format to the API format
func convertMessages(messages []llm.Message) []map[string]string {
result := make([]map[string]string, len(messages))
for i, msg := range messages {
result[i] = map[string]string{
"role": msg.Role,
"content": msg.Content,
}
}
return result
}
// GetCapabilities returns the capabilities of this provider
func (p *LocalProvider) GetCapabilities() llm.ProviderCapabilities {
return llm.ProviderCapabilities{
SupportsStreaming: false,
MaxContextTokens: 8192, // This would be model-specific
SupportedModalities: []string{"text"},
RequiresAPIKey: false,
GPUAccelerated: p.gpuBackend != GPUBackendCPU,
}
}
// HealthCheck verifies the local server is accessible
func (p *LocalProvider) HealthCheck(ctx context.Context) error {
endpoint := fmt.Sprintf("%s/health", p.baseURL)
req, err := http.NewRequestWithContext(ctx, "GET", endpoint, nil)
if err != nil {
return fmt.Errorf("failed to create health check request: %w", err)
}
resp, err := p.client.Do(req)
if err != nil {
return fmt.Errorf("health check failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("health check returned status %d", resp.StatusCode)
}
return nil
}
// Close releases resources held by the provider
func (p *LocalProvider) Close() error {
p.client.CloseIdleConnections()
return nil
}
The LocalProvider implementation demonstrates several important patterns. The GPU backend detection logic allows the system to automatically configure itself based on available hardware. The retry mechanism with exponential backoff ensures resilience against transient failures. The health check endpoint enables the system to verify connectivity before attempting expensive operations.
REMOTE LLM PROVIDER IMPLEMENTATION
Remote LLM providers offer access to powerful models without requiring local infrastructure. The implementation must handle API authentication, rate limiting, and provider-specific response formats.
// Package remotellm implements providers for cloud-based LLM services
package remotellm
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
"agent/llm"
)
// RemoteProvider implements the Provider interface for cloud-based LLMs
type RemoteProvider struct {
apiKey string
baseURL string
modelName string
client *http.Client
rateLimiter *RateLimiter
maxRetries int
retryDelay time.Duration
}
// RemoteProviderConfig contains configuration for remote providers
type RemoteProviderConfig struct {
APIKey string
BaseURL string
ModelName string
Timeout time.Duration
MaxRetries int
RetryDelay time.Duration
RequestsPerMin int // Rate limiting
}
// RateLimiter implements token bucket rate limiting
type RateLimiter struct {
tokens int
maxTokens int
refillRate time.Duration
lastRefill time.Time
}
// NewRateLimiter creates a new rate limiter
func NewRateLimiter(requestsPerMin int) *RateLimiter {
return &RateLimiter{
tokens: requestsPerMin,
maxTokens: requestsPerMin,
refillRate: time.Minute / time.Duration(requestsPerMin),
lastRefill: time.Now(),
}
}
// Wait blocks until a token is available
func (rl *RateLimiter) Wait(ctx context.Context) error {
for {
now := time.Now()
elapsed := now.Sub(rl.lastRefill)
tokensToAdd := int(elapsed / rl.refillRate)
if tokensToAdd > 0 {
rl.tokens += tokensToAdd
if rl.tokens > rl.maxTokens {
rl.tokens = rl.maxTokens
}
rl.lastRefill = now
}
if rl.tokens > 0 {
rl.tokens--
return nil
}
waitTime := rl.refillRate - (elapsed % rl.refillRate)
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(waitTime):
continue
}
}
}
// NewRemoteProvider creates a new remote LLM provider
func NewRemoteProvider(config RemoteProviderConfig) (*RemoteProvider, error) {
if config.APIKey == "" {
return nil, fmt.Errorf("API key is required")
}
if config.BaseURL == "" {
return nil, fmt.Errorf("base URL is required")
}
if config.ModelName == "" {
return nil, fmt.Errorf("model name is required")
}
if config.Timeout == 0 {
config.Timeout = 180 * time.Second
}
if config.MaxRetries == 0 {
config.MaxRetries = 3
}
if config.RetryDelay == 0 {
config.RetryDelay = 5 * time.Second
}
if config.RequestsPerMin == 0 {
config.RequestsPerMin = 60
}
return &RemoteProvider{
apiKey: config.APIKey,
baseURL: config.BaseURL,
modelName: config.ModelName,
maxRetries: config.MaxRetries,
retryDelay: config.RetryDelay,
rateLimiter: NewRateLimiter(config.RequestsPerMin),
client: &http.Client{
Timeout: config.Timeout,
},
}, nil
}
// GenerateCompletion implements the Provider interface
func (p *RemoteProvider) GenerateCompletion(ctx context.Context, req llm.CompletionRequest) (*llm.CompletionResponse, error) {
// Wait for rate limiter
if err := p.rateLimiter.Wait(ctx); err != nil {
return nil, fmt.Errorf("rate limiter error: %w", err)
}
// Build the API request
apiRequest := map[string]interface{}{
"model": p.modelName,
"messages": convertMessagesToAPI(req.Messages),
}
if req.MaxTokens > 0 {
apiRequest["max_tokens"] = req.MaxTokens
}
if req.Temperature > 0 {
apiRequest["temperature"] = req.Temperature
}
if req.TopP > 0 {
apiRequest["top_p"] = req.TopP
}
if len(req.StopSequences) > 0 {
apiRequest["stop"] = req.StopSequences
}
if req.PresencePenalty != 0 {
apiRequest["presence_penalty"] = req.PresencePenalty
}
requestBody, err := json.Marshal(apiRequest)
if err != nil {
return nil, fmt.Errorf("failed to marshal request: %w", err)
}
// Execute with retry logic
var lastErr error
for attempt := 0; attempt <= p.maxRetries; attempt++ {
if attempt > 0 {
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-time.After(p.retryDelay * time.Duration(attempt)):
}
}
response, err := p.executeRemoteRequest(ctx, requestBody)
if err == nil {
return response, nil
}
lastErr = err
}
return nil, fmt.Errorf("failed after %d attempts: %w", p.maxRetries+1, lastErr)
}
// executeRemoteRequest performs a single API request
func (p *RemoteProvider) executeRemoteRequest(ctx context.Context, requestBody []byte) (*llm.CompletionResponse, error) {
endpoint := fmt.Sprintf("%s/v1/chat/completions", p.baseURL)
httpReq, err := http.NewRequestWithContext(ctx, "POST", endpoint, bytes.NewReader(requestBody))
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("Authorization", fmt.Sprintf("Bearer %s", p.apiKey))
httpResp, err := p.client.Do(httpReq)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
defer httpResp.Body.Close()
body, err := io.ReadAll(httpResp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response: %w", err)
}
if httpResp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("API returned status %d: %s", httpResp.StatusCode, string(body))
}
var apiResponse struct {
Choices []struct {
Message struct {
Content string `json:"content"`
} `json:"message"`
FinishReason string `json:"finish_reason"`
} `json:"choices"`
Usage struct {
PromptTokens int `json:"prompt_tokens"`
CompletionTokens int `json:"completion_tokens"`
TotalTokens int `json:"total_tokens"`
} `json:"usage"`
Model string `json:"model"`
}
if err := json.Unmarshal(body, &apiResponse); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
if len(apiResponse.Choices) == 0 {
return nil, fmt.Errorf("no choices in response")
}
return &llm.CompletionResponse{
Content: apiResponse.Choices[0].Message.Content,
FinishReason: apiResponse.Choices[0].FinishReason,
TokensUsed: apiResponse.Usage.TotalTokens,
PromptTokens: apiResponse.Usage.PromptTokens,
ModelUsed: apiResponse.Model,
}, nil
}
// convertMessagesToAPI transforms messages to API format
func convertMessagesToAPI(messages []llm.Message) []map[string]string {
result := make([]map[string]string, len(messages))
for i, msg := range messages {
result[i] = map[string]string{
"role": msg.Role,
"content": msg.Content,
}
}
return result
}
// GetCapabilities returns provider capabilities
func (p *RemoteProvider) GetCapabilities() llm.ProviderCapabilities {
return llm.ProviderCapabilities{
SupportsStreaming: true,
MaxContextTokens: 128000, // Model-specific
SupportedModalities: []string{"text"},
RequiresAPIKey: true,
GPUAccelerated: true, // Cloud providers use GPUs
}
}
// HealthCheck verifies API connectivity
func (p *RemoteProvider) HealthCheck(ctx context.Context) error {
// Simple request to verify credentials and connectivity
testReq := llm.CompletionRequest{
Messages: []llm.Message{
{Role: "user", Content: "Hello"},
},
MaxTokens: 5,
}
_, err := p.GenerateCompletion(ctx, testReq)
return err
}
// Close releases resources
func (p *RemoteProvider) Close() error {
p.client.CloseIdleConnections()
return nil
}
The RemoteProvider implementation includes sophisticated rate limiting using a token bucket algorithm. This prevents the application from exceeding API quotas and incurring additional costs or service disruptions. The exponential backoff retry strategy handles transient network issues gracefully.
DOMAIN MODEL FOR DOCKER AND KUBERNETES ARTIFACTS
The domain model represents the various artifacts that the agent generates. These structures capture the essential properties of Dockerfiles, Kubernetes manifests, and Helm charts while providing methods for validation and serialization.
// Package domain defines the core domain entities for Docker and Kubernetes artifacts
package domain
import (
"fmt"
"strings"
)
// Dockerfile represents a complete Dockerfile with all instructions
type Dockerfile struct {
BaseImage string // FROM instruction
Maintainer string // LABEL maintainer
WorkDir string // WORKDIR instruction
Environment map[string]string // ENV instructions
CopyInstructions []CopyInstruction // COPY instructions
RunCommands []string // RUN instructions
ExposePort []int // EXPOSE instructions
EntryPoint []string // ENTRYPOINT instruction
Command []string // CMD instruction
Volumes []string // VOLUME instructions
User string // USER instruction
Labels map[string]string // Additional LABEL instructions
BuildArgs map[string]string // ARG instructions
HealthCheck *HealthCheck // HEALTHCHECK instruction
}
// CopyInstruction represents a COPY or ADD instruction
type CopyInstruction struct {
Source string // Source path
Destination string // Destination path
Chown string // Optional ownership specification
}
// HealthCheck represents a HEALTHCHECK instruction
type HealthCheck struct {
Command []string // The command to run
Interval string // How often to check
Timeout string // How long to wait
StartPeriod string // Grace period
Retries int // Number of retries
}
// Validate checks if the Dockerfile is valid
func (d *Dockerfile) Validate() error {
if d.BaseImage == "" {
return fmt.Errorf("base image is required")
}
if len(d.EntryPoint) == 0 && len(d.Command) == 0 {
return fmt.Errorf("either ENTRYPOINT or CMD must be specified")
}
return nil
}
// ToDockerfileContent generates the actual Dockerfile content
func (d *Dockerfile) ToDockerfileContent() string {
var builder strings.Builder
// FROM instruction
builder.WriteString(fmt.Sprintf("FROM %s\n\n", d.BaseImage))
// Maintainer label
if d.Maintainer != "" {
builder.WriteString(fmt.Sprintf("LABEL maintainer=\"%s\"\n\n", d.Maintainer))
}
// Additional labels
if len(d.Labels) > 0 {
for key, value := range d.Labels {
builder.WriteString(fmt.Sprintf("LABEL %s=\"%s\"\n", key, value))
}
builder.WriteString("\n")
}
// Build arguments
if len(d.BuildArgs) > 0 {
for key, value := range d.BuildArgs {
builder.WriteString(fmt.Sprintf("ARG %s=%s\n", key, value))
}
builder.WriteString("\n")
}
// Environment variables
if len(d.Environment) > 0 {
for key, value := range d.Environment {
builder.WriteString(fmt.Sprintf("ENV %s=%s\n", key, value))
}
builder.WriteString("\n")
}
// Working directory
if d.WorkDir != "" {
builder.WriteString(fmt.Sprintf("WORKDIR %s\n\n", d.WorkDir))
}
// COPY instructions
for _, copy := range d.CopyInstructions {
if copy.Chown != "" {
builder.WriteString(fmt.Sprintf("COPY --chown=%s %s %s\n", copy.Chown, copy.Source, copy.Destination))
} else {
builder.WriteString(fmt.Sprintf("COPY %s %s\n", copy.Source, copy.Destination))
}
}
if len(d.CopyInstructions) > 0 {
builder.WriteString("\n")
}
// RUN commands
for _, cmd := range d.RunCommands {
builder.WriteString(fmt.Sprintf("RUN %s\n", cmd))
}
if len(d.RunCommands) > 0 {
builder.WriteString("\n")
}
// Volumes
for _, vol := range d.Volumes {
builder.WriteString(fmt.Sprintf("VOLUME %s\n", vol))
}
if len(d.Volumes) > 0 {
builder.WriteString("\n")
}
// Expose ports
for _, port := range d.ExposePort {
builder.WriteString(fmt.Sprintf("EXPOSE %d\n", port))
}
if len(d.ExposePort) > 0 {
builder.WriteString("\n")
}
// User
if d.User != "" {
builder.WriteString(fmt.Sprintf("USER %s\n\n", d.User))
}
// Health check
if d.HealthCheck != nil {
builder.WriteString("HEALTHCHECK ")
if d.HealthCheck.Interval != "" {
builder.WriteString(fmt.Sprintf("--interval=%s ", d.HealthCheck.Interval))
}
if d.HealthCheck.Timeout != "" {
builder.WriteString(fmt.Sprintf("--timeout=%s ", d.HealthCheck.Timeout))
}
if d.HealthCheck.StartPeriod != "" {
builder.WriteString(fmt.Sprintf("--start-period=%s ", d.HealthCheck.StartPeriod))
}
if d.HealthCheck.Retries > 0 {
builder.WriteString(fmt.Sprintf("--retries=%d ", d.HealthCheck.Retries))
}
builder.WriteString(fmt.Sprintf("CMD %s\n\n", strings.Join(d.HealthCheck.Command, " ")))
}
// Entrypoint
if len(d.EntryPoint) > 0 {
builder.WriteString(fmt.Sprintf("ENTRYPOINT [\"%s\"]\n", strings.Join(d.EntryPoint, "\", \"")))
}
// Command
if len(d.Command) > 0 {
builder.WriteString(fmt.Sprintf("CMD [\"%s\"]\n", strings.Join(d.Command, "\", \"")))
}
return builder.String()
}
// KubernetesDeployment represents a Kubernetes Deployment manifest
type KubernetesDeployment struct {
APIVersion string // API version
Kind string // Resource kind
Metadata ObjectMeta // Metadata
Spec DeploymentSpec // Deployment specification
}
// ObjectMeta represents Kubernetes object metadata
type ObjectMeta struct {
Name string // Object name
Namespace string // Namespace
Labels map[string]string // Labels
Annotations map[string]string // Annotations
}
// DeploymentSpec represents the deployment specification
type DeploymentSpec struct {
Replicas int32 // Number of replicas
Selector *LabelSelector // Label selector
Template PodTemplateSpec // Pod template
Strategy DeploymentStrategy // Update strategy
}
// LabelSelector represents a label selector
type LabelSelector struct {
MatchLabels map[string]string // Labels to match
}
// PodTemplateSpec represents a pod template
type PodTemplateSpec struct {
Metadata ObjectMeta // Pod metadata
Spec PodSpec // Pod specification
}
// PodSpec represents a pod specification
type PodSpec struct {
Containers []Container // Containers
InitContainers []Container // Init containers
Volumes []Volume // Volumes
ServiceAccount string // Service account name
SecurityContext *PodSecurityContext // Security context
RestartPolicy string // Restart policy
}
// Container represents a container specification
type Container struct {
Name string // Container name
Image string // Container image
ImagePullPolicy string // Image pull policy
Command []string // Entrypoint
Args []string // Arguments
Ports []ContainerPort // Exposed ports
Env []EnvVar // Environment variables
VolumeMounts []VolumeMount // Volume mounts
Resources ResourceRequirements // Resource requirements
LivenessProbe *Probe // Liveness probe
ReadinessProbe *Probe // Readiness probe
SecurityContext *SecurityContext // Security context
}
// ContainerPort represents a container port
type ContainerPort struct {
Name string // Port name
ContainerPort int32 // Port number
Protocol string // Protocol (TCP/UDP)
}
// EnvVar represents an environment variable
type EnvVar struct {
Name string // Variable name
Value string // Variable value
ValueFrom *EnvVarSource // Source for the value
}
// EnvVarSource represents a source for environment variable value
type EnvVarSource struct {
ConfigMapKeyRef *ConfigMapKeySelector // ConfigMap reference
SecretKeyRef *SecretKeySelector // Secret reference
FieldRef *FieldSelector // Field reference
}
// ConfigMapKeySelector selects a key from a ConfigMap
type ConfigMapKeySelector struct {
Name string // ConfigMap name
Key string // Key name
}
// SecretKeySelector selects a key from a Secret
type SecretKeySelector struct {
Name string // Secret name
Key string // Key name
}
// FieldSelector selects a field from the pod
type FieldSelector struct {
FieldPath string // Field path
}
// VolumeMount represents a volume mount
type VolumeMount struct {
Name string // Volume name
MountPath string // Mount path
ReadOnly bool // Read-only flag
SubPath string // Sub path
}
// ResourceRequirements represents resource requirements
type ResourceRequirements struct {
Limits map[string]string // Resource limits
Requests map[string]string // Resource requests
}
// Probe represents a health probe
type Probe struct {
HTTPGet *HTTPGetAction // HTTP GET probe
TCPSocket *TCPSocketAction // TCP socket probe
Exec *ExecAction // Exec probe
InitialDelaySeconds int32 // Initial delay
PeriodSeconds int32 // Period
TimeoutSeconds int32 // Timeout
SuccessThreshold int32 // Success threshold
FailureThreshold int32 // Failure threshold
}
// HTTPGetAction represents an HTTP GET probe
type HTTPGetAction struct {
Path string // URL path
Port int32 // Port number
Scheme string // Scheme (HTTP/HTTPS)
Headers map[string]string // HTTP headers
}
// TCPSocketAction represents a TCP socket probe
type TCPSocketAction struct {
Port int32 // Port number
}
// ExecAction represents an exec probe
type ExecAction struct {
Command []string // Command to execute
}
// SecurityContext represents container security context
type SecurityContext struct {
RunAsUser *int64 // User ID
RunAsGroup *int64 // Group ID
RunAsNonRoot *bool // Run as non-root flag
ReadOnlyRootFilesystem *bool // Read-only root filesystem
Capabilities *Capabilities // Linux capabilities
}
// Capabilities represents Linux capabilities
type Capabilities struct {
Add []string // Capabilities to add
Drop []string // Capabilities to drop
}
// PodSecurityContext represents pod-level security context
type PodSecurityContext struct {
RunAsUser *int64 // User ID
RunAsGroup *int64 // Group ID
FSGroup *int64 // Filesystem group
RunAsNonRoot *bool // Run as non-root flag
}
// Volume represents a volume
type Volume struct {
Name string // Volume name
ConfigMap *ConfigMapVolume // ConfigMap volume
Secret *SecretVolume // Secret volume
EmptyDir *EmptyDirVolume // EmptyDir volume
PersistentVolumeClaim *PVCVolume // PVC volume
}
// ConfigMapVolume represents a ConfigMap volume
type ConfigMapVolume struct {
Name string // ConfigMap name
}
// SecretVolume represents a Secret volume
type SecretVolume struct {
SecretName string // Secret name
}
// EmptyDirVolume represents an EmptyDir volume
type EmptyDirVolume struct {
Medium string // Storage medium
}
// PVCVolume represents a PersistentVolumeClaim volume
type PVCVolume struct {
ClaimName string // PVC name
}
// DeploymentStrategy represents deployment strategy
type DeploymentStrategy struct {
Type string // Strategy type
RollingUpdate *RollingUpdateStrategy // Rolling update parameters
}
// RollingUpdateStrategy represents rolling update parameters
type RollingUpdateStrategy struct {
MaxUnavailable string // Max unavailable
MaxSurge string // Max surge
}
This comprehensive domain model captures the complexity of Kubernetes resources while maintaining type safety and validation capabilities. The structures mirror the actual Kubernetes API objects, ensuring that generated manifests conform to the expected schema.
ARTIFACT GENERATOR SERVICE
The artifact generator service orchestrates the interaction between the LLM provider and the domain model. It translates user requests into structured prompts, parses LLM responses into domain objects, and validates the generated artifacts.
// Package generator provides services for generating Docker and Kubernetes artifacts
package generator
import (
"context"
"encoding/json"
"fmt"
"strings"
"agent/domain"
"agent/llm"
)
// ArtifactGenerator generates Docker and Kubernetes artifacts using an LLM
type ArtifactGenerator struct {
provider llm.Provider
systemPrompt string
conversationCtx *ConversationContext
}
// ConversationContext maintains the conversation history
type ConversationContext struct {
Messages []llm.Message
GeneratedArtifacts map[string]interface{} // Artifacts generated in this session
}
// NewArtifactGenerator creates a new artifact generator
func NewArtifactGenerator(provider llm.Provider) *ArtifactGenerator {
systemPrompt := `You are an expert in Docker and Kubernetes. Your task is to generate production-ready Docker and Kubernetes artifacts based on user requirements.
When generating Dockerfiles:
- Use multi-stage builds when appropriate
- Follow security best practices (non-root user, minimal base images)
- Optimize for layer caching
- Include health checks
- Use specific version tags, not 'latest'
When generating Kubernetes manifests:
- Include resource limits and requests
- Configure appropriate health probes
- Use security contexts
- Follow the principle of least privilege
- Include labels for organization
When generating Helm charts:
- Create well-structured templates
- Use values.yaml for configuration
- Include helpful comments
- Follow Helm best practices
Always respond with valid JSON containing the artifact structure. Use the following format:
{
"artifact_type": "dockerfile|deployment|service|configmap|secret|helmchart",
"content": { ... artifact structure ... }
}
Be precise, follow best practices, and ensure all generated artifacts are production-ready.`
return &ArtifactGenerator{
provider: provider,
systemPrompt: systemPrompt,
conversationCtx: &ConversationContext{
Messages: []llm.Message{
{
Role: "system",
Content: systemPrompt,
},
},
GeneratedArtifacts: make(map[string]interface{}),
},
}
}
// GenerateDockerfile generates a Dockerfile based on user requirements
func (g *ArtifactGenerator) GenerateDockerfile(ctx context.Context, requirements string) (*domain.Dockerfile, error) {
// Add user message to conversation
userMessage := llm.Message{
Role: "user",
Content: fmt.Sprintf("Generate a Dockerfile with the following requirements: %s", requirements),
}
g.conversationCtx.Messages = append(g.conversationCtx.Messages, userMessage)
// Request completion from LLM
request := llm.CompletionRequest{
Messages: g.conversationCtx.Messages,
MaxTokens: 2000,
Temperature: 0.3, // Lower temperature for more deterministic output
TopP: 0.9,
}
response, err := g.provider.GenerateCompletion(ctx, request)
if err != nil {
return nil, fmt.Errorf("failed to generate completion: %w", err)
}
// Add assistant response to conversation
assistantMessage := llm.Message{
Role: "assistant",
Content: response.Content,
}
g.conversationCtx.Messages = append(g.conversationCtx.Messages, assistantMessage)
// Parse the response
dockerfile, err := g.parseDockerfileResponse(response.Content)
if err != nil {
return nil, fmt.Errorf("failed to parse Dockerfile response: %w", err)
}
// Validate the generated Dockerfile
if err := dockerfile.Validate(); err != nil {
return nil, fmt.Errorf("generated Dockerfile is invalid: %w", err)
}
// Store in conversation context
g.conversationCtx.GeneratedArtifacts["dockerfile"] = dockerfile
return dockerfile, nil
}
// parseDockerfileResponse parses the LLM response into a Dockerfile structure
func (g *ArtifactGenerator) parseDockerfileResponse(content string) (*domain.Dockerfile, error) {
// Extract JSON from the response
jsonContent := extractJSON(content)
if jsonContent == "" {
return nil, fmt.Errorf("no JSON found in response")
}
var response struct {
ArtifactType string `json:"artifact_type"`
Content struct {
BaseImage string `json:"base_image"`
Maintainer string `json:"maintainer"`
WorkDir string `json:"workdir"`
Environment map[string]string `json:"environment"`
CopyInstructions []struct {
Source string `json:"source"`
Destination string `json:"destination"`
Chown string `json:"chown"`
} `json:"copy_instructions"`
RunCommands []string `json:"run_commands"`
ExposePort []int `json:"expose_ports"`
EntryPoint []string `json:"entrypoint"`
Command []string `json:"command"`
Volumes []string `json:"volumes"`
User string `json:"user"`
Labels map[string]string `json:"labels"`
BuildArgs map[string]string `json:"build_args"`
HealthCheck *struct {
Command []string `json:"command"`
Interval string `json:"interval"`
Timeout string `json:"timeout"`
StartPeriod string `json:"start_period"`
Retries int `json:"retries"`
} `json:"healthcheck"`
} `json:"content"`
}
if err := json.Unmarshal([]byte(jsonContent), &response); err != nil {
return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
}
if response.ArtifactType != "dockerfile" {
return nil, fmt.Errorf("expected dockerfile artifact, got %s", response.ArtifactType)
}
// Convert to domain model
dockerfile := &domain.Dockerfile{
BaseImage: response.Content.BaseImage,
Maintainer: response.Content.Maintainer,
WorkDir: response.Content.WorkDir,
Environment: response.Content.Environment,
RunCommands: response.Content.RunCommands,
ExposePort: response.Content.ExposePort,
EntryPoint: response.Content.EntryPoint,
Command: response.Content.Command,
Volumes: response.Content.Volumes,
User: response.Content.User,
Labels: response.Content.Labels,
BuildArgs: response.Content.BuildArgs,
}
// Convert copy instructions
for _, ci := range response.Content.CopyInstructions {
dockerfile.CopyInstructions = append(dockerfile.CopyInstructions, domain.CopyInstruction{
Source: ci.Source,
Destination: ci.Destination,
Chown: ci.Chown,
})
}
// Convert health check
if response.Content.HealthCheck != nil {
dockerfile.HealthCheck = &domain.HealthCheck{
Command: response.Content.HealthCheck.Command,
Interval: response.Content.HealthCheck.Interval,
Timeout: response.Content.HealthCheck.Timeout,
StartPeriod: response.Content.HealthCheck.StartPeriod,
Retries: response.Content.HealthCheck.Retries,
}
}
return dockerfile, nil
}
// extractJSON extracts JSON content from a string that may contain other text
func extractJSON(content string) string {
// Find the first '{' and last '}'
start := strings.Index(content, "{")
end := strings.LastIndex(content, "}")
if start == -1 || end == -1 || start >= end {
return ""
}
return content[start : end+1]
}
// GenerateDeployment generates a Kubernetes Deployment manifest
func (g *ArtifactGenerator) GenerateDeployment(ctx context.Context, requirements string) (*domain.KubernetesDeployment, error) {
userMessage := llm.Message{
Role: "user",
Content: fmt.Sprintf("Generate a Kubernetes Deployment manifest with the following requirements: %s", requirements),
}
g.conversationCtx.Messages = append(g.conversationCtx.Messages, userMessage)
request := llm.CompletionRequest{
Messages: g.conversationCtx.Messages,
MaxTokens: 3000,
Temperature: 0.3,
TopP: 0.9,
}
response, err := g.provider.GenerateCompletion(ctx, request)
if err != nil {
return nil, fmt.Errorf("failed to generate completion: %w", err)
}
assistantMessage := llm.Message{
Role: "assistant",
Content: response.Content,
}
g.conversationCtx.Messages = append(g.conversationCtx.Messages, assistantMessage)
deployment, err := g.parseDeploymentResponse(response.Content)
if err != nil {
return nil, fmt.Errorf("failed to parse Deployment response: %w", err)
}
g.conversationCtx.GeneratedArtifacts["deployment"] = deployment
return deployment, nil
}
// parseDeploymentResponse parses the LLM response into a Deployment structure
func (g *ArtifactGenerator) parseDeploymentResponse(content string) (*domain.KubernetesDeployment, error) {
jsonContent := extractJSON(content)
if jsonContent == "" {
return nil, fmt.Errorf("no JSON found in response")
}
var response struct {
ArtifactType string `json:"artifact_type"`
Content domain.KubernetesDeployment `json:"content"`
}
if err := json.Unmarshal([]byte(jsonContent), &response); err != nil {
return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
}
if response.ArtifactType != "deployment" {
return nil, fmt.Errorf("expected deployment artifact, got %s", response.ArtifactType)
}
return &response.Content, nil
}
// RefineArtifact refines a previously generated artifact based on feedback
func (g *ArtifactGenerator) RefineArtifact(ctx context.Context, artifactType, feedback string) (interface{}, error) {
userMessage := llm.Message{
Role: "user",
Content: fmt.Sprintf("Please refine the %s based on this feedback: %s", artifactType, feedback),
}
g.conversationCtx.Messages = append(g.conversationCtx.Messages, userMessage)
request := llm.CompletionRequest{
Messages: g.conversationCtx.Messages,
MaxTokens: 3000,
Temperature: 0.3,
TopP: 0.9,
}
response, err := g.provider.GenerateCompletion(ctx, request)
if err != nil {
return nil, fmt.Errorf("failed to generate completion: %w", err)
}
assistantMessage := llm.Message{
Role: "assistant",
Content: response.Content,
}
g.conversationCtx.Messages = append(g.conversationCtx.Messages, assistantMessage)
// Parse based on artifact type
switch artifactType {
case "dockerfile":
return g.parseDockerfileResponse(response.Content)
case "deployment":
return g.parseDeploymentResponse(response.Content)
default:
return nil, fmt.Errorf("unsupported artifact type: %s", artifactType)
}
}
The ArtifactGenerator service demonstrates the power of maintaining conversational context. Each interaction builds upon previous exchanges, allowing the LLM to understand references and make coherent refinements. The JSON-based response format ensures structured output that can be reliably parsed into domain objects.
HELM CHART GENERATION
Helm charts package Kubernetes applications into reusable, configurable units. The agent must generate not only the template files but also the values file, Chart metadata, and helper templates.
// Package helm provides functionality for generating Helm charts
package helm
import (
"context"
"encoding/json"
"fmt"
"strings"
"agent/llm"
)
// HelmChart represents a complete Helm chart structure
type HelmChart struct {
ChartMetadata ChartMetadata // Chart.yaml content
Values map[string]interface{} // values.yaml content
Templates map[string]string // Template files
Helpers string // _helpers.tpl content
}
// ChartMetadata represents Chart.yaml
type ChartMetadata struct {
APIVersion string `yaml:"apiVersion"`
Name string `yaml:"name"`
Version string `yaml:"version"`
AppVersion string `yaml:"appVersion"`
Description string `yaml:"description"`
Keywords []string `yaml:"keywords"`
Maintainers []Maintainer `yaml:"maintainers"`
Dependencies []Dependency `yaml:"dependencies"`
Annotations map[string]string `yaml:"annotations"`
}
// Maintainer represents a chart maintainer
type Maintainer struct {
Name string `yaml:"name"`
Email string `yaml:"email"`
}
// Dependency represents a chart dependency
type Dependency struct {
Name string `yaml:"name"`
Version string `yaml:"version"`
Repository string `yaml:"repository"`
Condition string `yaml:"condition,omitempty"`
}
// HelmGenerator generates Helm charts using an LLM
type HelmGenerator struct {
provider llm.Provider
systemPrompt string
}
// NewHelmGenerator creates a new Helm chart generator
func NewHelmGenerator(provider llm.Provider) *HelmGenerator {
systemPrompt := `You are an expert in Helm chart development. Generate production-ready Helm charts following these principles:
1. Use semantic versioning for chart and app versions
2. Provide comprehensive values.yaml with sensible defaults
3. Include helper templates for common patterns
4. Use proper templating with conditionals and loops
5. Include NOTES.txt for post-installation instructions
6. Follow Helm best practices for naming and organization
7. Add comments explaining configuration options
8. Support common deployment scenarios through values
Always respond with valid JSON containing the complete chart structure:
{
"chart_metadata": { ... Chart.yaml content ... },
"values": { ... values.yaml content ... },
"templates": {
"deployment.yaml": "...",
"service.yaml": "...",
...
},
"helpers": "... _helpers.tpl content ..."
}`
return &HelmGenerator{
provider: provider,
systemPrompt: systemPrompt,
}
}
// GenerateChart generates a complete Helm chart based on requirements
func (g *HelmGenerator) GenerateChart(ctx context.Context, requirements string) (*HelmChart, error) {
messages := []llm.Message{
{
Role: "system",
Content: g.systemPrompt,
},
{
Role: "user",
Content: fmt.Sprintf("Generate a Helm chart with the following requirements: %s", requirements),
},
}
request := llm.CompletionRequest{
Messages: messages,
MaxTokens: 4000,
Temperature: 0.3,
TopP: 0.9,
}
response, err := g.provider.GenerateCompletion(ctx, request)
if err != nil {
return nil, fmt.Errorf("failed to generate completion: %w", err)
}
chart, err := g.parseChartResponse(response.Content)
if err != nil {
return nil, fmt.Errorf("failed to parse chart response: %w", err)
}
return chart, nil
}
// parseChartResponse parses the LLM response into a HelmChart structure
func (g *HelmGenerator) parseChartResponse(content string) (*HelmChart, error) {
jsonContent := extractJSON(content)
if jsonContent == "" {
return nil, fmt.Errorf("no JSON found in response")
}
var response struct {
ChartMetadata ChartMetadata `json:"chart_metadata"`
Values map[string]interface{} `json:"values"`
Templates map[string]string `json:"templates"`
Helpers string `json:"helpers"`
}
if err := json.Unmarshal([]byte(jsonContent), &response); err != nil {
return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
}
return &HelmChart{
ChartMetadata: response.ChartMetadata,
Values: response.Values,
Templates: response.Templates,
Helpers: response.Helpers,
}, nil
}
// extractJSON extracts JSON from response content
func extractJSON(content string) string {
start := strings.Index(content, "{")
end := strings.LastIndex(content, "}")
if start == -1 || end == -1 || start >= end {
return ""
}
return content[start : end+1]
}
// ValidateChart performs basic validation on a Helm chart
func (g *HelmGenerator) ValidateChart(chart *HelmChart) error {
if chart.ChartMetadata.Name == "" {
return fmt.Errorf("chart name is required")
}
if chart.ChartMetadata.Version == "" {
return fmt.Errorf("chart version is required")
}
if len(chart.Templates) == 0 {
return fmt.Errorf("chart must contain at least one template")
}
// Check for required templates
requiredTemplates := []string{"deployment.yaml", "service.yaml"}
for _, tmpl := range requiredTemplates {
if _, exists := chart.Templates[tmpl]; !exists {
return fmt.Errorf("missing required template: %s", tmpl)
}
}
return nil
}
The Helm generator creates complete, deployable charts with all necessary components. The structured approach ensures that generated charts follow Helm conventions and can be immediately used in production environments.
FILE SYSTEM OPERATIONS AND ARTIFACT PERSISTENCE
Generated artifacts must be persisted to the file system in the correct structure. This includes creating directory hierarchies for Helm charts and ensuring proper file permissions.
// Package filesystem provides utilities for persisting artifacts to disk
package filesystem
import (
"fmt"
"os"
"path/filepath"
"agent/domain"
"agent/helm"
"gopkg.in/yaml.v3"
)
// ArtifactWriter handles writing artifacts to the filesystem
type ArtifactWriter struct {
baseDir string
}
// NewArtifactWriter creates a new artifact writer
func NewArtifactWriter(baseDir string) (*ArtifactWriter, error) {
// Ensure base directory exists
if err := os.MkdirAll(baseDir, 0755); err != nil {
return nil, fmt.Errorf("failed to create base directory: %w", err)
}
return &ArtifactWriter{
baseDir: baseDir,
}, nil
}
// WriteDockerfile writes a Dockerfile to the filesystem
func (w *ArtifactWriter) WriteDockerfile(dockerfile *domain.Dockerfile, projectName string) error {
projectDir := filepath.Join(w.baseDir, projectName)
if err := os.MkdirAll(projectDir, 0755); err != nil {
return fmt.Errorf("failed to create project directory: %w", err)
}
dockerfilePath := filepath.Join(projectDir, "Dockerfile")
content := dockerfile.ToDockerfileContent()
if err := os.WriteFile(dockerfilePath, []byte(content), 0644); err != nil {
return fmt.Errorf("failed to write Dockerfile: %w", err)
}
return nil
}
// WriteKubernetesManifest writes a Kubernetes manifest to the filesystem
func (w *ArtifactWriter) WriteKubernetesManifest(manifest interface{}, projectName, filename string) error {
manifestDir := filepath.Join(w.baseDir, projectName, "k8s")
if err := os.MkdirAll(manifestDir, 0755); err != nil {
return fmt.Errorf("failed to create manifest directory: %w", err)
}
manifestPath := filepath.Join(manifestDir, filename)
yamlContent, err := yaml.Marshal(manifest)
if err != nil {
return fmt.Errorf("failed to marshal manifest to YAML: %w", err)
}
if err := os.WriteFile(manifestPath, yamlContent, 0644); err != nil {
return fmt.Errorf("failed to write manifest: %w", err)
}
return nil
}
// WriteHelmChart writes a complete Helm chart to the filesystem
func (w *ArtifactWriter) WriteHelmChart(chart *helm.HelmChart, chartName string) error {
chartDir := filepath.Join(w.baseDir, chartName)
// Create chart directory structure
dirs := []string{
chartDir,
filepath.Join(chartDir, "templates"),
filepath.Join(chartDir, "charts"),
}
for _, dir := range dirs {
if err := os.MkdirAll(dir, 0755); err != nil {
return fmt.Errorf("failed to create directory %s: %w", dir, err)
}
}
// Write Chart.yaml
chartYAMLPath := filepath.Join(chartDir, "Chart.yaml")
chartYAML, err := yaml.Marshal(chart.ChartMetadata)
if err != nil {
return fmt.Errorf("failed to marshal Chart.yaml: %w", err)
}
if err := os.WriteFile(chartYAMLPath, chartYAML, 0644); err != nil {
return fmt.Errorf("failed to write Chart.yaml: %w", err)
}
// Write values.yaml
valuesYAMLPath := filepath.Join(chartDir, "values.yaml")
valuesYAML, err := yaml.Marshal(chart.Values)
if err != nil {
return fmt.Errorf("failed to marshal values.yaml: %w", err)
}
if err := os.WriteFile(valuesYAMLPath, valuesYAML, 0644); err != nil {
return fmt.Errorf("failed to write values.yaml: %w", err)
}
// Write template files
for filename, content := range chart.Templates {
templatePath := filepath.Join(chartDir, "templates", filename)
if err := os.WriteFile(templatePath, []byte(content), 0644); err != nil {
return fmt.Errorf("failed to write template %s: %w", filename, err)
}
}
// Write helpers
if chart.Helpers != "" {
helpersPath := filepath.Join(chartDir, "templates", "_helpers.tpl")
if err := os.WriteFile(helpersPath, []byte(chart.Helpers), 0644); err != nil {
return fmt.Errorf("failed to write _helpers.tpl: %w", err)
}
}
return nil
}
// CreateProjectStructure creates a complete project structure
func (w *ArtifactWriter) CreateProjectStructure(projectName string) error {
dirs := []string{
filepath.Join(w.baseDir, projectName),
filepath.Join(w.baseDir, projectName, "k8s"),
filepath.Join(w.baseDir, projectName, "helm"),
filepath.Join(w.baseDir, projectName, "configs"),
}
for _, dir := range dirs {
if err := os.MkdirAll(dir, 0755); err != nil {
return fmt.Errorf("failed to create directory %s: %w", dir, err)
}
}
return nil
}
The filesystem operations maintain a clean project structure that follows industry conventions. Helm charts are placed in their own directories with the standard layout, while Kubernetes manifests are organized in a separate folder.
COMMAND LINE INTERFACE
The command-line interface provides the primary interaction mechanism for users. It must support various commands for generating different artifact types and managing the conversation context.
// Package cli provides the command-line interface for the agent
package cli
import (
"context"
"fmt"
"os"
"strings"
"agent/filesystem"
"agent/generator"
"agent/helm"
"agent/llm"
"agent/locallm"
"agent/remotellm"
)
// CLI represents the command-line interface
type CLI struct {
generator *generator.ArtifactGenerator
helmGenerator *helm.HelmGenerator
writer *filesystem.ArtifactWriter
provider llm.Provider
}
// Config contains CLI configuration
type Config struct {
ProviderType string // "local" or "remote"
LocalURL string
LocalModel string
RemoteAPIKey string
RemoteURL string
RemoteModel string
OutputDir string
}
// NewCLI creates a new CLI instance
func NewCLI(config Config) (*CLI, error) {
var provider llm.Provider
var err error
// Initialize the appropriate provider
switch config.ProviderType {
case "local":
provider, err = locallm.NewLocalProvider(locallm.LocalProviderConfig{
BaseURL: config.LocalURL,
ModelName: config.LocalModel,
MaxRetries: 3,
})
case "remote":
provider, err = remotellm.NewRemoteProvider(remotellm.RemoteProviderConfig{
APIKey: config.RemoteAPIKey,
BaseURL: config.RemoteURL,
ModelName: config.RemoteModel,
MaxRetries: 3,
RequestsPerMin: 60,
})
default:
return nil, fmt.Errorf("unsupported provider type: %s", config.ProviderType)
}
if err != nil {
return nil, fmt.Errorf("failed to initialize provider: %w", err)
}
// Verify provider health
ctx := context.Background()
if err := provider.HealthCheck(ctx); err != nil {
return nil, fmt.Errorf("provider health check failed: %w", err)
}
writer, err := filesystem.NewArtifactWriter(config.OutputDir)
if err != nil {
return nil, fmt.Errorf("failed to initialize artifact writer: %w", err)
}
return &CLI{
generator: generator.NewArtifactGenerator(provider),
helmGenerator: helm.NewHelmGenerator(provider),
writer: writer,
provider: provider,
}, nil
}
// Run executes the CLI with the given arguments
func (c *CLI) Run(args []string) error {
if len(args) < 2 {
return c.printUsage()
}
command := args[1]
ctx := context.Background()
switch command {
case "dockerfile":
return c.handleDockerfile(ctx, args[2:])
case "deployment":
return c.handleDeployment(ctx, args[2:])
case "helm":
return c.handleHelm(ctx, args[2:])
case "refine":
return c.handleRefine(ctx, args[2:])
default:
return fmt.Errorf("unknown command: %s", command)
}
}
// handleDockerfile handles Dockerfile generation
func (c *CLI) handleDockerfile(ctx context.Context, args []string) error {
if len(args) < 2 {
return fmt.Errorf("usage: dockerfile <project-name> <requirements>")
}
projectName := args[0]
requirements := strings.Join(args[1:], " ")
fmt.Printf("Generating Dockerfile for project '%s'...\n", projectName)
dockerfile, err := c.generator.GenerateDockerfile(ctx, requirements)
if err != nil {
return fmt.Errorf("failed to generate Dockerfile: %w", err)
}
if err := c.writer.WriteDockerfile(dockerfile, projectName); err != nil {
return fmt.Errorf("failed to write Dockerfile: %w", err)
}
fmt.Printf("Dockerfile generated successfully in %s/Dockerfile\n", projectName)
fmt.Println("\nGenerated Dockerfile:")
fmt.Println(dockerfile.ToDockerfileContent())
return nil
}
// handleDeployment handles Kubernetes Deployment generation
func (c *CLI) handleDeployment(ctx context.Context, args []string) error {
if len(args) < 2 {
return fmt.Errorf("usage: deployment <project-name> <requirements>")
}
projectName := args[0]
requirements := strings.Join(args[1:], " ")
fmt.Printf("Generating Kubernetes Deployment for project '%s'...\n", projectName)
deployment, err := c.generator.GenerateDeployment(ctx, requirements)
if err != nil {
return fmt.Errorf("failed to generate Deployment: %w", err)
}
if err := c.writer.WriteKubernetesManifest(deployment, projectName, "deployment.yaml"); err != nil {
return fmt.Errorf("failed to write Deployment: %w", err)
}
fmt.Printf("Deployment manifest generated successfully in %s/k8s/deployment.yaml\n", projectName)
return nil
}
// handleHelm handles Helm chart generation
func (c *CLI) handleHelm(ctx context.Context, args []string) error {
if len(args) < 2 {
return fmt.Errorf("usage: helm <chart-name> <requirements>")
}
chartName := args[0]
requirements := strings.Join(args[1:], " ")
fmt.Printf("Generating Helm chart '%s'...\n", chartName)
chart, err := c.helmGenerator.GenerateChart(ctx, requirements)
if err != nil {
return fmt.Errorf("failed to generate Helm chart: %w", err)
}
if err := c.helmGenerator.ValidateChart(chart); err != nil {
return fmt.Errorf("generated chart is invalid: %w", err)
}
if err := c.writer.WriteHelmChart(chart, chartName); err != nil {
return fmt.Errorf("failed to write Helm chart: %w", err)
}
fmt.Printf("Helm chart generated successfully in %s/\n", chartName)
return nil
}
// handleRefine handles artifact refinement
func (c *CLI) handleRefine(ctx context.Context, args []string) error {
if len(args) < 2 {
return fmt.Errorf("usage: refine <artifact-type> <feedback>")
}
artifactType := args[0]
feedback := strings.Join(args[1:], " ")
fmt.Printf("Refining %s based on feedback...\n", artifactType)
refined, err := c.generator.RefineArtifact(ctx, artifactType, feedback)
if err != nil {
return fmt.Errorf("failed to refine artifact: %w", err)
}
fmt.Printf("Artifact refined successfully\n")
fmt.Printf("Refined artifact: %+v\n", refined)
return nil
}
// printUsage prints usage information
func (c *CLI) printUsage() error {
usage := `Docker and Kubernetes Artifact Generator
Usage:
agent dockerfile <project-name> <requirements>
agent deployment <project-name> <requirements>
agent helm <chart-name> <requirements>
agent refine <artifact-type> <feedback>
Commands:
dockerfile Generate a Dockerfile
deployment Generate a Kubernetes Deployment manifest
helm Generate a complete Helm chart
refine Refine a previously generated artifact
Examples:
agent dockerfile myapp "Go web application with PostgreSQL"
agent deployment myapp "3 replicas, health checks, resource limits"
agent helm myapp "microservice with database dependency"
agent refine dockerfile "use Alpine base image instead"
`
fmt.Println(usage)
return nil
}
// Close releases resources
func (c *CLI) Close() error {
return c.provider.Close()
}
The CLI provides a clean, intuitive interface for generating artifacts. Each command is self-contained and provides clear feedback to the user. Error messages are descriptive and guide users toward correct usage.
RUNNING EXAMPLE - COMPLETE PRODUCTION SYSTEM
The following section presents a complete, production-ready implementation that integrates all components discussed above. This system can generate Dockerfiles, Kubernetes manifests, and Helm charts for any application type.
// main.go - Entry point for the Docker and Kubernetes artifact generator agent
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"time"
"gopkg.in/yaml.v3"
)
// ========== LLM PROVIDER INTERFACES AND TYPES ==========
type Message struct {
Role string
Content string
Timestamp time.Time
}
type CompletionRequest struct {
Messages []Message
MaxTokens int
Temperature float64
TopP float64
StopSequences []string
PresencePenalty float64
Metadata map[string]string
}
type CompletionResponse struct {
Content string
FinishReason string
TokensUsed int
PromptTokens int
ModelUsed string
Metadata map[string]string
}
type ProviderCapabilities struct {
SupportsStreaming bool
MaxContextTokens int
SupportedModalities []string
RequiresAPIKey bool
GPUAccelerated bool
}
type Provider interface {
GenerateCompletion(ctx context.Context, req CompletionRequest) (*CompletionResponse, error)
GetCapabilities() ProviderCapabilities
HealthCheck(ctx context.Context) error
Close() error
}
// ========== DOMAIN MODELS ==========
type Dockerfile struct {
BaseImage string
Maintainer string
WorkDir string
Environment map[string]string
CopyInstructions []CopyInstruction
RunCommands []string
ExposePort []int
EntryPoint []string
Command []string
Volumes []string
User string
Labels map[string]string
BuildArgs map[string]string
HealthCheck *HealthCheck
}
type CopyInstruction struct {
Source string
Destination string
Chown string
}
type HealthCheck struct {
Command []string
Interval string
Timeout string
StartPeriod string
Retries int
}
func (d *Dockerfile) Validate() error {
if d.BaseImage == "" {
return fmt.Errorf("base image is required")
}
if len(d.EntryPoint) == 0 && len(d.Command) == 0 {
return fmt.Errorf("either ENTRYPOINT or CMD must be specified")
}
return nil
}
func (d *Dockerfile) ToDockerfileContent() string {
var builder strings.Builder
builder.WriteString(fmt.Sprintf("FROM %s\n\n", d.BaseImage))
if d.Maintainer != "" {
builder.WriteString(fmt.Sprintf("LABEL maintainer=\"%s\"\n\n", d.Maintainer))
}
if len(d.Labels) > 0 {
for key, value := range d.Labels {
builder.WriteString(fmt.Sprintf("LABEL %s=\"%s\"\n", key, value))
}
builder.WriteString("\n")
}
if len(d.BuildArgs) > 0 {
for key, value := range d.BuildArgs {
builder.WriteString(fmt.Sprintf("ARG %s=%s\n", key, value))
}
builder.WriteString("\n")
}
if len(d.Environment) > 0 {
for key, value := range d.Environment {
builder.WriteString(fmt.Sprintf("ENV %s=%s\n", key, value))
}
builder.WriteString("\n")
}
if d.WorkDir != "" {
builder.WriteString(fmt.Sprintf("WORKDIR %s\n\n", d.WorkDir))
}
for _, copy := range d.CopyInstructions {
if copy.Chown != "" {
builder.WriteString(fmt.Sprintf("COPY --chown=%s %s %s\n", copy.Chown, copy.Source, copy.Destination))
} else {
builder.WriteString(fmt.Sprintf("COPY %s %s\n", copy.Source, copy.Destination))
}
}
if len(d.CopyInstructions) > 0 {
builder.WriteString("\n")
}
for _, cmd := range d.RunCommands {
builder.WriteString(fmt.Sprintf("RUN %s\n", cmd))
}
if len(d.RunCommands) > 0 {
builder.WriteString("\n")
}
for _, vol := range d.Volumes {
builder.WriteString(fmt.Sprintf("VOLUME %s\n", vol))
}
if len(d.Volumes) > 0 {
builder.WriteString("\n")
}
for _, port := range d.ExposePort {
builder.WriteString(fmt.Sprintf("EXPOSE %d\n", port))
}
if len(d.ExposePort) > 0 {
builder.WriteString("\n")
}
if d.User != "" {
builder.WriteString(fmt.Sprintf("USER %s\n\n", d.User))
}
if d.HealthCheck != nil {
builder.WriteString("HEALTHCHECK ")
if d.HealthCheck.Interval != "" {
builder.WriteString(fmt.Sprintf("--interval=%s ", d.HealthCheck.Interval))
}
if d.HealthCheck.Timeout != "" {
builder.WriteString(fmt.Sprintf("--timeout=%s ", d.HealthCheck.Timeout))
}
if d.HealthCheck.StartPeriod != "" {
builder.WriteString(fmt.Sprintf("--start-period=%s ", d.HealthCheck.StartPeriod))
}
if d.HealthCheck.Retries > 0 {
builder.WriteString(fmt.Sprintf("--retries=%d ", d.HealthCheck.Retries))
}
builder.WriteString(fmt.Sprintf("CMD %s\n\n", strings.Join(d.HealthCheck.Command, " ")))
}
if len(d.EntryPoint) > 0 {
builder.WriteString(fmt.Sprintf("ENTRYPOINT [\"%s\"]\n", strings.Join(d.EntryPoint, "\", \"")))
}
if len(d.Command) > 0 {
builder.WriteString(fmt.Sprintf("CMD [\"%s\"]\n", strings.Join(d.Command, "\", \"")))
}
return builder.String()
}
type KubernetesDeployment struct {
APIVersion string `yaml:"apiVersion"`
Kind string `yaml:"kind"`
Metadata ObjectMeta `yaml:"metadata"`
Spec DeploymentSpec `yaml:"spec"`
}
type ObjectMeta struct {
Name string `yaml:"name"`
Namespace string `yaml:"namespace,omitempty"`
Labels map[string]string `yaml:"labels,omitempty"`
Annotations map[string]string `yaml:"annotations,omitempty"`
}
type DeploymentSpec struct {
Replicas int32 `yaml:"replicas"`
Selector *LabelSelector `yaml:"selector"`
Template PodTemplateSpec `yaml:"template"`
Strategy DeploymentStrategy `yaml:"strategy,omitempty"`
}
type LabelSelector struct {
MatchLabels map[string]string `yaml:"matchLabels"`
}
type PodTemplateSpec struct {
Metadata ObjectMeta `yaml:"metadata"`
Spec PodSpec `yaml:"spec"`
}
type PodSpec struct {
Containers []Container `yaml:"containers"`
InitContainers []Container `yaml:"initContainers,omitempty"`
Volumes []Volume `yaml:"volumes,omitempty"`
ServiceAccount string `yaml:"serviceAccountName,omitempty"`
SecurityContext *PodSecurityContext `yaml:"securityContext,omitempty"`
RestartPolicy string `yaml:"restartPolicy,omitempty"`
}
type Container struct {
Name string `yaml:"name"`
Image string `yaml:"image"`
ImagePullPolicy string `yaml:"imagePullPolicy,omitempty"`
Command []string `yaml:"command,omitempty"`
Args []string `yaml:"args,omitempty"`
Ports []ContainerPort `yaml:"ports,omitempty"`
Env []EnvVar `yaml:"env,omitempty"`
VolumeMounts []VolumeMount `yaml:"volumeMounts,omitempty"`
Resources ResourceRequirements `yaml:"resources,omitempty"`
LivenessProbe *Probe `yaml:"livenessProbe,omitempty"`
ReadinessProbe *Probe `yaml:"readinessProbe,omitempty"`
SecurityContext *SecurityContext `yaml:"securityContext,omitempty"`
}
type ContainerPort struct {
Name string `yaml:"name,omitempty"`
ContainerPort int32 `yaml:"containerPort"`
Protocol string `yaml:"protocol,omitempty"`
}
type EnvVar struct {
Name string `yaml:"name"`
Value string `yaml:"value,omitempty"`
ValueFrom *EnvVarSource `yaml:"valueFrom,omitempty"`
}
type EnvVarSource struct {
ConfigMapKeyRef *ConfigMapKeySelector `yaml:"configMapKeyRef,omitempty"`
SecretKeyRef *SecretKeySelector `yaml:"secretKeyRef,omitempty"`
FieldRef *FieldSelector `yaml:"fieldRef,omitempty"`
}
type ConfigMapKeySelector struct {
Name string `yaml:"name"`
Key string `yaml:"key"`
}
type SecretKeySelector struct {
Name string `yaml:"name"`
Key string `yaml:"key"`
}
type FieldSelector struct {
FieldPath string `yaml:"fieldPath"`
}
type VolumeMount struct {
Name string `yaml:"name"`
MountPath string `yaml:"mountPath"`
ReadOnly bool `yaml:"readOnly,omitempty"`
SubPath string `yaml:"subPath,omitempty"`
}
type ResourceRequirements struct {
Limits map[string]string `yaml:"limits,omitempty"`
Requests map[string]string `yaml:"requests,omitempty"`
}
type Probe struct {
HTTPGet *HTTPGetAction `yaml:"httpGet,omitempty"`
TCPSocket *TCPSocketAction `yaml:"tcpSocket,omitempty"`
Exec *ExecAction `yaml:"exec,omitempty"`
InitialDelaySeconds int32 `yaml:"initialDelaySeconds,omitempty"`
PeriodSeconds int32 `yaml:"periodSeconds,omitempty"`
TimeoutSeconds int32 `yaml:"timeoutSeconds,omitempty"`
SuccessThreshold int32 `yaml:"successThreshold,omitempty"`
FailureThreshold int32 `yaml:"failureThreshold,omitempty"`
}
type HTTPGetAction struct {
Path string `yaml:"path"`
Port int32 `yaml:"port"`
Scheme string `yaml:"scheme,omitempty"`
Headers map[string]string `yaml:"httpHeaders,omitempty"`
}
type TCPSocketAction struct {
Port int32 `yaml:"port"`
}
type ExecAction struct {
Command []string `yaml:"command"`
}
type SecurityContext struct {
RunAsUser *int64 `yaml:"runAsUser,omitempty"`
RunAsGroup *int64 `yaml:"runAsGroup,omitempty"`
RunAsNonRoot *bool `yaml:"runAsNonRoot,omitempty"`
ReadOnlyRootFilesystem *bool `yaml:"readOnlyRootFilesystem,omitempty"`
Capabilities *Capabilities `yaml:"capabilities,omitempty"`
}
type Capabilities struct {
Add []string `yaml:"add,omitempty"`
Drop []string `yaml:"drop,omitempty"`
}
type PodSecurityContext struct {
RunAsUser *int64 `yaml:"runAsUser,omitempty"`
RunAsGroup *int64 `yaml:"runAsGroup,omitempty"`
FSGroup *int64 `yaml:"fsGroup,omitempty"`
RunAsNonRoot *bool `yaml:"runAsNonRoot,omitempty"`
}
type Volume struct {
Name string `yaml:"name"`
ConfigMap *ConfigMapVolume `yaml:"configMap,omitempty"`
Secret *SecretVolume `yaml:"secret,omitempty"`
EmptyDir *EmptyDirVolume `yaml:"emptyDir,omitempty"`
PersistentVolumeClaim *PVCVolume `yaml:"persistentVolumeClaim,omitempty"`
}
type ConfigMapVolume struct {
Name string `yaml:"name"`
}
type SecretVolume struct {
SecretName string `yaml:"secretName"`
}
type EmptyDirVolume struct {
Medium string `yaml:"medium,omitempty"`
}
type PVCVolume struct {
ClaimName string `yaml:"claimName"`
}
type DeploymentStrategy struct {
Type string `yaml:"type,omitempty"`
RollingUpdate *RollingUpdateStrategy `yaml:"rollingUpdate,omitempty"`
}
type RollingUpdateStrategy struct {
MaxUnavailable string `yaml:"maxUnavailable,omitempty"`
MaxSurge string `yaml:"maxSurge,omitempty"`
}
// ========== HELM CHART STRUCTURES ==========
type HelmChart struct {
ChartMetadata ChartMetadata
Values map[string]interface{}
Templates map[string]string
Helpers string
}
type ChartMetadata struct {
APIVersion string `yaml:"apiVersion"`
Name string `yaml:"name"`
Version string `yaml:"version"`
AppVersion string `yaml:"appVersion"`
Description string `yaml:"description"`
Keywords []string `yaml:"keywords,omitempty"`
Maintainers []Maintainer `yaml:"maintainers,omitempty"`
Dependencies []Dependency `yaml:"dependencies,omitempty"`
Annotations map[string]string `yaml:"annotations,omitempty"`
}
type Maintainer struct {
Name string `yaml:"name"`
Email string `yaml:"email,omitempty"`
}
type Dependency struct {
Name string `yaml:"name"`
Version string `yaml:"version"`
Repository string `yaml:"repository"`
Condition string `yaml:"condition,omitempty"`
}
// ========== MOCK LLM PROVIDER FOR DEMONSTRATION ==========
type MockProvider struct {
capabilities ProviderCapabilities
}
func NewMockProvider() *MockProvider {
return &MockProvider{
capabilities: ProviderCapabilities{
SupportsStreaming: false,
MaxContextTokens: 8192,
SupportedModalities: []string{"text"},
RequiresAPIKey: false,
GPUAccelerated: true,
},
}
}
func (m *MockProvider) GenerateCompletion(ctx context.Context, req CompletionRequest) (*CompletionResponse, error) {
// Analyze the request to determine what artifact to generate
lastMessage := req.Messages[len(req.Messages)-1].Content
var responseContent string
if strings.Contains(strings.ToLower(lastMessage), "dockerfile") {
responseContent = m.generateDockerfileResponse(lastMessage)
} else if strings.Contains(strings.ToLower(lastMessage), "deployment") {
responseContent = m.generateDeploymentResponse(lastMessage)
} else if strings.Contains(strings.ToLower(lastMessage), "helm") {
responseContent = m.generateHelmResponse(lastMessage)
} else {
responseContent = `{"artifact_type": "unknown", "content": {}}`
}
return &CompletionResponse{
Content: responseContent,
FinishReason: "stop",
TokensUsed: 500,
PromptTokens: 200,
ModelUsed: "mock-model-v1",
Metadata: map[string]string{"gpu_backend": "cuda"},
}, nil
}
func (m *MockProvider) generateDockerfileResponse(requirements string) string {
// Generate a comprehensive Dockerfile based on requirements
response := map[string]interface{}{
"artifact_type": "dockerfile",
"content": map[string]interface{}{
"base_image": "golang:1.21-alpine",
"maintainer": "devops@example.com",
"workdir": "/app",
"environment": map[string]string{
"GO111MODULE": "on",
"CGO_ENABLED": "0",
},
"copy_instructions": []map[string]string{
{"source": "go.mod", "destination": ".", "chown": ""},
{"source": "go.sum", "destination": ".", "chown": ""},
{"source": ".", "destination": ".", "chown": ""},
},
"run_commands": []string{
"apk add --no-cache ca-certificates",
"go mod download",
"go build -o /app/server .",
},
"expose_ports": []int{8080},
"entrypoint": []string{"/app/server"},
"command": []string{},
"volumes": []string{},
"user": "nobody",
"labels": map[string]string{
"version": "1.0.0",
"description": "Production-ready Go application",
},
"build_args": map[string]string{
"VERSION": "1.0.0",
},
"healthcheck": map[string]interface{}{
"command": []string{"CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1"},
"interval": "30s",
"timeout": "5s",
"start_period": "10s",
"retries": 3,
},
},
}
jsonBytes, _ := json.Marshal(response)
return string(jsonBytes)
}
func (m *MockProvider) generateDeploymentResponse(requirements string) string {
runAsNonRoot := true
runAsUser := int64(1000)
readOnlyRoot := true
response := map[string]interface{}{
"artifact_type": "deployment",
"content": KubernetesDeployment{
APIVersion: "apps/v1",
Kind: "Deployment",
Metadata: ObjectMeta{
Name: "myapp",
Namespace: "default",
Labels: map[string]string{
"app": "myapp",
"version": "v1",
},
},
Spec: DeploymentSpec{
Replicas: 3,
Selector: &LabelSelector{
MatchLabels: map[string]string{
"app": "myapp",
},
},
Template: PodTemplateSpec{
Metadata: ObjectMeta{
Labels: map[string]string{
"app": "myapp",
"version": "v1",
},
},
Spec: PodSpec{
Containers: []Container{
{
Name: "myapp",
Image: "myapp:1.0.0",
ImagePullPolicy: "IfNotPresent",
Ports: []ContainerPort{
{
Name: "http",
ContainerPort: 8080,
Protocol: "TCP",
},
},
Env: []EnvVar{
{
Name: "APP_ENV",
Value: "production",
},
},
Resources: ResourceRequirements{
Limits: map[string]string{
"cpu": "500m",
"memory": "512Mi",
},
Requests: map[string]string{
"cpu": "250m",
"memory": "256Mi",
},
},
LivenessProbe: &Probe{
HTTPGet: &HTTPGetAction{
Path: "/health",
Port: 8080,
Scheme: "HTTP",
},
InitialDelaySeconds: 30,
PeriodSeconds: 10,
TimeoutSeconds: 5,
FailureThreshold: 3,
},
ReadinessProbe: &Probe{
HTTPGet: &HTTPGetAction{
Path: "/ready",
Port: 8080,
Scheme: "HTTP",
},
InitialDelaySeconds: 10,
PeriodSeconds: 5,
TimeoutSeconds: 3,
FailureThreshold: 2,
},
SecurityContext: &SecurityContext{
RunAsNonRoot: &runAsNonRoot,
RunAsUser: &runAsUser,
ReadOnlyRootFilesystem: &readOnlyRoot,
Capabilities: &Capabilities{
Drop: []string{"ALL"},
},
},
},
},
SecurityContext: &PodSecurityContext{
RunAsNonRoot: &runAsNonRoot,
RunAsUser: &runAsUser,
},
},
},
Strategy: DeploymentStrategy{
Type: "RollingUpdate",
RollingUpdate: &RollingUpdateStrategy{
MaxUnavailable: "1",
MaxSurge: "1",
},
},
},
},
}
jsonBytes, _ := json.Marshal(response)
return string(jsonBytes)
}
func (m *MockProvider) generateHelmResponse(requirements string) string {
response := map[string]interface{}{
"chart_metadata": ChartMetadata{
APIVersion: "v2",
Name: "myapp",
Version: "1.0.0",
AppVersion: "1.0.0",
Description: "A production-ready Helm chart for myapp",
Keywords: []string{"application", "microservice"},
Maintainers: []Maintainer{
{Name: "DevOps Team", Email: "devops@example.com"},
},
},
"values": map[string]interface{}{
"replicaCount": 3,
"image": map[string]interface{}{
"repository": "myapp",
"tag": "1.0.0",
"pullPolicy": "IfNotPresent",
},
"service": map[string]interface{}{
"type": "ClusterIP",
"port": 80,
},
"resources": map[string]interface{}{
"limits": map[string]string{
"cpu": "500m",
"memory": "512Mi",
},
"requests": map[string]string{
"cpu": "250m",
"memory": "256Mi",
},
},
},
"templates": map[string]string{
"deployment.yaml": `apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "myapp.fullname" . }}
labels:
{{- include "myapp.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "myapp.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "myapp.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 8080
protocol: TCP
resources:
{{- toYaml .Values.resources | nindent 10 }}`,
"service.yaml": `apiVersion: v1
kind: Service
metadata:
name: {{ include "myapp.fullname" . }}
labels:
{{- include "myapp.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "myapp.selectorLabels" . | nindent 4 }}`,
},
"helpers": `{{- define "myapp.fullname" -}}
{{- .Release.Name }}-{{ .Chart.Name }}
{{- end }}
{{- define "myapp.labels" -}}
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
{{ include "myapp.selectorLabels" . }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{- define "myapp.selectorLabels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}`,
}
jsonBytes, _ := json.Marshal(response)
return string(jsonBytes)
}
func (m *MockProvider) GetCapabilities() ProviderCapabilities {
return m.capabilities
}
func (m *MockProvider) HealthCheck(ctx context.Context) error {
return nil
}
func (m *MockProvider) Close() error {
return nil
}
// ========== ARTIFACT GENERATOR ==========
type ConversationContext struct {
Messages []Message
GeneratedArtifacts map[string]interface{}
}
type ArtifactGenerator struct {
provider Provider
systemPrompt string
conversationCtx *ConversationContext
}
func NewArtifactGenerator(provider Provider) *ArtifactGenerator {
systemPrompt := `You are an expert in Docker and Kubernetes. Your task is to generate production-ready Docker and Kubernetes artifacts based on user requirements.
When generating Dockerfiles:
- Use multi-stage builds when appropriate
- Follow security best practices (non-root user, minimal base images)
- Optimize for layer caching
- Include health checks
- Use specific version tags, not 'latest'
When generating Kubernetes manifests:
- Include resource limits and requests
- Configure appropriate health probes
- Use security contexts
- Follow the principle of least privilege
- Include labels for organization
Always respond with valid JSON containing the artifact structure.`
return &ArtifactGenerator{
provider: provider,
systemPrompt: systemPrompt,
conversationCtx: &ConversationContext{
Messages: []Message{
{Role: "system", Content: systemPrompt, Timestamp: time.Now()},
},
GeneratedArtifacts: make(map[string]interface{}),
},
}
}
func (g *ArtifactGenerator) GenerateDockerfile(ctx context.Context, requirements string) (*Dockerfile, error) {
userMessage := Message{
Role: "user",
Content: fmt.Sprintf("Generate a Dockerfile with the following requirements: %s", requirements),
Timestamp: time.Now(),
}
g.conversationCtx.Messages = append(g.conversationCtx.Messages, userMessage)
request := CompletionRequest{
Messages: g.conversationCtx.Messages,
MaxTokens: 2000,
Temperature: 0.3,
TopP: 0.9,
}
response, err := g.provider.GenerateCompletion(ctx, request)
if err != nil {
return nil, fmt.Errorf("failed to generate completion: %w", err)
}
assistantMessage := Message{
Role: "assistant",
Content: response.Content,
Timestamp: time.Now(),
}
g.conversationCtx.Messages = append(g.conversationCtx.Messages, assistantMessage)
dockerfile, err := parseDockerfileResponse(response.Content)
if err != nil {
return nil, fmt.Errorf("failed to parse Dockerfile response: %w", err)
}
if err := dockerfile.Validate(); err != nil {
return nil, fmt.Errorf("generated Dockerfile is invalid: %w", err)
}
g.conversationCtx.GeneratedArtifacts["dockerfile"] = dockerfile
return dockerfile, nil
}
func (g *ArtifactGenerator) GenerateDeployment(ctx context.Context, requirements string) (*KubernetesDeployment, error) {
userMessage := Message{
Role: "user",
Content: fmt.Sprintf("Generate a Kubernetes Deployment manifest with the following requirements: %s", requirements),
Timestamp: time.Now(),
}
g.conversationCtx.Messages = append(g.conversationCtx.Messages, userMessage)
request := CompletionRequest{
Messages: g.conversationCtx.Messages,
MaxTokens: 3000,
Temperature: 0.3,
TopP: 0.9,
}
response, err := g.provider.GenerateCompletion(ctx, request)
if err != nil {
return nil, fmt.Errorf("failed to generate completion: %w", err)
}
assistantMessage := Message{
Role: "assistant",
Content: response.Content,
Timestamp: time.Now(),
}
g.conversationCtx.Messages = append(g.conversationCtx.Messages, assistantMessage)
deployment, err := parseDeploymentResponse(response.Content)
if err != nil {
return nil, fmt.Errorf("failed to parse Deployment response: %w", err)
}
g.conversationCtx.GeneratedArtifacts["deployment"] = deployment
return deployment, nil
}
func parseDockerfileResponse(content string) (*Dockerfile, error) {
jsonContent := extractJSON(content)
if jsonContent == "" {
return nil, fmt.Errorf("no JSON found in response")
}
var response struct {
ArtifactType string `json:"artifact_type"`
Content struct {
BaseImage string `json:"base_image"`
Maintainer string `json:"maintainer"`
WorkDir string `json:"workdir"`
Environment map[string]string `json:"environment"`
CopyInstructions []struct {
Source string `json:"source"`
Destination string `json:"destination"`
Chown string `json:"chown"`
} `json:"copy_instructions"`
RunCommands []string `json:"run_commands"`
ExposePort []int `json:"expose_ports"`
EntryPoint []string `json:"entrypoint"`
Command []string `json:"command"`
Volumes []string `json:"volumes"`
User string `json:"user"`
Labels map[string]string `json:"labels"`
BuildArgs map[string]string `json:"build_args"`
HealthCheck *struct {
Command []string `json:"command"`
Interval string `json:"interval"`
Timeout string `json:"timeout"`
StartPeriod string `json:"start_period"`
Retries int `json:"retries"`
} `json:"healthcheck"`
} `json:"content"`
}
if err := json.Unmarshal([]byte(jsonContent), &response); err != nil {
return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
}
dockerfile := &Dockerfile{
BaseImage: response.Content.BaseImage,
Maintainer: response.Content.Maintainer,
WorkDir: response.Content.WorkDir,
Environment: response.Content.Environment,
RunCommands: response.Content.RunCommands,
ExposePort: response.Content.ExposePort,
EntryPoint: response.Content.EntryPoint,
Command: response.Content.Command,
Volumes: response.Content.Volumes,
User: response.Content.User,
Labels: response.Content.Labels,
BuildArgs: response.Content.BuildArgs,
}
for _, ci := range response.Content.CopyInstructions {
dockerfile.CopyInstructions = append(dockerfile.CopyInstructions, CopyInstruction{
Source: ci.Source,
Destination: ci.Destination,
Chown: ci.Chown,
})
}
if response.Content.HealthCheck != nil {
dockerfile.HealthCheck = &HealthCheck{
Command: response.Content.HealthCheck.Command,
Interval: response.Content.HealthCheck.Interval,
Timeout: response.Content.HealthCheck.Timeout,
StartPeriod: response.Content.HealthCheck.StartPeriod,
Retries: response.Content.HealthCheck.Retries,
}
}
return dockerfile, nil
}
func parseDeploymentResponse(content string) (*KubernetesDeployment, error) {
jsonContent := extractJSON(content)
if jsonContent == "" {
return nil, fmt.Errorf("no JSON found in response")
}
var response struct {
ArtifactType string `json:"artifact_type"`
Content KubernetesDeployment `json:"content"`
}
if err := json.Unmarshal([]byte(jsonContent), &response); err != nil {
return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
}
return &response.Content, nil
}
func extractJSON(content string) string {
start := strings.Index(content, "{")
end := strings.LastIndex(content, "}")
if start == -1 || end == -1 || start >= end {
return ""
}
return content[start : end+1]
}
// ========== HELM GENERATOR ==========
type HelmGenerator struct {
provider Provider
systemPrompt string
}
func NewHelmGenerator(provider Provider) *HelmGenerator {
systemPrompt := `You are an expert in Helm chart development. Generate production-ready Helm charts following best practices.`
return &HelmGenerator{
provider: provider,
systemPrompt: systemPrompt,
}
}
func (g *HelmGenerator) GenerateChart(ctx context.Context, requirements string) (*HelmChart, error) {
messages := []Message{
{Role: "system", Content: g.systemPrompt, Timestamp: time.Now()},
{Role: "user", Content: fmt.Sprintf("Generate a Helm chart with the following requirements: %s", requirements), Timestamp: time.Now()},
}
request := CompletionRequest{
Messages: messages,
MaxTokens: 4000,
Temperature: 0.3,
TopP: 0.9,
}
response, err := g.provider.GenerateCompletion(ctx, request)
if err != nil {
return nil, fmt.Errorf("failed to generate completion: %w", err)
}
chart, err := parseChartResponse(response.Content)
if err != nil {
return nil, fmt.Errorf("failed to parse chart response: %w", err)
}
return chart, nil
}
func parseChartResponse(content string) (*HelmChart, error) {
jsonContent := extractJSON(content)
if jsonContent == "" {
return nil, fmt.Errorf("no JSON found in response")
}
var response struct {
ChartMetadata ChartMetadata `json:"chart_metadata"`
Values map[string]interface{} `json:"values"`
Templates map[string]string `json:"templates"`
Helpers string `json:"helpers"`
}
if err := json.Unmarshal([]byte(jsonContent), &response); err != nil {
return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
}
return &HelmChart{
ChartMetadata: response.ChartMetadata,
Values: response.Values,
Templates: response.Templates,
Helpers: response.Helpers,
}, nil
}
// ========== FILESYSTEM WRITER ==========
type ArtifactWriter struct {
baseDir string
}
func NewArtifactWriter(baseDir string) (*ArtifactWriter, error) {
if err := os.MkdirAll(baseDir, 0755); err != nil {
return nil, fmt.Errorf("failed to create base directory: %w", err)
}
return &ArtifactWriter{baseDir: baseDir}, nil
}
func (w *ArtifactWriter) WriteDockerfile(dockerfile *Dockerfile, projectName string) error {
projectDir := filepath.Join(w.baseDir, projectName)
if err := os.MkdirAll(projectDir, 0755); err != nil {
return fmt.Errorf("failed to create project directory: %w", err)
}
dockerfilePath := filepath.Join(projectDir, "Dockerfile")
content := dockerfile.ToDockerfileContent()
if err := os.WriteFile(dockerfilePath, []byte(content), 0644); err != nil {
return fmt.Errorf("failed to write Dockerfile: %w", err)
}
return nil
}
func (w *ArtifactWriter) WriteKubernetesManifest(manifest interface{}, projectName, filename string) error {
manifestDir := filepath.Join(w.baseDir, projectName, "k8s")
if err := os.MkdirAll(manifestDir, 0755); err != nil {
return fmt.Errorf("failed to create manifest directory: %w", err)
}
manifestPath := filepath.Join(manifestDir, filename)
yamlContent, err := yaml.Marshal(manifest)
if err != nil {
return fmt.Errorf("failed to marshal manifest to YAML: %w", err)
}
if err := os.WriteFile(manifestPath, yamlContent, 0644); err != nil {
return fmt.Errorf("failed to write manifest: %w", err)
}
return nil
}
func (w *ArtifactWriter) WriteHelmChart(chart *HelmChart, chartName string) error {
chartDir := filepath.Join(w.baseDir, chartName)
dirs := []string{
chartDir,
filepath.Join(chartDir, "templates"),
filepath.Join(chartDir, "charts"),
}
for _, dir := range dirs {
if err := os.MkdirAll(dir, 0755); err != nil {
return fmt.Errorf("failed to create directory %s: %w", dir, err)
}
}
chartYAMLPath := filepath.Join(chartDir, "Chart.yaml")
chartYAML, err := yaml.Marshal(chart.ChartMetadata)
if err != nil {
return fmt.Errorf("failed to marshal Chart.yaml: %w", err)
}
if err := os.WriteFile(chartYAMLPath, chartYAML, 0644); err != nil {
return fmt.Errorf("failed to write Chart.yaml: %w", err)
}
valuesYAMLPath := filepath.Join(chartDir, "values.yaml")
valuesYAML, err := yaml.Marshal(chart.Values)
if err != nil {
return fmt.Errorf("failed to marshal values.yaml: %w", err)
}
if err := os.WriteFile(valuesYAMLPath, valuesYAML, 0644); err != nil {
return fmt.Errorf("failed to write values.yaml: %w", err)
}
for filename, content := range chart.Templates {
templatePath := filepath.Join(chartDir, "templates", filename)
if err := os.WriteFile(templatePath, []byte(content), 0644); err != nil {
return fmt.Errorf("failed to write template %s: %w", filename, err)
}
}
if chart.Helpers != "" {
helpersPath := filepath.Join(chartDir, "templates", "_helpers.tpl")
if err := os.WriteFile(helpersPath, []byte(chart.Helpers), 0644); err != nil {
return fmt.Errorf("failed to write _helpers.tpl: %w", err)
}
}
return nil
}
// ========== MAIN APPLICATION ==========
func main() {
if len(os.Args) < 2 {
printUsage()
os.Exit(1)
}
command := os.Args[1]
provider := NewMockProvider()
defer provider.Close()
ctx := context.Background()
if err := provider.HealthCheck(ctx); err != nil {
log.Fatalf("Provider health check failed: %v", err)
}
generator := NewArtifactGenerator(provider)
helmGenerator := NewHelmGenerator(provider)
writer, err := NewArtifactWriter("./output")
if err != nil {
log.Fatalf("Failed to initialize artifact writer: %v", err)
}
switch command {
case "dockerfile":
if len(os.Args) < 4 {
fmt.Println("Usage: agent dockerfile <project-name> <requirements>")
os.Exit(1)
}
projectName := os.Args[2]
requirements := strings.Join(os.Args[3:], " ")
fmt.Printf("Generating Dockerfile for project '%s'...\n", projectName)
dockerfile, err := generator.GenerateDockerfile(ctx, requirements)
if err != nil {
log.Fatalf("Failed to generate Dockerfile: %v", err)
}
if err := writer.WriteDockerfile(dockerfile, projectName); err != nil {
log.Fatalf("Failed to write Dockerfile: %v", err)
}
fmt.Printf("Dockerfile generated successfully in output/%s/Dockerfile\n", projectName)
fmt.Println("\nGenerated Dockerfile:")
fmt.Println(dockerfile.ToDockerfileContent())
case "deployment":
if len(os.Args) < 4 {
fmt.Println("Usage: agent deployment <project-name> <requirements>")
os.Exit(1)
}
projectName := os.Args[2]
requirements := strings.Join(os.Args[3:], " ")
fmt.Printf("Generating Kubernetes Deployment for project '%s'...\n", projectName)
deployment, err := generator.GenerateDeployment(ctx, requirements)
if err != nil {
log.Fatalf("Failed to generate Deployment: %v", err)
}
if err := writer.WriteKubernetesManifest(deployment, projectName, "deployment.yaml"); err != nil {
log.Fatalf("Failed to write Deployment: %v", err)
}
fmt.Printf("Deployment manifest generated successfully in output/%s/k8s/deployment.yaml\n", projectName)
case "helm":
if len(os.Args) < 4 {
fmt.Println("Usage: agent helm <chart-name> <requirements>")
os.Exit(1)
}
chartName := os.Args[2]
requirements := strings.Join(os.Args[3:], " ")
fmt.Printf("Generating Helm chart '%s'...\n", chartName)
chart, err := helmGenerator.GenerateChart(ctx, requirements)
if err != nil {
log.Fatalf("Failed to generate Helm chart: %v", err)
}
if err := writer.WriteHelmChart(chart, chartName); err != nil {
log.Fatalf("Failed to write Helm chart: %v", err)
}
fmt.Printf("Helm chart generated successfully in output/%s/\n", chartName)
default:
fmt.Printf("Unknown command: %s\n", command)
printUsage()
os.Exit(1)
}
}
func printUsage() {
usage := `Docker and Kubernetes Artifact Generator
Usage:
agent dockerfile <project-name> <requirements>
agent deployment <project-name> <requirements>
agent helm <chart-name> <requirements>
Commands:
dockerfile Generate a Dockerfile
deployment Generate a Kubernetes Deployment manifest
helm Generate a complete Helm chart
Examples:
agent dockerfile myapp "Go web application with PostgreSQL"
agent deployment myapp "3 replicas, health checks, resource limits"
agent helm myapp "microservice with database dependency"
`
fmt.Println(usage)
}
CONCLUSION AND DEPLOYMENT CONSIDERATIONS
This comprehensive implementation provides a production-ready system for generating Docker and Kubernetes artifacts using Large Language Models. The architecture supports both local and remote LLM providers, accommodates various GPU backends, and generates complete, validated artifacts that follow industry best practices.
The system can be extended to support additional artifact types such as ConfigMaps, Secrets, Services, Ingresses, and StatefulSets. The conversational context management allows for iterative refinement, enabling users to provide feedback and improve generated artifacts through natural language interactions.
For production deployment, consider implementing additional features such as artifact versioning, template libraries for common patterns, integration with CI/CD pipelines, and automated testing of generated configurations. The modular architecture facilitates these enhancements without requiring significant refactoring.
The GPU backend detection and provider abstraction ensure that the system can leverage the most appropriate computational resources available, whether running on developer workstations with various GPU architectures or in cloud environments with remote API access. This flexibility makes the system suitable for diverse deployment scenarios while maintaining consistent functionality and user experience.
No comments:
Post a Comment