Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/370.added
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added NautobotGPT integration.
6 changes: 5 additions & 1 deletion development/creds.example.env
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,8 @@ NAUTOBOT_TOWER_PASSWORD="admin"

# - Cisco NSO ------------------------
# NSO_USERNAME="changeme"
# NSO_PASSWORD="changeme"
# NSO_PASSWORD="changeme"

# - NautobotGPT ----------------------
# NAUTOBOTGPT_USERNAME="changeme"
# NAUTOBOTGPT_PASSWORD="changeme"
5 changes: 5 additions & 0 deletions development/development.env
Original file line number Diff line number Diff line change
Expand Up @@ -107,5 +107,10 @@ NAUTOBOT_CHATOPS_ENABLE_SLURPIT="False"
SLURPIT_HOST="https://sandbox.slurpit.io"
SLURPIT_VERIFY="True"

# - NautobotGPT ------------------------
NAUTOBOT_CHATOPS_ENABLE_NAUTOBOTGPT="False"
NAUTOBOTGPT_MODEL="nautobotgpt_next"
NAUTOBOTGPT_URL="https://example.nautobot.cloud"

# Use a less verbose log level for Celery Beat
NAUTOBOT_BEAT_LOG_LEVEL=INFO
9 changes: 9 additions & 0 deletions development/nautobot_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,11 @@
"slurpit_host": os.environ.get("SLURPIT_HOST"),
"slurpit_token": os.environ.get("SLURPIT_API_TOKEN"),
"slurpit_verify": is_truthy(os.environ.get("SLURPIT_VERIFY", True)),
# - NautobotGPT ----------------------
"nautobotgpt_model": os.environ.get("NAUTOBOTGPT_MODEL", "nautobotgpt"),
"nautobotgpt_username": os.environ.get("NAUTOBOTGPT_USERNAME"),
"nautobotgpt_password": os.environ.get("NAUTOBOTGPT_PASSWORD"),
"nautobotgpt_url": os.environ.get("NAUTOBOTGPT_URL", ""),
},
}
if os.getenv("NAUTOBOT_CHATOPS_ENABLE_MATTERMOST", "") != "":
Expand Down Expand Up @@ -228,5 +233,9 @@
PLUGINS_CONFIG["nautobot_chatops"]["enable_nso"] = is_truthy(os.getenv("NAUTOBOT_CHATOPS_ENABLE_NSO"))
if os.getenv("NAUTOBOT_CHATOPS_ENABLE_SLURPIT", "") != "":
PLUGINS_CONFIG["nautobot_chatops"]["enable_slurpit"] = is_truthy(os.getenv("NAUTOBOT_CHATOPS_ENABLE_SLURPIT"))
if os.getenv("NAUTOBOT_CHATOPS_ENABLE_NAUTOBOTGPT", "") != "":
PLUGINS_CONFIG["nautobot_chatops"]["enable_nautobotgpt"] = is_truthy(
os.getenv("NAUTOBOT_CHATOPS_ENABLE_NAUTOBOTGPT")
)

METRICS_ENABLED = is_truthy(os.getenv("NAUTOBOT_METRICS_ENABLED"))
24 changes: 12 additions & 12 deletions docs/admin/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,17 @@

This guide outlines the process of enabling Nautobot ChatOps, which includes:

<!--- pyml disable list-anchored-indent --->
- [Nautobot ChatOps Installation Guide](#nautobot-chatops-installation-guide)
- [Prerequisites](#prerequisites)
- [Potential Apps Conflicts](#potential-apps-conflicts)
- [Chat Platforms Configuration](#chat-platforms-configuration)
- [Installation Guide](#installation-guide)
- [Install Guide](#install-guide)
- [Configuration Guide](#configuration-guide)
- [Granting Access to the Chat Platform](#granting-access-to-the-chat-platform)
- [Link Nautobot Account](#link-nautobot-account)
- [Test Your Chatbot](#test-your-chatbot)
- [Integrations Configuration](#integrations-configuration)
<!--- pyml enable list-anchored-indent --->
- [Prerequisites](#prerequisites)
- [Potential Apps Conflicts](#potential-apps-conflicts)
- [Chat Platforms Configuration](#chat-platforms-configuration)
- [Installation Guide](#installation-guide)
- [Install Guide](#install-guide)
- [Configuration Guide](#configuration-guide)
- [Granting Access to the Chat Platform](#granting-access-to-the-chat-platform)
- [Link Nautobot Account](#link-nautobot-account)
- [Test Your Chatbot](#test-your-chatbot)
- [Integrations Configuration](#integrations-configuration)

{% include-markdown '../glossary.md' heading-offset=1 %}

Expand Down Expand Up @@ -44,6 +42,7 @@ Conflicting Apps list:
- `nautobot_plugin_chatops_grafana`
- `nautobot_plugin_chatops_meraki`
- `nautobot_plugin_chatops_panorama`
- `nautobot_plugin_chatops_nautobotgpt`

To prevent conflicts during `nautobot-chatops` upgrade:

Expand Down Expand Up @@ -166,3 +165,4 @@ Set up integrations using the specific guides:
- [Cisco NSO](./integrations/nso.md)
- [Palo Alto Panorama](./integrations/panorama.md)
- [Slurpit](./integrations/slurpit.md)
- [NautobotGPT](./integrations/nautobotgpt.md)
50 changes: 50 additions & 0 deletions docs/admin/integrations/nautobotgpt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# NautobotGPT Integration Setup

This guide will walk you through steps to set up NautobotGPT integration with the `nautobot_chatops` App.

## Prerequisites

Before configuring the integration, please ensure the following:

- `nautobot-chatops` App was [installed with integration extra dependencies](../install.md#installation-guide).

```shell
pip install nautobot-chatops[nautobotgpt]
```

- `nautobot-chatops` App is set up with at least one [enabled chat platform](../install.md#chat-platforms-configuration) and [tested](./../install.md#test-your-chatbot).

## Command Setup

Create a top-level command named `nautobotgpt` in your enabled chat platform. For detailed instructions related to your specific chat platform, refer to the [platform specific set up](../install.md#chat-platforms-configuration).

## Configuration

You must define the following values in your `nautobot_config.py` file:

```
| Configuration Setting | Mandatory? | Default | Available on Admin Config |
| ---------------------- | ---------- | ----------- | ------------------------- |
| `enable_nautobotgpt` | Yes | False | Yes |
| `nautobotgpt_model` | No | | No |
| `nautobotgpt_url` | Yes | | No |
| `nautobotgpt_username` | Yes | | No |
| `nautobotgpt_password` | Yes | True | No |
```

Below is an example snippet from `development/nautobot_config.py` that demonstrates how to enable and configure NautobotGPT integration:

```python
PLUGINS = ["nautobot_chatops"]

PLUGINS_CONFIG = {
"nautobot_chatops": {
...
"enable_nautobotgpt": is_truthy(os.getenv("NAUTOBOT_CHATOPS_ENABLE_NAUTOBOTGPT", True)),
"nautobotgpt_model": os.getenv("NAUTOBOTGPT_MODEL"),
"nautobotgpt_url": os.getenv("NAUTOBOTGPT_URL"),
"nautobotgpt_username": os.getenv("NAUTOBOTGPT_USERNAME"),
"nautobotgpt_password": os.getenv("NAUTOBOTGPT_PASSWORD"),
}
}
```
1 change: 1 addition & 0 deletions docs/user/app_getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,4 @@ The `nautobot-chatops` package includes multiple integrations. Each integration
- [Cisco NSO](./integrations/nso.md)
- [Palo Alto Panorama](./integrations/panorama.md)
- [Slurpit](./integrations/slurpit.md)
- [NautobotGPT](./integrations/nautobotgpt.md)
34 changes: 34 additions & 0 deletions docs/user/integrations/nautobotgpt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# NautobotGPT Chat Commands

## `/nautobotgpt` Command

Interact with NautobotGPT by utilizing the following sub-commands:

| Command | Arguments | Description |
|-------- | --------- | ----------- |
| `ask` | | Ask NautobotGPT a question. |

!!! note
All sub-commands are intended to be used with the `/nautobotgpt` prefix.

## `/nautobotgpt ask` Command

Ask NautobotGPT a question about your Nautobot instance. The command will return a response based on the context of your Nautobot data.

### Usage

```shell
/nautobotgpt ask <question>
```

### Example

```shell
/nautobotgpt ask "What are the available devices in my Nautobot instance?"
```

### Response

NautobotGPT will analyze the question and provide a response based on the data available in your Nautobot instance. The response may include information about devices, circuits, IP addresses, or any other relevant data stored in Nautobot.

It can also analyze and respond with general information about Nautobot, such as its features, capabilities, documentation, and how to use it effectively.
2 changes: 2 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ nav:
- "user/integrations/panorama.md"
- "user/integrations/nso.md"
- "user/integrations/slurpit.md"
- "user/integrations/nautobotgpt.md"
- Administrator Guide:
- Install and Configure: "admin/install.md"
- Platforms:
Expand All @@ -136,6 +137,7 @@ nav:
- "admin/integrations/panorama.md"
- "admin/integrations/nso.md"
- "admin/integrations/slurpit.md"
- "admin/integrations/nautobotgpt.md"
- Upgrade: "admin/upgrade.md"
- Uninstall: "admin/uninstall.md"
- Compatibility Matrix: "admin/compatibility_matrix.md"
Expand Down
9 changes: 9 additions & 0 deletions nautobot_chatops/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"nautobot_plugin_chatops_ipfabric",
"nautobot_plugin_chatops_meraki",
"nautobot_plugin_chatops_panorama",
"nautobot_plugin_chatops_nautobotgpt",
]


Expand Down Expand Up @@ -114,6 +115,11 @@ class NautobotChatOpsConfig(NautobotAppConfig):
"slurpit_host": "",
"slurpit_token": "",
"slurpit_verify": True,
# - NautobotGPT ---------------------
"nautobotgpt_url": "",
"nautobotgpt_username": "",
"nautobotgpt_password": "",
"nautobotgpt_model": "nautobotgpt",
}
constance_config = {
"fallback_chatops_user": ConstanceConfigItem(default="chatbot", help_text="Enable Mattermost Chat Platform."),
Expand Down Expand Up @@ -142,6 +148,9 @@ class NautobotChatOpsConfig(NautobotAppConfig):
),
"enable_nso": ConstanceConfigItem(default=False, help_text="Enable NSO Integration.", field_type=bool),
"enable_slurpit": ConstanceConfigItem(default=False, help_text="Enable Slurpit Integration.", field_type=bool),
"enable_nautobotgpt": ConstanceConfigItem(
default=False, help_text="Enable NautobotGPT Integration.", field_type=bool
),
}

home_view_name = "plugins:nautobot_chatops:commandlog_list"
Expand Down
1 change: 1 addition & 0 deletions nautobot_chatops/integrations/nautobotgpt/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Base module for nautobot_chatops.integrations.nautobotgpt."""
104 changes: 104 additions & 0 deletions nautobot_chatops/integrations/nautobotgpt/nautobotgpt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
"""All interactions with NautobotGPT."""

import logging
from urllib.parse import urlparse

import requests
from django.conf import settings

logger = logging.getLogger(__name__)

_CONFIG = settings.PLUGINS_CONFIG["nautobot_chatops"]

DEFAULT_TIMEOUT = 30


def _get_url(uri):
"""Validate URI schema and no trailing slash.

Args:
uri(str): NautobotGPT URI

Returns:
(str): Validated/Cleaned URI.
"""
valid_uri = urlparse(uri)
if valid_uri.scheme not in ["http", "https"]:
return None
return valid_uri.geturl().rstrip("/")


class NautobotGPT: # pylint: disable=too-many-function-args
"""Representation and methods for interacting with NautobotGPT."""

def __init__(
self,
nautobotgpt_url=_CONFIG["nautobotgpt_url"],
username=_CONFIG["nautobotgpt_username"],
password=_CONFIG["nautobotgpt_password"],
model=_CONFIG["nautobotgpt_model"],
): # pylint: disable=too-many-arguments
"""Initialization of NautobotGPT class.

Args:
nautobotgpt_url (str): URL of NautobotGPT endpoint
username (str): Username to log into NautobotGPT
password (str): Password to log into NautobotGPT
model (str): Model to use for NautobotGPT
"""
if nautobotgpt_url:
self.url = _get_url(nautobotgpt_url)
else:
self.url = None
self.username = username
self.password = password
self.model = model
self.headers = {"Content-Type": "application/json"}
if not self.url or not self.username or not self.password:
raise ValueError(
"Missing required parameters for NautobotGPT access - check environment and app configuration"
)
self.login_open_webui()
self.template: str = "Format your response in markdown formatting. Use a single * for bold text, a single _ for italic text, and a single ` for inline code. For code blocks, use three backticks (```) before and after the code block. For example:\n\n```python\nprint('Hello, World!')\n```\n\n"

def login_open_webui(self) -> None:
"""Log in to Open WebUI and return the token."""
url = f"{self.url}/api/v1/auths/signin"
payload = {"email": self.username, "password": self.password}

response = requests.post(url, headers=self.headers, json=payload, timeout=DEFAULT_TIMEOUT)
data = response.json()
self.headers["Authorization"] = f"Bearer {data.get('token')}"

def ask(self, user_prompt: str, chat_id: str = "00000000-0000-0000-0000-000000000000") -> str:
"""Ask a question to NautobotGPT and return the response.

Args:
user_prompt (str): The question or prompt to send to NautobotGPT.
chat_id (str): The ID of the chat session. Defaults to a placeholder value.

Returns:
str: The response from NautobotGPT.
"""
url = f"{self.url}/api/chat/completions"
payload = {
"model": self.model,
"messages": [{"role": "user", "content": self.template + user_prompt}],
"stream": False,
"chat_id": chat_id,
}
max_retries = 3 # Maximum number of retries
attempt = 0

while attempt < max_retries:
try:
response = requests.post(url, headers=self.headers, json=payload, timeout=DEFAULT_TIMEOUT)
response.raise_for_status() # Raise HTTPError for bad responses
break # Exit the loop if the request is successful
except requests.RequestException:
attempt += 1

Check warning on line 99 in nautobot_chatops/integrations/nautobotgpt/nautobotgpt.py

View workflow job for this annotation

GitHub Actions / unittest_report (3.13, postgresql, stable)

Missing coverage

Missing coverage on lines 98-99

data = response.json()
choices = data.get("choices", [])
message_content = choices[0].get("message", {}).get("content", "") if choices else "No response"
return message_content
45 changes: 45 additions & 0 deletions nautobot_chatops/integrations/nautobotgpt/worker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"""Worker functions implementing Nautobot "NautobotGPT" command and subcommands."""

import logging

from django.conf import settings

from nautobot_chatops.choices import CommandStatusChoices
from nautobot_chatops.workers import handle_subcommands, subcommand_of

from .nautobotgpt import NautobotGPT

NAUTOBOTGPT_URL = settings.PLUGINS_CONFIG["nautobot_chatops"]["nautobotgpt_url"]

NAUTOBOTGPT_LOGO_PATH = "nautobotgpt/NautobotGPT.png"
NAUTOBOTGPT_LOGO_ALT = "NautobotGPT Logo"

LOGGER = logging.getLogger("nautobot_chatops.integrations.nautobotgpt")


def nautobotgpt_logo(dispatcher):
"""Construct an image_element containing the locally hosted NautobotGPT logo."""
return dispatcher.image_element(dispatcher.static_url(NAUTOBOTGPT_LOGO_PATH), alt_text=NAUTOBOTGPT_LOGO_ALT)


def nautobotgpt(subcommand, **kwargs):
"""Interact with NautobotGPT."""
return handle_subcommands("nautobotgpt", subcommand, **kwargs)


@subcommand_of("nautobotgpt")
def ask(dispatcher, *args):
"""Ask NautobotGPT a question."""
nbgpt = NautobotGPT()

if not args:
dispatcher.send_markdown("Please provide a question to ask NautobotGPT.")
return CommandStatusChoices.STATUS_FAILED

question = " ".join("".join(word) for word in args).strip()
dispatcher.send_markdown(f"*Question:* {question}\nThinking...", ephemeral=True)

response = nbgpt.ask(question)

dispatcher.send_markdown(response, ephemeral=True)
return CommandStatusChoices.STATUS_SUCCEEDED
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions nautobot_chatops/tests/nautobotgpt/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Unit tests for nautobot_chatops.integrations.nautobotgpt app."""
Loading
Loading