helpers

package
v0.0.0-...-a14d088 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 2, 2026 License: MIT Imports: 9 Imported by: 0

README

Helper Library

High-level convenience functions for common patterns with automatic capability checking, fallback handling, and standardized error messages.

Overview

The helper library wraps the sampling and elicitation APIs with opinionated, production-ready patterns:

  • Automatic capability checking - Detect if sampling/elicitation is available
  • Graceful fallback - Use defaults when capabilities unavailable
  • Standardized error handling - Consistent error messages and types
  • Logging and observability - Built-in structured logging
  • Best practice patterns - Proven patterns for common scenarios

Quick Start

import "github.com/amp-labs/server/builder-mcp/statemachine/helpers"

// Sampling with fallback
explanation, err := helpers.SampleWithFallback(
    ctx,
    samplingClient,
    "Explain why the error occurred: " + errorMsg,
    "The operation failed. Please check the logs for details.",
)

// Elicitation with defaults
answers, err := helpers.ElicitWithDefaults(
    ctx,
    elicitationClient,
    question,
    map[string]string{
        "provider": "salesforce", // Default if user can't answer
    },
)

// Validation
err := helpers.ValidateRequired(input, []string{
    "projectId",
    "integrationId",
})

Sampling Helpers

Core Functions
  • SampleWithFallback - Generate content with automatic fallback
  • SampleJSON - Generate structured JSON with schema validation
  • SampleWithRetry - Generate content with automatic retries
  • CanSample - Check if sampling is available
Usage Patterns

Basic Sampling:

content, err := helpers.SampleWithFallback(
    ctx,
    client,
    "Generate recommendations for {{provider}}",
    "Use standard best practices for this provider",
)

JSON Generation:

type Recommendation struct {
    Action   string   `json:"action"`
    Priority string   `json:"priority"`
    Reasons  []string `json:"reasons"`
}

var rec Recommendation
err := helpers.SampleJSON(
    ctx,
    client,
    "Generate recommendations for improving integration health",
    &rec,
)

With Retries:

content, err := helpers.SampleWithRetry(
    ctx,
    client,
    "Generate config for " + provider,
    3, // Max retries
)

Conditional Sampling:

if helpers.CanSample(ctx, client) {
    content, _ := helpers.SampleWithFallback(ctx, client, prompt, fallback)
} else {
    content = fallback
}

Elicitation Helpers

Core Functions
  • ElicitWithDefaults - Collect input with automatic defaults
  • ElicitConfirmation - Ask yes/no question
  • ElicitMultipleChoice - Collect multiple selections
  • ElicitText - Collect free-form text with validation
  • CanElicit - Check if elicitation is available
Usage Patterns

Single Choice:

question := ElicitationQuestion{
    Text: "Which provider?",
    Type: "single_choice",
    Options: []Option{
        {Label: "Salesforce", Value: "salesforce"},
        {Label: "HubSpot", Value: "hubspot"},
    },
}

answers, err := helpers.ElicitWithDefaults(
    ctx,
    client,
    question,
    map[string]string{"selection": "salesforce"},
)

provider := answers["selection"]

Confirmation:

confirmed, err := helpers.ElicitConfirmation(
    ctx,
    client,
    "Enable webhooks?",
    true, // Default to yes
)

Multiple Choice:

objects, err := helpers.ElicitMultipleChoice(
    ctx,
    client,
    "Which objects?",
    []string{"Account", "Contact", "Lead", "Opportunity"},
    []string{"Account", "Contact"}, // Defaults
)

Text with Validation:

projectName, err := helpers.ElicitText(
    ctx,
    client,
    "Enter project name:",
    "my-integration",
    func(s string) error {
        if len(s) < 3 {
            return errors.New("name too short")
        }
        return nil
    },
)

Validation Helpers

Core Functions
  • ValidateRequired - Check required fields present
  • ValidateEnum - Check value in allowed list
  • ValidateRegex - Check value matches pattern
  • ValidateStruct - Validate struct with tags
Usage Patterns

Required Fields:

err := helpers.ValidateRequired(input, []string{
    "projectId",
    "integrationId",
    "provider",
})

Enum Validation:

err := helpers.ValidateEnum(provider, []string{
    "salesforce",
    "hubspot",
    "notion",
})

Regex Validation:

