handlers

package
v0.0.7 Latest Latest
Warning

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

Go to latest
Published: Jan 31, 2025 License: Apache-2.0 Imports: 5 Imported by: 0

README

Table of Contents

  1. Introduction
  2. The First Handler
  3. ThreadFlow
  4. The Switch Handler: Conditional Routing for Conversation Threads

First Handler

The First handler is designed to execute multiple handlers in parallel and return the result of the first successful handler. This is particularly useful in scenarios where you have multiple ways to achieve the same goal, and you want to proceed as soon as one of them succeeds. If all handlers fail, it aggregates the errors and returns them.


Analogy: The Race to Success

Think of the First handler as a race where multiple runners (handlers) are competing to reach the finish line (success). The moment one runner crosses the line, the race is over, and the other runners stop. If no runner finishes successfully, the race is considered a failure, and you get a report of what went wrong with each runner.

This pattern is similar to:

  • Load Balancing with Fallbacks: Trying multiple servers to handle a request and using the first successful response.
  • Redundant Validation: Validating data using multiple methods and accepting the first valid result.
  • Competing Algorithms: Running different algorithms in parallel and using the first one that produces a valid result.

Key Features:
  1. Concurrent Execution: All handlers run in parallel, maximizing efficiency.
  2. First Success Wins: As soon as one handler succeeds, the rest are canceled.
  3. Error Aggregation: If all handlers fail, all errors are collected and returned.
  4. Graceful Fallback: If no handlers are provided, the input is passed through to the next handler in the chain.

Example Use Case

Imagine you’re building a conversational AI system, and you want to validate user input using multiple methods. You might have:

  • A rule-based validator (fast but less accurate).
  • A machine learning validator (slower but more accurate).
  • A fallback validator (basic checks).

You want to use the first validator that succeeds, and if all fail, return an error.

first := handlers.First("validation",
    ruleBasedValidator,
    mlValidator,
    fallbackValidator,
)

result, err := first.HandleThread(inputContext, nil)
if err != nil {
    log.Fatalf("Validation failed: %v", err)
}
fmt.Println("Validated input:", result)

How It Works
  1. Setup: The First handler is initialized with a name and a list of handlers to execute.
  2. Execution:
    • All handlers are run concurrently using goroutines.
    • The first handler to succeed sends its result to a channel.
    • Once a result is received, the context is canceled to stop the remaining handlers.
  3. Result Handling:
    • If a handler succeeds, its result is returned.
    • If all handlers fail, an aggregated error is returned.
  4. Fallback: If no handlers are provided, the input is passed through to the next handler in the chain.

Code Breakdown
Initialization
first := handlers.First("validation",
    validateA,
    validateB,
    validateC,
)
  • "validation": A name for the handler group (useful for debugging and logging).
  • validateA, validateB, validateC: Handlers to execute in parallel.
Execution
result, err := first.HandleThread(inputContext, nil)
  • inputContext: The initial conversation thread or data to process.
  • nil: The next handler in the chain (optional).
Error Handling
if err != nil {
    log.Fatalf("Validation failed: %v", err)
}
  • If all handlers fail, the error contains details about each failure.

Why Use the First Handler?
  1. Efficiency: By running handlers in parallel, you reduce the overall processing time.
  2. Flexibility: You can define multiple strategies for handling a task and let the system choose the best one.
  3. Robustness: If one strategy fails, others can still succeed, improving reliability.
  4. Error Insights: Aggregated errors provide a comprehensive view of what went wrong.

Real-World Scenarios
  1. Validation: Use multiple validation methods and accept the first valid result.
  2. Fallback Mechanisms: Try multiple ways to generate a response (e.g., LLM, rule-based, cached responses).
  3. Redundant Systems: Attempt to fetch data from multiple sources and use the first successful response.
  4. Competing Algorithms: Run different algorithms in parallel and use the first valid output.

Example: Fallback Response Generation
flow := NewThreadFlow("conversation")
flow.Use(NewLogging("audit"))

flow.Handle(handlers.First("response-generation",
    generateLLMResponse,
    generateRuleBasedResponse,
    generateCachedResponse,
))

result, err := flow.HandleThread(inputContext, nil)
if err != nil {
    log.Fatalf("Failed to generate response: %v", err)
}
fmt.Println("Generated response:", result)

In this example:

  • The system first tries to generate a response using an LLM.
  • If that fails, it falls back to a rule-based response.
  • If that also fails, it uses a cached response.
  • The first successful response is returned.

Summary

The First handler is a powerful tool for building resilient and efficient systems. By running multiple strategies in parallel and using the first successful result, you can improve both performance and reliability. Whether you're validating input, generating responses, or fetching data, the First handler ensures that your system can adapt and succeed even when individual components fail.

<a href="#>

ThreadFlow

A ThreadFlow is similar to an HTTP router like Chi or Flow, but instead of routing HTTP requests, it manages the flow of conversation threads through different processing stages. Just as an HTTP router directs requests through middleware and handlers, ThreadFlow guides conversation threads through a series of processing steps.

Consider this HTTP router example:

router := chi.NewRouter()
router.Use(middleware.Logger)        // Global middleware
router.Use(middleware.Recoverer)     // Another global middleware

router.Group(func(r chi.Router) {
    r.Use(middleware.Timeout(5 * time.Second))  // Group-specific middleware
    r.Get("/api/users", handleUsers)           // Route handler
    r.Get("/api/posts", handlePosts)           // Another handler
})

ThreadFlow follows a similar pattern but for conversation processing:

flow := NewThreadFlow("conversation")
flow.Use(NewLogging("audit"))          // Global middleware logs all operations

flow.Handle(validateInput)             // Base handler for input validation

flow.Group(func(f *ThreadFlow) {
    f.Use(NewTimeout("timeout", 5))    // Group-specific timeout
    f.Use(NewRetry("retry", 3))        // Group-specific retry logic
    f.Handle(generateResponse)         // Handler for LLM response
    f.Handle(validateOutput)           // Handler for output validation
})

The key difference is that while HTTP routers process web requests, ThreadFlow processes conversation threads. Each handler in ThreadFlow can modify the conversation state, add messages, or update metadata, while middleware can add cross-cutting concerns like logging, timeouts, or retries.

Just as HTTP middleware can inspect or modify a request before it reaches the handler, ThreadFlow middleware can examine or transform the conversation thread before it reaches each processing stage. This makes it easy to add capabilities like:

  1. Logging: Track each stage of conversation processing.
  2. Timeouts: Set limits for how long LLM operations can take.
  3. Retries: Automatically retry failed operations.
  4. Validation: Ensure conversation content meets specific criteria.
  5. State Management: Maintain and update conversation context.

The ThreadFlow pattern is particularly valuable when building complex conversational applications that require multiple processing steps, each with different requirements for reliability, timing, and validation.

This architecture keeps your conversation processing logic organized and maintainable, just as a well-structured HTTP router keeps your web application organized. The ability to group handlers and apply specific middleware to those groups gives you fine-grained control over how different parts of your conversation processing pipeline behave.

A ThreadFlow is essentially a manager for handling a sequence of tasks (called "handlers") and applying additional processing steps (called "middleware") to those tasks. Think of it as a way to organize and control how a series of operations are executed, with the ability to add extra behavior (like logging, retries, or timeouts) around those operations.

Key Concepts:
  1. Handlers: These are the main tasks or operations you want to perform. For example, validating data, processing content, or saving something to a database.

  2. Middleware: These are additional steps that wrap around your handlers. They can modify the input, output, or behavior of the handlers. For example, logging the start and end of a task, retrying a task if it fails, or setting a timeout for how long a task can run.

  3. ThreadFlow: This is the top-level manager that organizes your handlers and middleware. It allows you to:

    • Add global middleware that applies to all handlers.
    • Group handlers and apply specific middleware only to that group.
How It Works:
  • Global Middleware: You can add middleware that will apply to all handlers in the ThreadFlow. For example, you might want to log every task that runs.

  • Grouped Middleware: You can create groups of handlers and apply middleware only to those groups. This is useful when you want certain middleware (like retries or timeouts) to apply only to specific tasks.

Example Breakdown:
// Create a new ThreadFlow
flow := NewThreadFlow("example")

// Add global middleware (applies to all handlers)
flow.Use(NewLogging("audit"))

// Add base handlers
flow.Handle(seq)

// Create a group with specific middleware
flow.Group(func(f *ThreadFlow) {
    f.Use(NewRetry("retry", 3))    // Retry up to 3 times
    f.Use(NewTimeout("timeout", 5)) // Timeout after 5 seconds
    f.Handle(NewContentProcessor("content")) // Add a handler
    f.Handle(NewValidator("validate"))       // Add another handler
})

// Execute the flow
result, err := flow.HandleThread(initialContext, nil)
if err != nil {
    log.Fatalf("Error in flow: %v", err)
}
fmt.Println("Result:", result.Messages().Last().Content)
What Happens Here:
  1. Global Middleware: The NewLogging("audit") middleware is added globally, so it will log every handler that runs.

  2. Base Handlers: The seq handler is added to the flow. This could be a sequence of tasks like validation and processing.

  3. Grouped Handlers: Inside the Group function, two middleware (NewRetry and NewTimeout) are added, and two handlers (NewContentProcessor and NewValidator) are added. These middleware will only apply to the handlers within this group.

  4. Execution: When flow.HandleThread is called, the flow executes all handlers in sequence, applying the appropriate middleware to each one.

Why Use ThreadFlow?
  • Flexibility: You can easily add or remove middleware for specific tasks without affecting others.
  • Organization: It helps keep your code clean and organized by grouping related tasks and their middleware together.
  • Reusability: Middleware can be reused across different groups or flows, reducing code duplication.

In summary, a ThreadFlow is a powerful tool for managing and orchestrating a series of tasks with the ability to add and control additional behavior around those tasks. It’s like having a conductor for your orchestra of handlers and middleware, ensuring everything runs smoothly and efficiently.


The Switch Handler: Conditional Routing for Conversation Threads

The Switch handler is a powerful tool for building conditional logic into your conversation processing pipelines. It allows you to route threads to different handlers based on dynamic conditions, such as metadata, LLM evaluations, or custom logic. This is particularly useful when you need to handle different types of user inputs or tasks within a single conversational flow.


Why Use the Switch Handler?

The Switch handler is ideal for scenarios where you need to:

  1. Route Messages Based on Metadata: For example, directing a message to a specific handler if it contains a certain metadata key or value.
  2. Evaluate Conditions with LLMs: Use an LLM to determine which handler should process the message (e.g., "Is this a math question?").
  3. Implement Fallback Logic: Provide a default handler when no conditions match.
  4. Organize Complex Workflows: Break down large conversational flows into smaller, manageable handlers.

Switch vs. LLM Tool Usage

While LLM tool usage (e.g., function calling) can handle conditional logic, the Switch handler offers several advantages:

  • Explicit Control: Conditions are explicitly defined, making the logic easier to understand and debug.
  • Efficiency: Avoids unnecessary LLM calls by evaluating conditions directly (e.g., metadata checks).
  • Flexibility: Supports custom conditions (e.g., Lua scripts, metadata checks) alongside LLM-based evaluations.
  • Performance: Reduces latency by minimizing reliance on LLMs for simple routing decisions.

Use Switch when you need clear, structured routing logic. Use LLM tool usage when the decision-making process is complex and requires natural language understanding.


How It Works

The Switch handler evaluates a list of conditions in order. When a condition evaluates to true, the corresponding handler is executed. If no conditions match, a default handler (if provided) is used.

