YueGuobin dde2a00c5b feat(agent): update import paths for gns3_copilot modules
Updated import statements across multiple agent files to use absolute paths starting with `gns3server.agent.gns3_copilot` instead of relative `gns3_copilot` imports. This ensures proper module resolution within the gns3-server package structure and prevents import errors when the agent is executed from different contexts.
2026-03-04 12:27:50 +08:00

133 lines
4.5 KiB
Python

"""
This module provides a LangChain BaseTool to retrieve the topology of a
specific GNS3 project by project ID.
"""
import copy
import logging
from typing import Any
from langchain.tools import BaseTool
from gns3server.agent.gns3_copilot.gns3_client import Project, get_gns3_connector
# Configure logging
logger = logging.getLogger(__name__)
# Define LangChain tool class
class GNS3TopologyTool(BaseTool):
"""LangChain tool for retrieving GNS3 project topology information."""
name: str = "gns3_topology_reader"
description: str = """
Retrieves the topology of a GNS3 project including nodes and links.
Input: `project_id` (str, required): UUID of the GNS3 project.
Output: Dictionary with:
- `project_id`, `name`, `status`: Project metadata
- `nodes`: Dict of node details (node_id, name, ports, console_port, type, etc.)
- `links`: List of link connections
Use this to understand network structure before making changes.
"""
def _run(
self,
tool_input: Any = None,
run_manager: Any = None,
project_id: str | None = None,
) -> dict:
"""
Synchronous method to retrieve the topology of a specific GNS3 project.
Args:
tool_input : Input parameters, typically a dict or Pydantic model containing server_url.
run_manager : Callback manager for tool run.
project_id : The UUID of the specific GNS3 project to retrieve topology from.
Returns:
dict: A dictionary containing the project ID, name, status, nodes, and links,
or an error dictionary if an exception occurs or project_id is not provided.
"""
# Log received input
logger.info("Received tool_input: %s, project_id: %s", tool_input, project_id)
try:
# Validate project_id parameter
if not project_id:
logger.error("project_id parameter is required.")
return {
"error": "project_id parameter is required. Please provide a valid project UUID."
}
# Initialize Gns3Connector using factory function
logger.info("Connecting to GNS3 server...")
server = get_gns3_connector()
if server is None:
logger.error("Failed to create GNS3 connector")
return {
"error": "Failed to connect to GNS3 server. Please check your configuration."
}
# Use the provided project_id directly
logger.info(f"Retrieving topology for project_id: {project_id}")
project = Project(project_id=project_id, connector=server)
project.get() # Load project details
# Get topology JSON: includes nodes (devices), links, etc.
topology = {
"project_id": project.project_id,
"name": project.name,
"status": project.status,
"nodes": self._clean_nodes_ports(
copy.deepcopy(project.nodes_inventory())
),
"links": project.links_summary(is_print=False),
}
# Log topology result
logger.info("Topology retrieved: %s", topology)
return topology
except Exception as e:
logger.error("Error retrieving GNS3 topology: %s", str(e))
return {"error": f"Failed to retrieve topology: {str(e)}"}
def _clean_nodes_ports(self, data: dict) -> dict:
"""
Clean and simplify the nodes data structure.
Simplify each node's ports list to only keep name and short_name fields.
"""
for node in data.values(): # Iterate through R-1, R-2, R-3, R-4
if "ports" in node and isinstance(node["ports"], list):
node["ports"] = [
{"name": port["name"], "short_name": port["short_name"]}
for port in node["ports"]
]
return data
if __name__ == "__main__":
from pprint import pprint
# Test the tool
tool = GNS3TopologyTool()
# Example usage with project_id
# Replace with an actual project UUID from your GNS3 server
example_project_id = "0c0fde25-6ead-4413-a283-ea8fd2324291"
print("Testing GNS3TopologyTool with project_id...")
result = tool._run(project_id=example_project_id)
pprint(result)
# Test without project_id (should return error)
print("\nTesting without project_id (should return error)...")
error_result = tool._run()
pprint(error_result)