| name | cli-sdk-guide |
| description | Guide for implementing Backend.AI client SDK and CLI (Session, BaseFunction, @api_function, Click commands, Pydantic models, FieldSpec, output handlers, APIConfig, testing) |
| version | 1.0.0 |
| dependencies | ["api-guide","test-guide"] |
| tags | ["client-sdk","cli","session","api-function","click","pydantic","output-handler"] |
CLI/SDK Implementation Guide
Guide for implementing Backend.AI client SDK and CLI with session management, API functions, and Click commands.
Purpose
SDK (Software Development Kit):
- Python library for Backend.AI REST API
- Programmatic access for integrations and automation
- Async-first with sync wrapper for CLI compatibility
CLI (Command Line Interface):
- User-facing commands for Backend.AI operations
- Built on top of SDK functions
- Supports console and JSON output modes
When to use:
- SDK: Building integrations, automation scripts, web dashboards
- CLI: Interactive administration, shell scripts, CI/CD pipelines
- Direct API: Only when SDK doesn't support the operation yet
Architecture Overview
Layers: CLI → SDK → REST API → Manager
Flow: CLI commands call SDK functions through Session, which makes HTTP requests to REST API endpoints.
CLI Layer:
ExtendedCommandGroup - Command groups with aliases and interrupt handling
LazyGroup - Lazy loading for faster startup
CLIContext - Shared state (config, output mode)
@pass_ctx_obj - CLIContext injection decorator
FieldSpec - Output field definitions
BaseOutputHandler - Console/JSON formatters
SDK Layer:
BaseFunction - Metaclass for API function classes
@api_function - Decorator for API methods
AsyncSession - Primary async HTTP session
Session - Sync wrapper for CLI
api_session - ContextVar for session context
Data Layer:
- Pydantic models - Request/Response DTOs (future standard)
- attrs classes - Legacy DTOs (current usage in
client/cli/types.py)
- New code should use Pydantic (
common/dto/)
Config Layer:
APIConfig - Environment variables and .env file
CLIContext - CLI state and output handler
SDK Implementation Patterns
Session Management
AsyncSession (Primary):
- Async context manager for API operations
- Manages HTTP client lifecycle, authentication tokens
- Uses
api_session ContextVar for context propagation
Session (Sync Wrapper):
- Synchronous wrapper for CLI commands
- Creates async event loop internally
- Same interface as AsyncSession
See:
client/session.py - Session and AsyncSession implementation
client/request.py - HTTP request handling
client/config.py - APIConfig for environment variables
API Function Pattern
@api_function decorator:
- Wraps instance methods as API functions
- Retrieves session from
api_session ContextVar
- Handles async/sync execution
- Provides consistent error handling
BaseFunction metaclass:
- Creates function group instances bound to session
- Examples:
session.FairShare, session.Admin
Standard operations:
create_* - POST requests
get_* - GET single item
search_* - GET collection with filters
update_* - PATCH requests
delete_* - DELETE requests
purge_* - Permanent deletion
See:
client/func/base.py - BaseFunction and @api_function
client/func/fair_share.py - Complete implementation example
Pydantic Models (Future Standard)
Current state:
- attrs - Current usage in
client/cli/types.py
- Pydantic - Future standard in
common/dto/
- New code should use Pydantic
Why Pydantic:
- Runtime type validation
- Automatic JSON serialization/deserialization
- IDE autocomplete and type checking
- Better error messages
Usage:
model_dump(mode="json", exclude_none=True) - To JSON dict
model_validate(data) - From JSON dict
- Handles nested models automatically
See:
client/func/fair_share.py - Pydantic usage in SDK
common/dto/fair_share.py - Shared DTO definitions
SDK Implementation Flow
Standard pattern:
- Define request/response Pydantic models
- Create method with @api_function decorator
- Build Request object with endpoint and data
- Fetch JSON response from session
- Parse with
Response.model_validate(data)
- Return typed result
See complete example:
client/func/fair_share.py - All standard operations (create, get, search, update, delete)
CLI Implementation Patterns
Click Command Structure
Command organization:
ExtendedCommandGroup - Enhanced Click group with interrupt handling
LazyGroup - Defers module imports until needed
@pass_ctx_obj - Type-safe CLIContext injection
See:
client/cli/main.py - Main entry and command groups
client/cli/extensions.py - @pass_ctx_obj decorator
client/cli/fair_share/__init__.py - LazyGroup usage
Output Handlers
FieldSpec:
- Defines displayable fields for a model
- Specifies field name, human-readable label, formatters
- Supports nested fields (dot notation) and custom formatters
BaseOutputHandler:
print_item() - Single item display
print_list() - Collection display
print_error() - Error messages
- Implementations: ConsoleOutputHandler (table), JSONOutputHandler
See:
client/output/types.py - FieldSpec and BaseOutputHandler protocol
client/output/fields.py - Field utilities
client/cli/fair_share/commands.py - FieldSpec definitions and usage
CLI + SDK Integration
Pattern:
- Click command → Session context → SDK function → REST API
- Catch
BackendAIError exceptions
- Use
ctx.output.print_error() for consistent formatting
- Exit with appropriate ExitCode
Error handling:
- All exceptions inherit from
BackendAIError
- Exit codes from
ExitCode enum (OK=0, FAILURE=1, INVALID_ARGUMENT=2, PERMISSION_DENIED=3)
ctx.output.print_error() handles both console and JSON modes
See:
client/cli/fair_share/commands.py - Complete CLI integration
client/cli/types.py - CLIContext and ExitCode
client/exceptions.py - Exception hierarchy
Common Patterns
| Pattern | SDK | CLI |
|---|
| Session | async with AsyncSession() | with Session() (sync wrapper) |
| Request | Request("POST", "/endpoint", json={...}) | SDK handles internally |
| Response | Response.model_validate(data) | SDK returns parsed model |
| Output | Return Pydantic model | ctx.output.print_item() or print_list() |
| Error | Raise BackendAIError subclass | print_error() + sys.exit(ExitCode) |
| Config | APIConfig() reads environment | Passed via CLIContext |
| Async | Always async (async def, await) | Sync wrapper handles internally |
Implementation Checklist
When implementing new CLI/SDK feature:
-
✅ Implement REST API (/api-guide)
- Handler with standard operations
- Proper error responses
-
✅ Define Pydantic models
- Request DTOs in
common/dto/
- Response DTOs shared with API layer
-
✅ Implement SDK function
- Add method to appropriate function group
- Use
@api_function decorator
- Handle all operations (create, get, search, update, delete)
-
✅ Define FieldSpec
- List displayable fields
- Add formatters for complex types
- Consider both console and JSON output
-
✅ Implement CLI commands
- Add Click command group
- Map options/arguments to SDK calls
- Handle errors with proper exit codes
-
✅ Write tests (/test-guide)
- SDK: Mock HTTP responses (pytest-aiohttp)
- CLI: Use CliRunner, mock SDK functions (not HTTP)
- Verify request/response serialization
-
✅ Update documentation
- Add usage examples to README
- Document new CLI commands
Testing
SDK tests:
- Mock HTTP responses with
pytest-aiohttp
- Test request serialization and response parsing
- Verify Pydantic model validation
CLI tests:
- Use Click's
CliRunner for command invocation
- Mock SDK functions (not HTTP layer)
- Test output formatting and exit codes
See:
/test-guide skill for testing workflow
tests/unit/client/ - Test examples
Reference Files
Complete implementations:
client/func/fair_share.py - Full SDK with Pydantic models
client/cli/fair_share/commands.py - Full CLI with all patterns
Architecture components:
client/session.py - Session and AsyncSession
client/func/base.py - @api_function and BaseFunction
client/cli/main.py - CLI entry point
client/cli/extensions.py - @pass_ctx_obj decorator
client/output/types.py - FieldSpec and output handlers
client/config.py - APIConfig
client/cli/types.py - CLIContext and ExitCode
client/exceptions.py - Exception hierarchy
V2 SDK/CLI (New Pattern)
All new SDK/CLI work MUST use the v2 pattern.
V2 SDK Client (client/v2/domains_v2/)
- Inherits
BaseDomainClient, receives BackendAIAuthClient via constructor
- Uses v2 DTOs from
common/dto/manager/v2/ exclusively
typed_request() handles serialization and response parsing
- Registry:
V2ClientRegistry (client/v2/v2_registry.py) with @cached_property per domain
V2 CLI (client/cli/v2/)
Command pattern: ./bai [admin] {entity} [{sub-entity}] {operation}
admin_ SDK methods → commands under ./bai admin {entity} ...
- Non-admin methods →
./bai {entity} ...
- Entity names are singular (domain, user, agent)
- Each entity has its own directory with
__init__.py + commands.py
- Admin commands in
admin/{entity}.py
- Sub-entities as separate Click sub-groups (e.g., revision, channel, role)
- Construct Pydantic DTOs with explicit field arguments (never kwargs unpacking)
- Filter options are domain-specific CLI args (
--name-contains, --status, etc.)
--order-by field:direction supports multiple values
- Config from
~/.backend.ai/ via load_v2_config() → V2ConnectionConfig dataclass
V2 Config (~/.backend.ai/)
config.toml — endpoint, endpoint_type, api_version
credentials.toml — access_key, secret_key
session/cookie.dat — webserver session cookie (login/logout)
Related skills:
/api-guide - REST API implementation (prerequisite)
/test-guide - Testing workflow
/local-dev - Service management and CLI testing