err := helpers.ValidateRegex(email, `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)

Struct Validation:

type Config struct {
    Provider string   `validate:"required,enum=salesforce|hubspot"`
    Objects  []string `validate:"required,min=1"`
    Email    string   `validate:"email"`
}

err := helpers.ValidateStruct(config)

Error Handling

All helpers follow consistent error handling:

Capability Unavailable:

  • Returns fallback/default value
  • Never returns error for unavailability
  • Logs warning for observability

Validation Failure:

  • Returns descriptive error
  • Includes field name and reason
  • Aggregates multiple errors

Critical Failure:

  • Returns error
  • Includes context
  • Wrapped for tracing

Best Practices

Always Provide Fallbacks
// ✓ Good
content, _ := helpers.SampleWithFallback(ctx, client, prompt, "Fallback content")

// ✗ Bad
content, err := client.Sample(ctx, prompt)
if err != nil {
    // Now what?
}
Validate Early
// ✓ Good
if err := helpers.ValidateRequired(input, requiredFields); err != nil {
    return nil, err
}
// Continue processing

// ✗ Bad
processData(input)
if err := helpers.ValidateRequired(input, requiredFields); err != nil {
    // Already processed invalid data!
}
Check Capabilities
// ✓ Good
if helpers.CanElicit(ctx, client) {
    // Use elicitation
} else {
    // Use defaults
}

// ✗ Bad
answers, _ := client.Elicit(ctx, question) // May fail!
Provide Meaningful Defaults
// ✓ Good
defaults := map[string]string{
    "provider": "salesforce", // Most common
    "sync": "true",           // Sensible default
}

// ✗ Bad
defaults := map[string]string{
    "provider": "",  // Empty
    "sync": "null",  // Confusing
}

Integration with State Machines

Helpers work seamlessly with state machine actions:

In YAML:

- type: sampling
  name: generate_content
  parameters:
    prompt: "Generate content"
    fallback: "Default content"

In Handler:

func generateContentHandler(ctx context.Context, smCtx *statemachine.Context) error {
    content, err := helpers.SampleWithFallback(
        ctx,
        samplingClient,
        "Generate content for {{provider}}",
        "Default content",
    )
    if err != nil {
        return err
    }
    smCtx.Set("content", content)
    return nil
}

Common Patterns

Sampling with Elicitation Fallback
var content string

if helpers.CanSample(ctx, samplingClient) {
    content, _ = helpers.SampleWithFallback(ctx, samplingClient, prompt, fallback)
} else if helpers.CanElicit(ctx, elicitClient) {
    answers, _ := helpers.ElicitText(ctx, elicitClient, "Enter content:", fallback, nil)
    content = answers
} else {
    content = fallback
}
Validated Elicitation
provider, err := helpers.ElicitText(
    ctx,
    client,
    "Enter provider:",
    "salesforce",
    func(p string) error {
        return helpers.ValidateEnum(p, []string{"salesforce", "hubspot", "notion"})
    },
)
Conditional Sampling
func smartGenerate(ctx context.Context, enabled bool) (string, error) {
    if !enabled || !helpers.CanSample(ctx, client) {
        return "Template content", nil
    }

    return helpers.SampleWithFallback(
        ctx,
        client,
        "Generate content",
        "Template content",
    )
}

File Organization

helpers/
├── README.md              # This file
├── sampling.go            # Sampling helpers
├── sampling_test.go       # Sampling tests
├── elicitation.go         # Elicitation helpers
├── elicitation_test.go    # Elicitation tests
├── validation.go          # Validation helpers
├── errors.go              # Error handling
└── errors_test.go         # Error tests

Testing

Helpers are fully tested with unit tests:

cd statemachine/helpers
go test -v

Run specific tests:

go test -v -run TestSampleWithFallback

With coverage:

go test -cover

See Also

Contributing

When adding new helpers:

  1. Follow existing patterns
  2. Add comprehensive tests
  3. Document with examples
  4. Update this README
  5. Add to reference docs

Helper Checklist:

  • Automatic capability checking
  • Graceful fallback handling
  • Clear error messages
  • Comprehensive tests
  • Usage examples
  • Documentation

Documentation

Overview

Package helpers provides utility functions for state machine operations.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrSamplingUnavailable     = errors.New("sampling capability not available")
	ErrElicitationUnavailable  = errors.New("elicitation capability not available")
	ErrUserDeclinedAction      = errors.New("user declined action")
	ErrValidationFailed        = errors.New("validation failed")
	ErrJSONParseFailed         = errors.New("failed to parse JSON from response")
	ErrCapabilityCheckFailed   = errors.New("capability check failed")
	ErrFallbackExecutionFailed = errors.New("fallback execution failed")
	ErrUnsupportedFallbackType = errors.New("unsupported fallback type")
)

Sentinel errors for common failure modes.

Functions

func BuildSystemPrompt

func BuildSystemPrompt(tmplStr string, contextData map[string]any) (string, error)

BuildSystemPrompt builds a system prompt from a template and context data.

func ExtractContextFromState

func ExtractContextFromState(smCtx *statemachine.Context, keys []string) map[string]any

ExtractContextFromState extracts multiple values from state machine context.

func FormatContextChunks

func FormatContextChunks(chunks []ContextChunk) string

FormatContextChunks formats context chunks into a readable string.

func FormatContextForPrompt

func FormatContextForPrompt(contextData map[string]any) string

FormatContextForPrompt formats context data as a readable string for inclusion in prompts.

func HandleGracefulDegradation

func HandleGracefulDegradation(err error, fallback any) (any, error)

HandleGracefulDegradation handles an error with graceful fallback If the error is capability-related or user decline, returns the fallback value Otherwise, returns the error.

func IsCapabilityMissing

func IsCapabilityMissing(err error) bool

IsCapabilityMissing checks if an error is due to missing sampling/elicitation capability.

func IsElicitationError

func IsElicitationError(err error) bool

IsElicitationError checks if an error is an elicitation error.

func IsSamplingError

func IsSamplingError(err error) bool

IsSamplingError checks if an error is a sampling error.

func IsUserDeclined

func IsUserDeclined(err error) bool

IsUserDeclined checks if an error represents a user declining an action.

func LogCapabilityCheck

func LogCapabilityCheck(ctx context.Context, capability string, available bool)

LogCapabilityCheck logs capability checking results.

func LogElicitationAttempt

func LogElicitationAttempt(ctx context.Context, operation string, mode string, message string)

LogElicitationAttempt logs an elicitation request attempt.

func LogElicitationResult

func LogElicitationResult(ctx context.Context, operation string, action string, declined bool, err error)

LogElicitationResult logs an elicitation response.

func LogGracefulDegradation

func LogGracefulDegradation(ctx context.Context, operation string, reason string, fallbackType string)

LogGracefulDegradation logs when a fallback is used.

func LogSamplingAttempt

func LogSamplingAttempt(ctx context.Context, operation string, prompt string, opts map[string]any)

LogSamplingAttempt logs a sampling request attempt.

func LogSamplingResult

func LogSamplingResult(ctx context.Context, operation string, source string, resultLength int, err error)

LogSamplingResult logs a sampling response.

func MergeContextData

func MergeContextData(contexts ...map[string]any) map[string]any

MergeContextData merges multiple context data maps, with later maps taking precedence.

func TruncateToTokenLimit

func TruncateToTokenLimit(content string, maxTokens int) string

TruncateToTokenLimit truncates content to approximately fit within a token limit Uses rough heuristic: 1 token ≈ 4 characters.

func WrapElicitationError

func WrapElicitationError(operation string, err error, context map[string]any) error

WrapElicitationError wraps an error with elicitation context.

func WrapSamplingError

func WrapSamplingError(operation string, err error, context map[string]any) error

WrapSamplingError wraps an error with sampling context.

Types

type ContextChunk

type ContextChunk struct {
	Title   string
	Content string
}

ContextChunk represents a piece of context information.

type ElicitationError

type ElicitationError struct {
	Operation string
	Err       error
	Context   map[string]any
}

ElicitationError wraps elicitation-related errors with additional context.

func (*ElicitationError) Error

func (e *ElicitationError) Error() string

func (*ElicitationError) Unwrap

func (e *ElicitationError) Unwrap() error

type SamplingError

type SamplingError struct {
	Operation string
	Err       error
	Context   map[string]any
}

SamplingError wraps sampling-related errors with additional context.

func (*SamplingError) Error

func (e *SamplingError) Error() string

func (*SamplingError) Unwrap

func (e *SamplingError) Unwrap() error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL