This document describes the architecture and design patterns used in AI-Gents.
- Overview
- Modular Library System
- Provider Plugin System
- Command Structure
- Data Flow
- Error Handling
- Performance Optimizations
- Security Model
AI-Gents follows a modular, plugin-based architecture designed for maintainability, extensibility, and performance. The codebase is organized into distinct layers:
┌─────────────────────────────────────────────────────────────┐
│ CLI Commands │
│ (ai, ask, chat, agent, race) │
├─────────────────────────────────────────────────────────────┤
│ parseArger Parsing │
│ (Argument parsing, validation, help generation) │
├─────────────────────────────────────────────────────────────┤
│ Core Libraries │
│ (core, validation, security, api, parallel, errors) │
├─────────────────────────────────────────────────────────────┤
│ Provider Plugins │
│ (openai, openrouter, anthropic, ollama, etc.) │
├─────────────────────────────────────────────────────────────┤
│ External Services │
│ (LLM APIs, Local models) │
└─────────────────────────────────────────────────────────────┘
All shared functionality is organized into libraries in src/bash/lib/:
Purpose: Common utilities, caching, and lazy loading
Key Functions:
log()- Logging with levels (-3 to 3)cache_set/get/has/clear()- Simple key-value cachinglazy_load()- TTL-based lazy loading wrapperget_agent_file()- Agent file path resolutionparse_command()- Safe command parsing
Usage:
source "$_SCRIPT_DIR/lib/core"
# Caching
cache_set "my_key" "my_value"
value=$(cache_get "my_key")
# Lazy loading with 5min TTL
result=$(lazy_load "expensive_op" my_function 300)Purpose: Input validation for LLM parameters
Key Functions:
validate_temperature()- Range 0-2validate_top_p()- Range 0-1validate_max_tokens()- Positive integersvalidate_frequency_penalty()- Range -2 to 2validate_seed()- Non-negative integersvalidate_provider()- Against available plugins
Usage:
source "$_SCRIPT_DIR/lib/validation"
if ! validate_temperature "$temp"; then
die "Invalid temperature" $E_VALIDATION_ERROR
fi
# Validate all LLM parameters
validate_all_llm_params || die "Validation failed"Purpose: Command blacklist and credential handling
Key Functions:
load_command_blacklist()- Load from config fileis_command_blacklisted()- Check command safetyfilter_command()- Filter through blacklistextract_prompt_commands()- Extract commands from promptsget_provider_credential()- Read API keys
Usage:
source "$_SCRIPT_DIR/lib/security"
# Initialize (empty by default)
load_command_blacklist
# Check if command is safe
if is_command_blacklisted "$cmd"; then
log "Command blocked" -1
fiPurpose: HTTP client with connection pooling
Key Functions:
api_init_pool()- Initialize connection poolapi_curl_cmd()- Build curl command with poolingapi_request()- Make request with retry logicapi_request_stream()- Streaming requestapi_request_stream_optimized()- 60fps batching
Usage:
source "$_SCRIPT_DIR/lib/api"
# Initialize pool
api_init_pool
# Make request
response=$(api_request "$endpoint" "$payload_file" "$api_key")
# Stream with optimization
api_request_stream_optimized "$endpoint" "$payload" "$api_key" output_callbackPurpose: Semaphore-based concurrent execution
Key Functions:
parallel_init()- Initialize semaphore systemparallel_acquire/release()- Slot managementparallel_exec()- Execute in backgroundparallel_map()- Process array in parallelparallel_wait()- Wait for completion
Usage:
source "$_SCRIPT_DIR/lib/parallel"
# Initialize with max 4 concurrent jobs
parallel_init 4
# Execute in parallel
parallel_exec my_callback "$result_file" "${args[@]}"
# Wait for all
parallel_waitPurpose: Standardized error handling
Key Functions:
die()- Exit with message and codetry()- Try/catch wrapperrequire_cmd()- Check dependenciesassert()- Condition validation
Exit Codes:
E_SUCCESS=0
E_GENERIC=1
E_INVALID_ARGS=2
E_MISSING_DEPENDENCY=3
E_CONFIG_ERROR=4
E_NETWORK_ERROR=5
E_API_ERROR=6
E_VALIDATION_ERROR=7
E_SECURITY_ERROR=8
E_FILE_ERROR=9
E_INTERRUPTED=130Providers are dynamically loaded plugins that standardize interaction with different LLM services.
All providers must implement:
# Metadata
PROVIDER_NAME=""
PROVIDER_URL=""
PROVIDER_DEFAULT_MODEL=""
PROVIDER_API_KEY_ENV=""
# Core functions
provider_get_name()
provider_get_url()
provider_get_default_model()
provider_get_credential_env()
provider_get_chat_endpoint()
provider_build_payload()
provider_parse_stream_chunk()
provider_parse_response()
provider_get_headers()Dynamic discovery and loading:
provider_discover() # Scan providers/ directory
provider_is_registered() # Check if provider exists
provider_load() # Source provider file
provider_list_available()# Get all provider names- Create file in
src/bash/lib/providers/myprovider - Source base interface
- Override required functions
- Set metadata variables
- Make executable
Example:
#!/bin/bash
source "${_SCRIPT_DIR}/lib/providers/_base"
PROVIDER_NAME="myprovider"
PROVIDER_URL="https://api.myprovider.com/v1"
PROVIDER_DEFAULT_MODEL="model-1"
PROVIDER_API_KEY_ENV="AI_MYPROVIDER_API_KEY"
provider_get_url() {
echo "${AI_MYPROVIDER_HOST:-https://api.myprovider.com}/v1"
}
# Override other functions as neededDispatches to subcommands based on arguments:
ai ask "question" # -> src/bash/ask
ai chat # -> src/bash/chat
ai agent create ... # -> src/bash/agent -> src/bash/_agent/createEach command follows this structure:
#!/bin/bash
# @parseArger-begin
# Generated argument parsing
# @parseArger-end
# Source libraries
source "$_SCRIPT_DIR/lib/validation"
source "$_SCRIPT_DIR/lib/security"
source "$_SCRIPT_DIR/lib/core"
# Initialize
load_command_blacklist
validate_all_llm_params
# Command logic
# ...User Input
↓
Argument Parsing (parseArger)
↓
Parameter Validation
↓
Command Extraction (#!/command;)
↓
Blacklist Filtering
↓
Provider Selection
↓
Payload Building
↓
API Request (with pooling)
↓
Response Parsing
↓
Output
Initialize
↓
rlwrap Input Loop
↓
Build Conversation
↓
Streaming Request
↓
Real-time Output (60fps)
↓
Save to History
↓
Repeat
Parse Models List
↓
Initialize Parallel Pool
↓
Execute All Queries (parallel)
↓
Collect Results
↓
Display Comparison
- Validation at entry: Check all inputs early
- Graceful degradation: Fall back to defaults when possible
- Informative messages: Use standardized exit codes
- Cleanup on error: Always clean up temp files
# Validate inputs early
if ! validate_temperature "$temp"; then
die "Invalid temperature: $temp" $E_VALIDATION_ERROR
fi
# Check dependencies
require_cmds jq yq curl
# Try with fallback
try "risky_operation" handle_error || fallback_operation
# Cleanup
cleanup() {
rm -f "$temp_file"
}
trap cleanup EXIT- Cookie jar for HTTP keep-alive
- Configurable keepalive time (default: 120s, defined in
API_KEEPALIVE_TIME)- Balance between reuse and freshness
- Too low: frequent reconnections
- Too high: stale connections
- HTTP/1.1 for compatibility
- Connection reuse across requests
- Configurable batching interval (default: 16ms for 60fps, defined in
STREAM_BATCH_INTERVAL_NS)- Lower values = smoother but more CPU
- Higher values = less CPU but choppier
- Buffer accumulation
- jq-based parsing (not awk)
- Minimal terminal updates
- TTL-based caching
- Agent configs cached 5 minutes (300s)
- Provider configs persist until manually cleared
- Automatic expiration cleanup
- Semaphore-based concurrency
- Configurable max jobs (default 4)
- Atomic slot acquisition
- Background job management
Located in src/bash/lib/api:
# Connection keep-alive time (seconds)
# Balance between reuse and connection freshness
readonly API_KEEPALIVE_TIME=120
# Default request timeout (seconds)
# Long enough for large LLM responses
readonly API_DEFAULT_TIMEOUT=300
# Maximum retry attempts
# Exponential backoff: 1s, 2s, 4s
readonly API_MAX_RETRIES=3
readonly API_RETRY_DELAY=1
# Streaming batch interval (nanoseconds)
# 16ms = ~60fps output
readonly STREAM_BATCH_INTERVAL_NS=16000000To modify these values, edit the constants in src/bash/lib/api directly.
"Power++ users control their own security"
- Empty blacklist by default
- User configures their own restrictions
- Simple, transparent security model
- No hand-holding, user responsibility
- Input Validation: Reject invalid parameters
- Command Blacklist: Filter dangerous commands
- YAML Safety: Use
yq --arg, no interpolation - Credential Isolation: Separate credential files
Users create ~/.config/ai-gents/command-blacklist:
# Dangerous patterns
rm[[:space:]]+-rf[[:space:]]+/
mkfs\.
dd[[:space:]]+if=.*of=/devThe --safe-mode flag (available on ask and chat commands) disables ALL command execution:
# Block all #!/command; execution
ai ask "Analyze this: #!/ls -la;" --safe-mode
# Output: [SAFE-MODE:BLOCKED]
# Use in chat for extra safety
ai chat --safe-modeUseful when:
- Working with untrusted AI outputs
- Copy-pasting prompts from external sources
- Running in automated/CI environments
- First-time users who want to disable commands entirely
When a user first executes a prompt containing #!/command;, a security warning is displayed:
⚠️ SECURITY WARNING: Command Execution Detected
This prompt contains executable bash commands (#!/command; syntax).
These commands will be executed on your system BEFORE being sent to the AI.
...
Press Enter to acknowledge and continue (this warning will not show again)
Or press Ctrl+C to cancel
The warning is stored in ~/.config/ai-gents/.command-warning-acknowledged and won't show again once acknowledged.
# Dangerous patterns
rm[[:space:]]+-rf[[:space:]]+/
mkfs\.
dd[[:space:]]+if=.*of=/devPatterns are bash regex. Commands matching any pattern are blocked.
- Create file in
src/bash/lib/ - Follow naming conventions
- Document public API
- Add tests
- Create provider file
- Implement base interface
- Make executable
- Test with all commands
- Generate with parseArger
- Source required libraries
- Follow existing patterns
- Add integration tests
- Always validate inputs before using
- Use libraries instead of duplicating code
- Follow naming conventions (see CODING_STANDARDS)
- Handle errors with appropriate exit codes
- Clean up temp files and resources
- Test thoroughly with BATS suite
- Document public APIs and behaviors
- MCP server integration
- More provider plugins
- Advanced caching strategies
- Plugin marketplace
- Configuration UI