Key Components
  1. SwitchCondition: An interface for defining conditions. Built-in implementations include:
    • MetadataEquals: Checks if a metadata key matches a specific value.
    • LLMCondition: Uses an LLM to evaluate a condition based on a prompt.
  2. SwitchCase: Pairs a condition with a handler.
  3. Default Handler: Executed when no conditions match.

Example: Routing Messages Based on Metadata and LLM Evaluation
---
title: Switch Handler Workflow
config:
 look: handDrawn
---
flowchart TD
A[Initial Message] --> B{Switch}
B -->|Condition 1: MetadataEquals| C[Handler 1: Math Tool]
B -->|Condition 2: LLMCondition| D[Handler 2: Question Handler]
B -->|No Match| E[Default Handler]
func main() {
	llm, _ := openai.NewProvider()

	// Define conditions and handlers
	mathCondition := handlers.MetadataEquals{Key: "type", Value: "math"}
	mathHandler := tools.NewCalculator()

	questionCondition := handlers.LLMCondition{
		Generator: llm,
		Prompt:    "Is this message a general knowledge question?",
	}
	questionHandler := handlers.NewQuestionHandler(llm)

	defaultHandler := handlers.NewDefaultHandler(llm)

	// Create the Switch handler
	sw := handlers.Switch("router",
		defaultHandler,
		handlers.SwitchCase{Condition: mathCondition, Handler: mathHandler},
		handlers.SwitchCase{Condition: questionCondition, Handler: questionHandler},
	)

	// Initial thread with metadata
	thread := minds.NewThreadContext(context.Background()).
		WithMessages(minds.Messages{
			{Role: minds.RoleUser, Content: "What is 7 * 12 + 5?"},
		}).
		WithMetadata(map[string]interface{}{
			"type": "math",
		})

	// Process the thread
	result, err := sw.HandleThread(thread, nil)
	if err != nil {
		log.Fatalf("Error processing thread: %v", err)
	}
	fmt.Println("Response:", result.Messages().Last().Content)
}

Built-In Conditions
1. MetadataEquals

Checks if a metadata key matches a specific value.

condition := handlers.MetadataEquals{Key: "type", Value: "math"}
2. LLMCondition

Uses an LLM to evaluate a condition based on a prompt.

condition := handlers.LLMCondition{
    Generator: llm,
    Prompt:    "Is this message a general knowledge question?",
}

Custom Conditions

You can implement the SwitchCondition interface to create custom conditions. For example, a Lua-based condition:

type LuaCondition struct {
    Script string
}

func (l LuaCondition) Evaluate(tc minds.ThreadContext) (bool, error) {
    // Execute Lua script and return result
}

Why Use the Switch Handler?
  1. Modularity: Break down complex workflows into smaller, reusable handlers.
  2. Flexibility: Combine metadata checks, LLM evaluations, and custom logic.
  3. Efficiency: Avoid unnecessary LLM calls for simple routing decisions.
  4. Debugging: Explicit conditions make it easier to trace and debug issues.

Real-World Use Cases
  1. Intent Routing: Direct messages to specific handlers based on user intent (e.g., "Is this a question or a command?").
  2. Fallback Mechanisms: Provide a default response when no conditions match.
  3. Dynamic Workflows: Adapt the conversation flow based on metadata or LLM evaluations.
  4. Tool Integration: Route messages to external tools (e.g., calculators, APIs) based on content.

Example: Intent-Based Routing
sw := handlers.Switch("intent-router",
    handlers.NewDefaultHandler(llm), // Fallback handler
    handlers.SwitchCase{
        Condition: handlers.LLMCondition{
            Generator: llm,
            Prompt:    "Does this message contain a mathematical calculation?",
        },
        Handler: tools.NewCalculator(),
    },
    handlers.SwitchCase{
        Condition: handlers.MetadataEquals{
            Key:   "type",
            Value: "question",
        },
        Handler: handlers.NewQuestionHandler(llm),
    },
)

Summary

The Switch handler is a versatile tool for building conditional logic into your conversational AI pipelines. It provides explicit, efficient, and flexible routing capabilities, making it easier to manage complex workflows. Whether you're routing based on metadata, LLM evaluations, or custom logic, the Switch handler helps you keep your code organized and maintainable.


Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DefaultAggregator added in v0.0.5

