Core Framework API (ai_nn_controller)
This is the API reference for the core ai_nn_controller package — the
control-plane half of ai_nn_controller.
The framework is domain-agnostic and supports any type of network node — optical, wireless, RAN, core network, and more.
AicApp
- class ai_nn_controller.AicApp
Base class for AIC control applications.
Required Class Attributes:
- read_measurements: dict
Dictionary mapping node IDs to lists of measurement names to subscribe to. Example:
{3: ["gain", "power"]}
- control_functions: dict
Dictionary mapping node IDs to lists of available command names. Example:
{3: ["SET_GAIN"]}
Auto-Generated Attributes (set by @aic_app decorator):
- cell_ids: list
List of node IDs this application monitors. Auto-derived from the union of
read_measurementsandcontrol_functionskeys.
- send_commands: deque
Queue of commands to be sent. Auto-initialized as an empty
dequeby the@aic_appdecorator.
Methods:
AicController
- class ai_nn_controller.AicController(with_api=False, api_host='0.0.0.0', api_port=8000, verbose=False)
Runtime engine that manages application lifecycle and message routing.
- Parameters:
with_api – Enable FastAPI server (default: False)
api_host – Host address for API server (default: “0.0.0.0”)
api_port – Port for API server (default: 8000)
verbose – Enable verbose logging (default: False)
Methods:
- run()
Start the controller. If with_api=True, starts FastAPI server. This method blocks until interrupted.
- update_app_state(app_name, state)
Update the state of an application.
- Parameters:
app_name – Name of the application
state – New state - “running”, “paused”, or “stopped”
- Returns:
Dict with app name, new state, and previous state
- Raises:
ValueError – If app doesn’t exist or state is invalid
- get_app_state(app_name)
Get current state of an application.
- Parameters:
app_name – Name of the application
- Returns:
Current state string
- Raises:
ValueError – If app doesn’t exist
- get_app_measurements(app_name)
Get latest measurements for an application.
- Parameters:
app_name – Name of the application
- Returns:
Dict mapping node_id to latest measurement
- Raises:
ValueError – If app doesn’t exist
- send_manual_control(app_name, node_id, command)
Send a manual control command.
- Parameters:
app_name – Name of the application
node_id – Target node ID
command – Command specification dict
- Returns:
Result dict with status
@aic_app Decorator
- @ai_nn_controller.decorators.aic_app.aic_app(name)
Decorator that registers an AIC application with the framework.
- Parameters:
name – Unique name for the application
This decorator:
Registers the app with AicManager
Creates FastAPI REST endpoints
Auto-generates MCP tools based on capabilities
Scans for @command_validator decorated methods
Scans for @agent_controlled decorated methods
Auto-derives
cell_idsfromread_measurementsandcontrol_functionskeysAuto-initializes
send_commandsas an emptydequeInitializes
agent_requestsqueue and_agent_handlersmap
Example:
@aic_app(name="MyNetworkApp") class MyNetworkApp(AicApp): aic_app_id = 1 control_loop_update_time = 2 read_measurements = {3: ["gain"]} control_functions = {3: ["SET_GAIN"]} # cell_ids and send_commands are auto-generated @classmethod def process(cls, measurements): pass
@command_validator Decorator
- @ai_nn_controller.decorators.command_validator.command_validator(command_name)
Decorator to register a validator function for a specific command.
Command validators are optional. If no validator is defined for a command, it passes through unchanged. Validators are primarily useful when the FastAPI REST API or MCP tools are used to send manual control commands.
- Parameters:
command_name – Name of the command to validate (e.g., “SET_GAIN”)
The validator function should be a classmethod that takes a
paramsdict and returns a tuple of(is_valid: bool, error_message: str | None).Example:
from ai_nn_controller.decorators.command_validator import command_validator @aic_app(name="SafeApp") class SafeApp(AicApp): control_functions = {8: ["SET_GAIN"]} MAX_GAIN = 25.0 # IMPORTANT: @classmethod must be ABOVE @command_validator @classmethod @command_validator("SET_GAIN") def validate_set_gain(cls, params: dict) -> tuple[bool, str | None]: """Validate SET_GAIN commands.""" target_gain = params.get("target_gain", 0) if target_gain > cls.MAX_GAIN: return False, f"Gain {target_gain} exceeds max {cls.MAX_GAIN}" return True, None
Validator Parameters:
The
paramsdict contains:node_id: Target node IDCommand-specific parameters (e.g.,
target_gain,amp_type)
Validator Return Values:
(True, None): Command is valid, proceed with execution(False, "error message"): Command is rejected with error
Behavior:
No validator defined: Command passes through unchanged
Validator raises exception: Command is rejected for safety
Applied to both REST API/MCP calls and internal
add_command()calls
@agent_controlled Decorator
- @ai_nn_controller.decorators.agent_controlled.agent_controlled(name, description, schema)
Decorator to register a method as an agent-controlled operation.
Agent-controlled operations allow MCP/AI agents to execute logic inside the process loop, with access to live measurements and the app’s internal state. Unlike regular MCP control tools (which bypass
process()), these handlers are synchronized with the process cycle.- Parameters:
name – Operation name (used as MCP tool suffix, e.g., “optimize_gain”)
description – Human-readable description for the MCP tool
schema – JSON Schema for the tool’s input parameters (properties + required)
The handler function should be a classmethod that takes
request(dict of MCP arguments) andmeasurements(dict from the process loop) and returns a result dict.Example:
from ai_nn_controller.decorators.agent_controlled import agent_controlled @aic_app(name="SmartApp") class SmartApp(AicApp): read_measurements = {8: ["preamp_gain"]} control_functions = {8: ["SET_GAIN"]} # IMPORTANT: @classmethod must be ABOVE @agent_controlled @classmethod @agent_controlled( name="optimize_gain", description="Optimize gain based on a strategy", schema={ "properties": { "node_id": {"type": "integer"}, "strategy": {"type": "string", "enum": ["max_snr", "min_power"]} }, "required": ["node_id", "strategy"] } ) def handle_optimize_gain(cls, request, measurements): """Runs inside the process loop.""" node_id = request["node_id"] latest = measurements.get(node_id, [{}])[-1] or {} new_gain = latest.get("preamp_gain", 15) + 2.0 cls.add_command(("SET_GAIN", {"node_id": node_id, "value": {"target_gain": new_gain}})) return {"status": "applied", "new_gain": new_gain}
Handler Parameters:
request(dict): The MCP tool arguments matching the defined schemameasurements(dict): Current measurements from the process loop
Handler Return Value:
A
dictthat is returned to the MCP caller as the tool result
Synchronization:
The MCP handler waits (non-blocking) for the process loop to execute the handler
Timeout defaults to
control_loop_update_time * 3 + 5secondsThe app must be in
runningstate for agent-controlled tools to work
AicManager
- class ai_nn_controller.managers.AicManager
Centralized manager for AIC applications (singleton pattern).
Class Attributes:
- controller_instance: AicController
Reference to the active controller.
Methods:
- classmethod add_aic_app(name, aic_app)
Register an application. Called by @aic_app decorator.
- classmethod add_router(name, router)
Register a FastAPI router for an application.
- classmethod get_routers()
Get all registered routers.
- classmethod set_controller(controller)
Set the controller instance.
- classmethod update_state(app_name, state)
Update application state via controller.
- classmethod get_app_state(app_name)
Get application state.
- classmethod get_measurements(app_name)
Get latest measurements.
- classmethod send_manual_control(app_name, node_id, command)
Send manual control command.
Command Registry
- ai_nn_controller.registry.register_command(name, handler, schema=None)
Register a command with the framework.
- Parameters:
name – Unique command name (e.g., “SET_GAIN”)
handler – Function taking (node_id, value) returning JSON string
schema – Optional JSON Schema for MCP tool generation
Example:
register_command( name="SET_GAIN", handler=lambda node_id, value: json.dumps({"gain": value["target"]}), schema={"description": "Set gain", "properties": {...}} )
- ai_nn_controller.registry.register_commands(commands)
Register multiple commands at once.
- Parameters:
commands – Dict mapping names to {“handler”: fn, “schema”: dict}
- ai_nn_controller.registry.execute_command(name, node_id, value)
Execute a registered command.
- Parameters:
name – Command name
node_id – Target node ID
value – Command parameters
- Returns:
JSON string payload
- Raises:
ValueError – If command not registered
- ai_nn_controller.registry.has_command(name)
Check if a command is registered.
- Parameters:
name – Command name
- Returns:
True if command exists
- ai_nn_controller.registry.list_commands()
List all registered command names.
- Returns:
List of command names
- ai_nn_controller.registry.get_command_schema(name, allowed_node_ids=None)
Get JSON schema for a command.
- Parameters:
name – Command name
allowed_node_ids – Optional list to constrain node_id enum
- Returns:
JSON Schema dict
MeasurementsHandler
Configuration
- ai_nn_controller.config.vprint(*args, **kwargs)
Verbose print function. Only outputs when verbose mode is enabled.