Interface Reference¶
Complete reference for IJarvisCommand and all supporting classes. This is the authoritative guide to every property, method, and dataclass in the command system.
Class Hierarchy¶
JarvisCommandBase provides the execute() orchestration (validation, auto-correction, then run()). IJarvisCommand adds the full property interface -- command name, description, parameters, secrets, examples, and all optional hooks.
Abstract Properties (Required)¶
These must be implemented by every command.
command_name -> str¶
Unique identifier for this command. Used as the tool name in the LLM schema, in test filters, and in the secrets database.
Rules:
- Must be unique across all commands on a node
- Use
snake_case - Keep it short and descriptive
description -> str¶
Human-readable description sent to the LLM as the tool description. This is the primary signal the LLM uses to decide whether to call this command.
@property
def description(self) -> str:
return "Weather conditions or forecast (up to 5 days). Use for ALL weather queries."
Tips:
- Be specific about what this command handles vs. what it does not
- Mention edge cases the LLM should know about
- If the LLM confuses this with another command, add clarification here
parameters -> List[IJarvisParameter]¶
Parameter definitions. These become the tool's JSON Schema for the LLM, and are validated by the execution pipeline.
@property
def parameters(self) -> List[JarvisParameter]:
return [
JarvisParameter("city", "string", required=False, description="City name"),
JarvisParameter("unit_system", "string", required=False, enum_values=["metric", "imperial"]),
]
See Parameters Deep Dive for complete documentation.
required_secrets -> List[IJarvisSecret]¶
Secrets this command needs at runtime. Validated before run() is called -- if any required secret is missing, a MissingSecretsError is raised.
@property
def required_secrets(self) -> List[IJarvisSecret]:
return [
JarvisSecret("OPENWEATHER_API_KEY", "API key", "integration", "string"),
]
See Secrets Deep Dive for complete documentation.
keywords -> List[str]¶
Keywords for fuzzy matching. Used during command discovery and as hints to the LLM.
@property
def keywords(self) -> List[str]:
return ["weather", "forecast", "temperature", "rain", "snow", "wind"]
generate_prompt_examples() -> List[CommandExample]¶
Concise examples included in the LLM system prompt. These teach the LLM how to extract parameters from natural language.
def generate_prompt_examples(self) -> List[CommandExample]:
return [
CommandExample(
voice_command="What's the weather in Chicago?",
expected_parameters={"city": "Chicago"},
is_primary=True,
),
CommandExample(
voice_command="How's the weather?",
expected_parameters={},
),
]
Rules:
- At most 1 example may have
is_primary=True - Keep this list small (3-7 examples) -- it goes into every prompt
generate_adapter_examples() -> List[CommandExample]¶
Larger, more varied example set used for LoRA adapter training. Cover edge cases, alternative phrasings, and tricky inputs.
def generate_adapter_examples(self) -> List[CommandExample]:
return [
CommandExample("What's the weather?", {}, is_primary=True),
CommandExample("Do I need an umbrella?", {}),
CommandExample("Weather in Miami", {"city": "Miami"}),
# ... 20-40 examples covering variations
]
See Examples & Training for best practices.
run(request_info, **kwargs) -> CommandResponse¶
The actual command logic. Called after all validation passes.
def run(self, request_info: RequestInformation, **kwargs) -> CommandResponse:
city = kwargs.get("city", "New York")
# ... do work ...
return CommandResponse.success_response(
context_data={"temperature": 72, "city": city}
)
Arguments:
request_info: ARequestInformationobject with the original voice command, conversation ID, and validation context**kwargs: Validated parameters extracted by the LLM, matching yourparametersdefinitions
Optional Properties (Overridable)¶
These have sensible defaults. Override only when needed.
rules -> List[str]¶
General rules included in the command schema for the LLM. Use for behavioral guidance.
@property
def rules(self) -> List[str]:
return [
"Use 'resume' not 'play' when continuing paused music",
"Default action is 'list' for email",
]
Default: []
critical_rules -> List[str]¶
Must-follow rules. Formatted with extra emphasis in the prompt to the LLM.
@property
def critical_rules(self) -> List[str]:
return [
"NEVER send an email without explicit user intent",
"Map terms: plus/sum -> 'add', minus -> 'subtract'",
]
Default: []
antipatterns -> List[CommandAntipattern]¶
Commands that the LLM should NOT confuse with this one. Helps with disambiguation.
@property
def antipatterns(self) -> List[CommandAntipattern]:
return [
CommandAntipattern(
command_name="get_current_time",
description="Time queries like 'What time is it?' Use get_current_time."
),
]
Default: []
allow_direct_answer -> bool¶
Whether the LLM may respond directly to the user without calling this tool. Useful for commands like calculate where the LLM might already know the answer.
Default: False
required_packages -> List[JarvisPackage]¶
Pip dependencies for this command. Installed automatically on first use and written to custom-requirements.txt.
from core.ijarvis_package import JarvisPackage
@property
def required_packages(self) -> List[JarvisPackage]:
return [
JarvisPackage("music-assistant-client", ">=1.3.0"),
]
Default: []
associated_service -> str | None¶
Logical grouping name for the mobile settings UI. Commands sharing the same associated_service are grouped together.
Default: Returns authentication.friendly_name if auth is configured, otherwise None.
authentication -> AuthenticationConfig | None¶
OAuth configuration. Commands sharing the same provider string share auth state -- once one command completes OAuth, all commands with that provider see the tokens.
from core.ijarvis_authentication import AuthenticationConfig
@property
def authentication(self) -> AuthenticationConfig | None:
return AuthenticationConfig(
type="oauth",
provider="google_gmail",
friendly_name="Gmail",
client_id="your-client-id",
keys=["access_token", "refresh_token"],
authorize_url="https://accounts.google.com/o/oauth2/v2/auth",
exchange_url="https://oauth2.googleapis.com/token",
scopes=["https://www.googleapis.com/auth/gmail.modify"],
supports_pkce=True,
)
Default: None
See OAuth Command Tutorial for full details.
all_possible_secrets -> List[IJarvisSecret]¶
All secrets this command could ever need across all configuration variants. Used by install_command.py to seed the database upfront. Override when required_secrets is config-dependent (e.g., different secrets for Gmail vs. IMAP).
@property
def all_possible_secrets(self) -> List[IJarvisSecret]:
return [
JarvisSecret("EMAIL_PROVIDER", "Provider", "integration", "string", required=False),
JarvisSecret("GMAIL_ACCESS_TOKEN", "Token", "integration", "string"),
JarvisSecret("IMAP_USERNAME", "Username", "integration", "string"),
# ... all variants
]
Default: Delegates to required_secrets.
Lifecycle Methods¶
These methods participate in the command execution pipeline. See Execution Lifecycle for the full flow.
pre_route(voice_command) -> PreRouteResult | None¶
Fast-path routing that bypasses the LLM entirely. Override for short, unambiguous utterances that can be parsed deterministically.
def pre_route(self, voice_command: str) -> PreRouteResult | None:
text = voice_command.lower().strip()
if text in ("pause", "pause the music"):
return PreRouteResult(arguments={"action": "pause"})
return None # Fall through to LLM
Returns: PreRouteResult with arguments dict and optional spoken_response, or None to use the normal LLM path.
post_process_tool_call(args, voice_command) -> dict¶
Fix up LLM tool-call arguments before execution. Called after the LLM produces a tool call but before execute(). Use to patch common LLM mistakes.
def post_process_tool_call(self, args: dict, voice_command: str) -> dict:
if args.get("action") == "delete":
args["action"] = "trash" # Normalize "delete" to "trash"
return args
Default: Returns args unchanged.
validate_call(**kwargs) -> list[ValidationResult]¶
Custom parameter validation after basic type and presence checks pass. Override for cross-parameter validation, context-dependent checks, or entity resolution.
from core.validation_result import ValidationResult
def validate_call(self, **kwargs) -> list[ValidationResult]:
results = super().validate_call(**kwargs) # Run default enum/type checks
entity_id = kwargs.get("entity_id")
if entity_id and not self._entity_exists(entity_id):
results.append(ValidationResult(
success=False,
param_name="entity_id",
command_name=self.command_name,
message=f"Device '{entity_id}' not found",
valid_values=self._get_known_entities(),
))
return results
Default: Iterates parameters, calls param.validate(value) on each.
handle_action(action_name, context) -> CommandResponse¶
Handle interactive button taps from the mobile app. Called when a user taps a button on a response that included actions.
from core.ijarvis_button import IJarvisButton
def handle_action(self, action_name: str, context: dict) -> CommandResponse:
if action_name == "send_click":
# Send the email draft from context
self._send_email(context["draft"])
return CommandResponse.final_response(
context_data={"message": "Email sent."}
)
return super().handle_action(action_name, context) # Handles cancel_click
Default: Handles cancel_click automatically. Returns error for unknown actions.
store_auth_values(values) -> None¶
Called when OAuth tokens are delivered from the mobile app. Override to process and store tokens as secrets.
def store_auth_values(self, values: dict[str, str]) -> None:
from services.secret_service import set_secret
if "access_token" in values:
set_secret("GMAIL_ACCESS_TOKEN", values["access_token"], "integration")
if "refresh_token" in values:
set_secret("GMAIL_REFRESH_TOKEN", values["refresh_token"], "integration")
Default: No-op.
refresh_token() -> bool¶
Refresh an OAuth2 access token. The default implementation POSTs to auth.exchange_url with grant_type=refresh_token, stores new tokens via store_auth_values(), and persists the expiration time.
Override for non-standard refresh flows.
Default: Standard OAuth2 refresh_token grant. Returns True on success, False on failure (and flags re-auth).
needs_auth() -> bool¶
Check whether the mobile app should prompt the user for authentication.
Default: If authentication is declared, checks that all required secrets are present and that no re-auth flag is set in the command_auth table.
init_data() -> dict¶
One-time initialization hook. Called manually via python scripts/init_data.py --command <name>. Use for first-install setup like registering devices or fetching initial state.
def init_data(self) -> dict:
# Fetch and cache initial device list
devices = self._fetch_devices()
return {"status": "success", "devices_found": len(devices)}
Default: Returns {"status": "no_init_required"}.
The execute() Method (JarvisCommandBase)¶
You do not override execute() -- it is the orchestration method on JarvisCommandBase that calls your hooks in order:
def execute(self, request_info, **kwargs) -> CommandResponse:
self._validate_secrets() # Check all required secrets present
self._validate_params(kwargs) # Check required params present
results = self.validate_call(**kwargs) # Value validation
errors = [r for r in results if not r.success]
if errors:
return CommandResponse.validation_error(errors)
for r in results: # Apply auto-corrections
if r.suggested_value is not None:
kwargs[r.param_name] = r.suggested_value
return self.run(request_info, **kwargs)
Related Classes¶
CommandExample¶
@dataclass
class CommandExample:
voice_command: str # "What's the weather in Chicago?"
expected_parameters: dict # {"city": "Chicago"}
is_primary: bool = False # At most 1 per example list
CommandAntipattern¶
@dataclass
class CommandAntipattern:
command_name: str # "get_current_time"
description: str # "Time queries like 'What time is it?'"
PreRouteResult¶
@dataclass
class PreRouteResult:
arguments: dict # kwargs to pass to execute()
spoken_response: str | None = None # Optional TTS override
RequestInformation¶
@dataclass
class RequestInformation:
voice_command: str # Original utterance
conversation_id: str # Conversation session ID
is_validation_response: bool = False # True if this is a clarification reply
validation_context: dict | None = None # Context from validation flow
CommandResponse¶
See Response Patterns for full documentation. Quick reference:
| Factory Method | Success | Wait for Input | Use Case |
|---|---|---|---|
success_response() |
True |
True |
Standard success |
error_response() |
False |
False |
Errors |
follow_up_response() |
True |
True |
Expects more user input |
final_response() |
True |
False |
One-shot, conversation ends |
chunked_response() |
True |
True |
Large paginated data |
validation_error() |
False |
False |
Parameter validation failures |
ValidationResult¶
@dataclass
class ValidationResult:
success: bool # True = passed, False = failed
param_name: str # Which parameter
command_name: str # Which command
message: str | None = None # Human-readable error
suggested_value: str | None = None # Auto-correction value
valid_values: list[str] | None = None # Allowed values (for LLM retry)
JarvisParameter¶
See Parameters Deep Dive.
JarvisSecret¶
See Secrets Deep Dive.
IJarvisButton¶
@dataclass
class IJarvisButton:
button_text: str # "Send", "Cancel"
button_action: str # "send_click", "cancel_click"
button_type: Literal["primary", "secondary", "destructive"]
button_icon: str | None = None # MaterialCommunityIcons name
JarvisPackage¶
@dataclass(frozen=True)
class JarvisPackage:
name: str # PyPI package name
version: str | None = None # Version spec or None for latest
# Examples:
JarvisPackage("requests") # Latest
JarvisPackage("httpx", "0.25.1") # Pinned
JarvisPackage("pydantic", ">=2.0,<3.0") # Constraint
AuthenticationConfig¶
See OAuth Command Tutorial for full documentation. Key fields:
@dataclass
class AuthenticationConfig:
type: str # "oauth"
provider: str # Groups commands sharing auth
friendly_name: str # Display name in mobile UI
client_id: str # OAuth client ID
keys: list[str] # Keys to extract from token response
# External OAuth (full URLs):
authorize_url: str | None # "https://accounts.google.com/..."
exchange_url: str | None # "https://oauth2.googleapis.com/token"
# Local/discoverable OAuth (paths + network scan):
authorize_path: str | None # "/auth/authorize"
exchange_path: str | None # "/auth/token"
discovery_port: int | None # 8123
discovery_probe_path: str | None # "/api/"
# OAuth extras:
scopes: list[str]
extra_authorize_params: dict[str, str]
extra_exchange_params: dict[str, str]
supports_pkce: bool = False
native_redirect_uri: str | None = None
# Background refresh:
requires_background_refresh: bool = False
refresh_interval_seconds: int = 600
refresh_token_secret_key: str | None = None