func DefaultAggregator(results []HandlerResult) (minds.ThreadContext, error)

DefaultAggregator combines results by merging contexts in order. Merging is done by starting with the first successful context and merging all subsequent successful contexts into it. If no successful contexts are found, an error is returned.

Metadata merging is done with the minds.KeepNew strategy, which overwrites existing values with new values. Messages are appended in order.

Parameters:

  • results: List of handler results to aggregate

Returns:

  • A single thread context that combines all successful results

func NewSequence added in v0.0.6

func NewSequence(name string, handlers ...minds.ThreadHandler) *sequence

NewSequence creates a new Sequence handler with the given name and handlers. The sequence executes handlers in order, stopping on the first error.

Example:

validate := NewValidationHandler()
process := NewProcessingHandler()

// Create a sequence with retry middleware
seq := NewSequence("main", validate, process)
seq.Use(RetryMiddleware())

// Or create a variation with different middleware
timeoutSeq := seq.With(TimeoutMiddleware())

result, err := seq.HandleThread(tc, nil)

func Noop added in v0.0.6

func Noop() *noop

Noop creates a new no-operation handler that simply returns the thread context unchanged. It's useful as a default handler in Switch or If handlers when no action is desired for the default case.

Example:

// Use as default in Switch
sw := Switch("type-switch", Noop(), cases...)

// Use as default in If
ih := If("type-check", condition, trueHandler, Noop())

func Summarize added in v0.0.4

func Summarize(provider minds.ContentGenerator, systemMsg string) *summarize

Summarize creates a handler that maintains a running summary of thread messages.

The handler prompts an LLM to generate a concise summary of all messages in the thread, focusing on key information. The summary is appended to the system message in XML tags and persists across handler invocations.

Parameters:

  • provider: LLM content generator for creating summaries.
  • systemMsg: Initial system message to prepend to summaries.

Returns:

  • A handler that maintains thread summaries via LLM generation.

Note: The original thread context is not modified; a new context with the updated system message is created.

func WithDescription

func WithDescription(description string) func(*HandlerOption)

func WithName

func WithName(name string) func(*HandlerOption)

func WithPrompt

func WithPrompt(prompt minds.Prompt) func(*HandlerOption)

Types

type BoolResp added in v0.0.6

type BoolResp struct {
	Bool bool `json:"bool"`
}

BoolResp represents the expected JSON response format from the LLM.

type ConditionFunc added in v0.0.7

type ConditionFunc func(tc minds.ThreadContext) (bool, error)

func (ConditionFunc) Evaluate added in v0.0.7

func (f ConditionFunc) Evaluate(tc minds.ThreadContext) (bool, error)

type First added in v0.0.3

type First struct {
	// contains filtered or unexported fields
}

First represents a handler that executes multiple handlers in parallel and returns on first success. Each handler runs in its own goroutine, and execution of remaining handlers is canceled once a successful result is obtained.

func NewFirst added in v0.0.6

func NewFirst(name string, handlers ...minds.ThreadHandler) *First

NewFirst creates a handler that runs multiple handlers in parallel and returns on first success. If all handlers fail, an error containing all handler errors is returned. If no handlers are provided, the thread context is passed to the next handler unmodified.

Parameters:

  • name: Identifier for this parallel handler group
  • handlers: Variadic list of handlers to execute in parallel

Returns:

  • A First handler configured with the provided handlers

Example:

first := handlers.NewFirst("validation",
    validateA,
    validateB,
    validateC,
)

func (*First) HandleThread added in v0.0.6

func (f *First) HandleThread(tc minds.ThreadContext, next minds.ThreadHandler) (minds.ThreadContext, error)

HandleThread executes the First handler by running child handlers in parallel and returning on first success.

func (*First) String added in v0.0.6

func (f *First) String() string

String returns a string representation of the First handler.

