diff --git a/docs/configuration/miscellaneous.md b/docs/configuration/miscellaneous.md index 18de6458d19..4ad3ba6ac1c 100644 --- a/docs/configuration/miscellaneous.md +++ b/docs/configuration/miscellaneous.md @@ -53,6 +53,16 @@ Sets content for the top banner in the user interface. --- +## COPILOT_ENABLED + +!!! tip "Dynamic Configuration Parameter" + +Default: `True` + +Enables or disables the [NetBox Copilot](https://netboxlabs.com/docs/copilot/) agent globally. When enabled, users can opt to toggle the agent individually. + +--- + ## CENSUS_REPORTING_ENABLED Default: `True` diff --git a/docs/development/user-preferences.md b/docs/development/user-preferences.md index 9944b506054..c67c3576392 100644 --- a/docs/development/user-preferences.md +++ b/docs/development/user-preferences.md @@ -6,10 +6,14 @@ For end‑user guidance on resetting saved table layouts, see [Features > User P ## Available Preferences -| Name | Description | -|--------------------------|---------------------------------------------------------------| -| data_format | Preferred format when rendering raw data (JSON or YAML) | -| pagination.per_page | The number of items to display per page of a paginated table | -| pagination.placement | Where to display the paginator controls relative to the table | -| tables.${table}.columns | The ordered list of columns to display when viewing the table | -| tables.${table}.ordering | A list of column names by which the table should be ordered | +| Name | Description | +|----------------------------|---------------------------------------------------------------| +| `csv_delimiter` | The delimiting character used when exporting CSV data | +| `data_format` | Preferred format when rendering raw data (JSON or YAML) | +| `locale.language` | The language selected for UI translation | +| `pagination.per_page` | The number of items to display per page of a paginated table | +| `pagination.placement` | Where to display the paginator controls relative to the table | +| `tables.${table}.columns` | The ordered list of columns to display when viewing the table | +| `tables.${table}.ordering` | A list of column names by which the table should be ordered | +| `ui.copilot_enabled` | Toggles the NetBox Copilot AI agent | +| `ui.tables.striping` | Toggles visual striping of tables in the UI | diff --git a/netbox/core/forms/model_forms.py b/netbox/core/forms/model_forms.py index 0a683a381e3..52c68fd7930 100644 --- a/netbox/core/forms/model_forms.py +++ b/netbox/core/forms/model_forms.py @@ -166,8 +166,8 @@ class ConfigRevisionForm(forms.ModelForm, metaclass=ConfigFormMetaclass): FieldSet('CUSTOM_VALIDATORS', 'PROTECTION_RULES', name=_('Validation')), FieldSet('DEFAULT_USER_PREFERENCES', name=_('User Preferences')), FieldSet( - 'MAINTENANCE_MODE', 'GRAPHQL_ENABLED', 'CHANGELOG_RETENTION', 'JOB_RETENTION', 'MAPS_URL', - name=_('Miscellaneous') + 'MAINTENANCE_MODE', 'COPILOT_ENABLED', 'GRAPHQL_ENABLED', 'CHANGELOG_RETENTION', 'JOB_RETENTION', + 'MAPS_URL', name=_('Miscellaneous'), ), FieldSet('comment', name=_('Config Revision')) ) diff --git a/netbox/netbox/config/parameters.py b/netbox/netbox/config/parameters.py index 54c9027ccc6..cc87b58bbb3 100644 --- a/netbox/netbox/config/parameters.py +++ b/netbox/netbox/config/parameters.py @@ -183,6 +183,15 @@ def __init__(self, name, label, default, description='', field=None, field_kwarg description=_("Enable maintenance mode"), field=forms.BooleanField ), + ConfigParam( + name='COPILOT_ENABLED', + label=_('NetBox Copilot enabled'), + default=True, + description=_( + "Enable the NetBox Copilot AI agent globally. If enabled, users can toggle the agent individually." + ), + field=forms.BooleanField + ), ConfigParam( name='GRAPHQL_ENABLED', label=_('GraphQL enabled'), diff --git a/netbox/netbox/context_processors.py b/netbox/netbox/context_processors.py index 3065855e6bd..d68236da8e8 100644 --- a/netbox/netbox/context_processors.py +++ b/netbox/netbox/context_processors.py @@ -25,10 +25,15 @@ def preferences(request): Adds preferences for the current user (if authenticated) to the template context. Example: {{ preferences|get_key:"pagination.placement" }} """ + config = get_config() user_preferences = request.user.config if request.user.is_authenticated else {} return { 'preferences': user_preferences, - 'htmx_navigation': user_preferences.get('ui.htmx_navigation', False) == 'true' + 'copilot_enabled': ( + config.COPILOT_ENABLED and not django_settings.ISOLATED_DEPLOYMENT and + user_preferences.get('ui.copilot_enabled', False) == 'true' + ), + 'htmx_navigation': user_preferences.get('ui.htmx_navigation', False) == 'true', } diff --git a/netbox/netbox/preferences.py b/netbox/netbox/preferences.py index d8fb130f4ac..942efe38f85 100644 --- a/netbox/netbox/preferences.py +++ b/netbox/netbox/preferences.py @@ -49,6 +49,15 @@ def get_csv_delimiters(): else '' ) ), + 'ui.copilot_enabled': UserPreference( + label=_('NetBox Copilot'), + choices=( + ('', _('Disabled')), + ('true', _('Enabled')), + ), + description=_('Enable the NetBox Copilot AI agent'), + default=False, + ), 'pagination.per_page': UserPreference( label=_('Page length'), choices=get_page_lengths(), diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 194567b47aa..87c20c11ee1 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -653,6 +653,13 @@ def _setting(name, default=None): CENSUS_URL = 'https://census.netbox.oss.netboxlabs.com/api/v1/' +# +# NetBox Copilot +# + +NETBOX_COPILOT_URL = 'https://static.copilot.netboxlabs.ai/load.js' + + # # Django social auth # diff --git a/netbox/templates/base/base.html b/netbox/templates/base/base.html index 22d7ddcfb02..47133add728 100644 --- a/netbox/templates/base/base.html +++ b/netbox/templates/base/base.html @@ -69,6 +69,9 @@ {% block layout %}{% endblock %} {# Additional Javascript #} + {% if copilot_enabled and request.user.is_authenticated %} + + {% endif %} {% block javascript %}{% endblock %} {# User messages #} diff --git a/netbox/templates/core/inc/config_data.html b/netbox/templates/core/inc/config_data.html index 939b8588f34..883fa0426db 100644 --- a/netbox/templates/core/inc/config_data.html +++ b/netbox/templates/core/inc/config_data.html @@ -129,6 +129,10 @@ {% trans "Maintenance mode" %} {% checkmark config.MAINTENANCE_MODE %} + + {% trans "NetBox Copilot enabled" %} + {% checkmark config.COPILOT_ENABLED %} + {% trans "GraphQL enabled" %} {% checkmark config.GRAPHQL_ENABLED %} diff --git a/netbox/users/forms/model_forms.py b/netbox/users/forms/model_forms.py index 3e8c853a56f..bc54d94fdd1 100644 --- a/netbox/users/forms/model_forms.py +++ b/netbox/users/forms/model_forms.py @@ -11,6 +11,7 @@ from core.models import ObjectType from ipam.formfields import IPNetworkFormField from ipam.validators import prefix_validator +from netbox.config import get_config from netbox.preferences import PREFERENCES from users.constants import * from users.models import * @@ -64,8 +65,8 @@ def __new__(mcs, name, bases, attrs): class UserConfigForm(forms.ModelForm, metaclass=UserConfigFormMetaclass): fieldsets = ( FieldSet( - 'locale.language', 'pagination.per_page', 'pagination.placement', 'ui.htmx_navigation', - 'ui.tables.striping', + 'locale.language', 'ui.copilot_enabled', 'pagination.per_page', 'pagination.placement', + 'ui.htmx_navigation', 'ui.tables.striping', name=_('User Interface') ), FieldSet('data_format', 'csv_delimiter', name=_('Miscellaneous')), @@ -83,8 +84,7 @@ class Meta: def __init__(self, *args, instance=None, **kwargs): # Get initial data from UserConfig instance - initial_data = flatten_dict(instance.data) - kwargs['initial'] = initial_data + kwargs['initial'] = flatten_dict(instance.data) super().__init__(*args, instance=instance, **kwargs) @@ -93,6 +93,10 @@ def __init__(self, *args, instance=None, **kwargs): (f'tables.{table_name}', '') for table_name in instance.data.get('tables', []) ) + # Disable Copilot preference if it has been disabled globally + if not get_config().COPILOT_ENABLED: + self.fields['ui.copilot_enabled'].disabled = True + def save(self, *args, **kwargs): # Set UserConfig data