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 - tdd-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 handlingLazyGroup- Lazy loading for faster startupCLIContext- Shared state (config, output mode)@pass_ctx_obj- CLIContext injection decoratorFieldSpec- Output field definitionsBaseOutputHandler- Console/JSON formatters
SDK Layer:
BaseFunction- Metaclass for API function classes@api_function- Decorator for API methodsAsyncSession- Primary async HTTP sessionSession- Sync wrapper for CLIapi_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 fileCLIContext- 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_sessionContextVar 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 implementationclient/request.py- HTTP request handlingclient/config.py- APIConfig for environment variables
API Function Pattern
@api_function decorator:
- Wraps instance methods as API functions
- Retrieves session from
api_sessionContextVar - 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 requestsget_*- GET single itemsearch_*- GET collection with filtersupdate_*- PATCH requestsdelete_*- DELETE requestspurge_*- Permanent deletion
See:
client/func/base.py- BaseFunction and @api_functionclient/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 dictmodel_validate(data)- From JSON dict- Handles nested models automatically
See:
client/func/fair_share.py- Pydantic usage in SDKcommon/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 handlingLazyGroup- Defers module imports until needed@pass_ctx_obj- Type-safe CLIContext injection
See:
client/cli/main.py- Main entry and command groupsclient/cli/extensions.py- @pass_ctx_obj decoratorclient/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 displayprint_list()- Collection displayprint_error()- Error messages- Implementations: ConsoleOutputHandler (table), JSONOutputHandler
See:
client/output/types.py- FieldSpec and BaseOutputHandler protocolclient/output/fields.py- Field utilitiesclient/cli/fair_share/commands.py- FieldSpec definitions and usage
CLI + SDK Integration
Pattern:
- Click command → Session context → SDK function → REST API
- Catch
BackendAIErrorexceptions - Use
ctx.output.print_error()for consistent formatting - Exit with appropriate ExitCode
Error handling:
- All exceptions inherit from
BackendAIError - Exit codes from
ExitCodeenum (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 integrationclient/cli/types.py- CLIContext and ExitCodeclient/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
- Request DTOs in
✅ Implement SDK function
- Add method to appropriate function group
- Use
@api_functiondecorator - 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 (
/tdd-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
CliRunnerfor command invocation - Mock SDK functions (not HTTP layer)
- Test output formatting and exit codes
See:
/tdd-guideskill for testing workflowtests/unit/client/- Test examples
Reference Files
Complete implementations:
client/func/fair_share.py- Full SDK with Pydantic modelsclient/cli/fair_share/commands.py- Full CLI with all patterns
Architecture components:
client/session.py- Session and AsyncSessionclient/func/base.py- @api_function and BaseFunctionclient/cli/main.py- CLI entry pointclient/cli/extensions.py- @pass_ctx_obj decoratorclient/output/types.py- FieldSpec and output handlersclient/config.py- APIConfigclient/cli/types.py- CLIContext and ExitCodeclient/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, receivesBackendAIAuthClientvia 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_propertyper 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:directionsupports multiple values- Config from
~/.backend.ai/viaload_v2_config()→V2ConnectionConfigdataclass
V2 Config (~/.backend.ai/)
config.toml— endpoint, endpoint_type, api_versioncredentials.toml— access_key, secret_keysession/cookie.dat— webserver session cookie (login/logout)
Related skills:
/api-guide- REST API implementation (prerequisite)/tdd-guide- Testing workflow/local-dev- Service management and CLI testing