func (*First) Use added in v0.0.6

func (f *First) Use(middleware ...minds.Middleware)

Use applies middleware to the First handler, wrapping its child handlers.

func (*First) With added in v0.0.6

func (f *First) With(middleware ...minds.Middleware) minds.ThreadHandler

With returns a new First handler with the provided middleware applied.

type For added in v0.0.3

type For struct {
	// contains filtered or unexported fields
}

For represents a handler that repeats processing based on iterations and conditions. It supports both fixed iteration counts and conditional execution through a continuation function.

func NewFor added in v0.0.6

func NewFor(name string, iterations int, handler minds.ThreadHandler, fn ForConditionFn) *For

NewFor creates a handler that repeats processing based on iterations and conditions.

A continuation function can optionally control the loop based on the ThreadContext and iteration count. The handler runs either until the iteration count is reached, the continuation function returns false, or infinitely if iterations is 0.

Parameters:

  • name: Identifier for this loop handler
  • iterations: Number of iterations (0 for infinite)
  • handler: The handler to repeat
  • fn: Optional function controlling loop continuation

Returns:

  • A handler that implements controlled repetition of processing

Example:

loop := NewFor("validation", 3, validateHandler, func(tc ThreadContext, i int) bool {
    return tc.ShouldContinue()
})

func (*For) HandleThread added in v0.0.6

func (f *For) HandleThread(tc minds.ThreadContext, next minds.ThreadHandler) (minds.ThreadContext, error)

HandleThread implements the ThreadHandler interface

func (*For) String added in v0.0.6

func (f *For) String() string

String returns a string representation of the For handler

func (*For) Use added in v0.0.6

func (f *For) Use(middleware ...minds.Middleware)

Use adds middleware to the handler

func (*For) With added in v0.0.6

func (f *For) With(middleware ...minds.Middleware) minds.ThreadHandler

With returns a new handler with the provided middleware

type ForConditionFn added in v0.0.3

type ForConditionFn func(tc minds.ThreadContext, iteration int) bool

ForConditionFn defines a function type that controls loop continuation based on the current thread context and iteration count.

type HandlerOption

type HandlerOption struct {
	// contains filtered or unexported fields
}

type HandlerResult added in v0.0.5

type HandlerResult struct {
	Handler minds.ThreadHandler
	Context minds.ThreadContext
	Error   error
}

HandlerResult represents the result of executing a single handler

type If added in v0.0.6

type If struct {
	// contains filtered or unexported fields
}

If represents a conditional handler that executes one of two handlers based on a condition evaluation. It's a simplified version of Switch for binary conditions.

func NewIf added in v0.0.6

func NewIf(name string, condition SwitchCondition, trueHandler minds.ThreadHandler, fallbackHandler minds.ThreadHandler) *If

NewIf creates a new If handler that executes trueHandler when the condition evaluates to true, otherwise executes fallbackHandler if provided.

Parameters:

  • name: Identifier for this conditional handler
  • condition: The condition to evaluate
  • trueHandler: Handler to execute when condition is true
  • fallbackHandler: Optional handler to execute when condition is false

Returns:

  • A handler that implements conditional execution based on the provided condition

Example:

metadata := MetadataEquals{Key: "type", Value: "question"}
questionHandler := SomeQuestionHandler()
defaultHandler := DefaultHandler()

ih := NewIf("type-check", metadata, questionHandler, defaultHandler)

func (*If) HandleThread added in v0.0.6

func (h *If) HandleThread(tc minds.ThreadContext, next minds.ThreadHandler) (minds.ThreadContext, error)

HandleThread implements the ThreadHandler interface

func (*If) String added in v0.0.6

func (h *If) String() string

String returns a string representation of the If handler

func (*If) Use added in v0.0.6

func (h *If) Use(middleware ...minds.Middleware)

Use adds middleware to the handler

func (*If) With added in v0.0.6

func (h *If) With(middleware ...minds.Middleware) minds.ThreadHandler

With returns a new handler with the provided middleware

type LLMCondition added in v0.0.4

type LLMCondition struct {
	Generator minds.ContentGenerator
	Prompt    string
}

LLMCondition evaluates a condition using an LLM response.

func (LLMCondition) Evaluate added in v0.0.4

func (l LLMCondition) Evaluate(tc minds.ThreadContext) (bool, error)

Evaluate sends a prompt to the LLM and expects a boolean response.

type MetadataEquals added in v0.0.4

type MetadataEquals struct {
	Key   string
	Value interface{}
}

MetadataEquals checks if a metadata key equals a specific value.

func (MetadataEquals) Evaluate added in v0.0.4

func (m MetadataEquals) Evaluate(tc minds.ThreadContext) (bool, error)

Evaluate checks if the metadata value for the specified key equals the target value.

type Must

type Must struct {
	// contains filtered or unexported fields
}

Must represents a handler that runs multiple handlers in parallel requiring all to succeed

func NewMust added in v0.0.6

func NewMust(name string, agg ResultAggregator, handlers ...minds.ThreadHandler) *Must

NewMust creates a handler that runs multiple handlers in parallel with all-success semantics.

All handlers execute concurrently and must complete successfully. If any handler fails, remaining handlers are canceled and the first error encountered is returned. The timing of which error is returned is nondeterministic due to parallel execution.

Parameters:

  • name: Identifier for this parallel handler group
  • agg: ResultAggregator function to combine successful results
  • handlers: Variadic list of handlers that must all succeed

Returns:

  • A handler that implements parallel execution with all-success semantics

Example:

must := handlers.NewMust("validation",
    handlers.DefaultAggregator,
    validateA,
    validateB,
    validateC,
)

func (*Must) HandleThread added in v0.0.6

func (m *Must) HandleThread(tc minds.ThreadContext, next minds.ThreadHandler) (minds.ThreadContext, error)

HandleThread executes all child handlers in parallel, requiring all to succeed.

func (*Must) String added in v0.0.6

func (m *Must) String() string

String returns a string representation of the Must handler.

func (*Must) Use added in v0.0.6

func (m *Must) Use(middleware ...minds.Middleware)

Use applies middleware to the Must handler, wrapping its child handlers.

func (*Must) With added in v0.0.6

func (m *Must) With(middleware ...minds.Middleware) *Must

With returns a new Must handler with additional middleware, preserving existing state.

type Policy added in v0.0.3

type Policy struct {
	// contains filtered or unexported fields
}

Policy performs policy validation on thread content using a content generator (LLM).

func NewPolicy added in v0.0.6

func NewPolicy(llm minds.ContentGenerator, name, systemPrompt string, resultFn PolicyResultFunc) *Policy

NewPolicy creates a new policy validator handler.

The handler uses an LLM to validate thread content against a given policy. A system prompt is used to guide the validation process, and the result is processed by the optional result function. If the result function is nil, the handler defaults to checking the `Valid` field of the validation result.

Parameters:

  • llm: A content generator for generating validation responses.
  • name: The name of the policy validator.
  • systemPrompt: A prompt describing the policy validation rules.
  • resultFn: (Optional) Function to process validation results.

Returns:

  • A thread handler that validates thread content against a policy.

func (*Policy) HandleThread added in v0.0.6

func (h *Policy) HandleThread(tc minds.ThreadContext, next minds.ThreadHandler) (minds.ThreadContext, error)

validate implements the core policy validation logic

func (*Policy) String added in v0.0.6

func (h *Policy) String() string

String returns a string representation of the policy validator.

type PolicyResult added in v0.0.3

type PolicyResult struct {
	Valid     bool   `json:"valid" description:"Whether the content passes policy validation"`
	Reason    string `json:"reason" description:"Explanation for the validation result"`
	Violation string `json:"violation" description:"Description of the specific violation if any"`
}

PolicyResult represents the outcome of a policy validation.

type PolicyResultFunc

type PolicyResultFunc func(ctx context.Context, tc minds.ThreadContext, res PolicyResult) error

PolicyResultFunc defines a function to handle the result of a policy validation. It takes a context, thread context, and validation result, and returns an error if the validation fails or cannot be processed.

type Range added in v0.0.2

type Range struct {
	// contains filtered or unexported fields
}

Range executes a handler multiple times with different values. For each value in the provided list, the handler executes with the value stored in the thread context's metadata under the key "range_value". The handler supports middleware that will be applied to each iteration.

func NewRange added in v0.0.6

func NewRange(name string, handler minds.ThreadHandler, values ...any) *Range

NewRange creates a handler that processes a thread with a series of values.

Parameters:

  • name: Identifier for this range handler
  • handler: The handler to execute for each value
  • values: Values to iterate over

Returns:

  • A Range handler that processes the thread once for each value

Example:

rng := NewRange("process",
    processHandler,
    "value1", "value2", "value3",
)

func (*Range) HandleThread added in v0.0.6

func (r *Range) HandleThread(tc minds.ThreadContext, next minds.ThreadHandler) (minds.ThreadContext, error)

HandleThread implements the ThreadHandler interface

func (*Range) String added in v0.0.6

func (r *Range) String() string

String returns a string representation of the Range handler

func (*Range) Use added in v0.0.6

func (r *Range) Use(middleware ...minds.Middleware)

Use adds middleware to the handler

func (*Range) With added in v0.0.6

func (r *Range) With(middleware ...minds.Middleware) minds.ThreadHandler

With returns a new handler with the provided middleware

type ResultAggregator added in v0.0.5

type ResultAggregator func([]HandlerResult) (minds.ThreadContext, error)

ResultAggregator defines a function type for combining multiple handler results

type Switch added in v0.0.4

type Switch struct {
	// contains filtered or unexported fields
}

Switch executes the first matching case's handler, or the default handler if no cases match.

func NewSwitch added in v0.0.6

func NewSwitch(name string, defaultHandler minds.ThreadHandler, cases ...SwitchCase) *Switch

NewSwitch creates a new Switch handler that executes the first matching case's handler. If no cases match, it executes the default handler. The name parameter is used for debugging and logging purposes.

The SwitchCase struct pairs a `SwitchCondition` interface with a handler. When the condition evaluates to true, the corresponding handler is executed.

Example:

    // The MetadataEquals condition checks if the metadata key "type" equals "question"
	metadata := MetadataEquals{Key: "type", Value: "question"}
	questionHandler := SomeQuestionHandler()
	defaultHandler := DefaultHandler()

sw := Switch("type-switch",

defaultHandler,
SwitchCase{metadata, questionHandler},

)

func (*Switch) HandleThread added in v0.0.6

func (s *Switch) HandleThread(tc minds.ThreadContext, next minds.ThreadHandler) (minds.ThreadContext, error)

HandleThread processes the thread context, executing the first matching case's handler.

func (*Switch) String added in v0.0.6

func (s *Switch) String() string

String returns a string representation of the Switch handler.

func (*Switch) Use added in v0.0.6

func (s *Switch) Use(middleware ...minds.Middleware)

Use applies middleware to the Switch handler.

func (*Switch) With added in v0.0.6

func (s *Switch) With(middleware ...minds.Middleware) *Switch

With returns a new Switch handler with additional middleware, preserving existing state.

type SwitchCase added in v0.0.4

type SwitchCase struct {
	Condition SwitchCondition
	Handler   minds.ThreadHandler
}

SwitchCase pairs a condition with a handler. When the condition evaluates to true, the corresponding handler is executed.

type SwitchCondition added in v0.0.4

type SwitchCondition interface {
	// Evaluate examines the thread context and returns true if the condition is met.
	// It returns an error if the evaluation fails.
	Evaluate(tc minds.ThreadContext) (bool, error)
}

SwitchCondition represents a condition that can be evaluated against a thread context. Implementations of this interface are used by the Switch handler to determine which case should be executed.

Jump to

Keyboard shortcuts

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