From 61caa20524124368c51ddd013e9df6f70f73b8ce Mon Sep 17 00:00:00 2001 From: Mark Paronyan Date: Wed, 30 Sep 2026 20:51:22 +0300 Subject: [PATCH] style: sort imports in agent and remaining compute modules --- gns3server/agent/__init__.py | 3 +- .../gns3_copilot/agent/context_manager.py | 7 +- .../agent/gns3_copilot/agent/gns3_copilot.py | 39 ++-- .../agent/gns3_copilot/agent/model_factory.py | 3 +- .../agent/gns3_copilot/agent_service.py | 8 +- .../gns3_copilot/chat_sessions_repository.py | 5 +- .../gns3_copilot/gns3_client/__init__.py | 20 +- .../gns3_copilot/gns3_client/api_handlers.py | 10 +- .../gns3_client/connector_factory.py | 7 +- .../gns3_copilot/project_agent_manager.py | 3 +- .../agent/gns3_copilot/skills/__init__.py | 18 +- .../agent/gns3_copilot/skills/manager.py | 3 +- .../agent/gns3_copilot/skills/registry.py | 4 +- .../agent/gns3_copilot/tools_v2/__init__.py | 3 +- .../tools_v2/config_tools_nornir.py | 13 +- .../tools_v2/display_tools_nornir.py | 13 +- .../tools_v2/vpcs_tools_netmiko.py | 7 +- .../agent/gns3_copilot/utils/__init__.py | 4 +- .../scripts/list_netmiko_telnet_devices.py | 2 +- .../custom_netmiko/tests/test_huawei_ce.py | 2 +- .../tests/test_ruijie_telnet.py | 2 +- .../custom_netmiko/tests/test_vpcs_telnet.py | 4 +- .../utils/custom_netmiko/vpcs_telnet.py | 1 - .../gns3_copilot/utils/gns3_drawing_utils.py | 3 +- .../gns3_copilot/utils/llm_config_helper.py | 4 +- .../gns3_copilot/utils/message_converters.py | 9 +- gns3server/agent/mcp/__init__.py | 175 +++++++++--------- gns3server/agent/mcp/appliances.py | 3 +- gns3server/agent/mcp/computes.py | 2 +- gns3server/agent/mcp/device_config.py | 3 +- gns3server/agent/mcp/drawings.py | 3 +- gns3server/agent/mcp/images.py | 3 +- gns3server/agent/mcp/projects.py | 3 +- gns3server/agent/mcp/server.py | 3 +- gns3server/agent/mcp/snapshots.py | 3 +- gns3server/agent/mcp/symbols.py | 3 +- gns3server/agent/mcp/templates.py | 3 +- .../agent/web_wireshark/manage_wireshark.py | 6 +- gns3server/agent/web_wireshark/manager.py | 1 + .../web_wireshark/setup_wireshark_image.py | 5 +- gns3server/api/server.py | 39 ++-- gns3server/compute/notification_manager.py | 1 + gns3server/compute/project_manager.py | 7 +- gns3server/compute/ubridge/hypervisor.py | 13 +- .../compute/ubridge/ubridge_hypervisor.py | 5 +- gns3server/crash_report.py | 14 +- 46 files changed, 218 insertions(+), 274 deletions(-) diff --git a/gns3server/agent/__init__.py b/gns3server/agent/__init__.py index bf6ecac3e..f27893a61 100644 --- a/gns3server/agent/__init__.py +++ b/gns3server/agent/__init__.py @@ -34,8 +34,7 @@ AI_COPILOT_AVAILABLE = False # Try to import AI Copilot components try: - from .gns3_copilot.project_agent_manager import get_project_agent_manager - from .gns3_copilot.project_agent_manager import ProjectAgentManager + from .gns3_copilot.project_agent_manager import ProjectAgentManager, get_project_agent_manager AI_COPILOT_AVAILABLE = True diff --git a/gns3server/agent/gns3_copilot/agent/context_manager.py b/gns3server/agent/gns3_copilot/agent/context_manager.py index a17338c83..48258729b 100644 --- a/gns3server/agent/gns3_copilot/agent/context_manager.py +++ b/gns3server/agent/gns3_copilot/agent/context_manager.py @@ -46,8 +46,7 @@ import logging import os import warnings from pathlib import Path -from typing import Any -from typing import Callable +from typing import Any, Callable # Configure tiktoken cache directory (must be set before importing tiktoken) _cache_dir = Path(__file__).parent.parent / "cache" / "tiktoken" @@ -55,9 +54,7 @@ _cache_dir.mkdir(parents=True, exist_ok=True) os.environ["TIKTOKEN_CACHE_DIR"] = str(_cache_dir) import tiktoken -from langchain_core.messages import BaseMessage -from langchain_core.messages import SystemMessage -from langchain_core.messages import trim_messages +from langchain_core.messages import BaseMessage, SystemMessage, trim_messages logger = logging.getLogger(__name__) diff --git a/gns3server/agent/gns3_copilot/agent/gns3_copilot.py b/gns3server/agent/gns3_copilot/agent/gns3_copilot.py index ac784cab0..148ccac63 100644 --- a/gns3server/agent/gns3_copilot/agent/gns3_copilot.py +++ b/gns3server/agent/gns3_copilot/agent/gns3_copilot.py @@ -50,17 +50,12 @@ import json import logging import operator from datetime import datetime -from typing import Annotated -from typing import Literal +from typing import Annotated, Literal # Third-party imports -from langchain.messages import AnyMessage -from langchain.messages import SystemMessage -from langchain.messages import ToolMessage +from langchain.messages import AnyMessage, SystemMessage, ToolMessage from langchain_core.runnables import RunnableConfig -from langgraph.graph import END -from langgraph.graph import START -from langgraph.graph import StateGraph +from langgraph.graph import END, START, StateGraph from langgraph.managed.is_last_step import RemainingSteps from typing_extensions import TypedDict @@ -70,8 +65,6 @@ from gns3server.agent.gns3_copilot.agent.context_manager import ( ) from gns3server.agent.gns3_copilot.agent.model_factory import ( create_base_model_with_tools, -) -from gns3server.agent.gns3_copilot.agent.model_factory import ( create_title_model, ) from gns3server.agent.gns3_copilot.gns3_client import GNS3TopologyTool @@ -79,27 +72,23 @@ from gns3server.agent.gns3_copilot.gns3_client.context_helpers import ( get_current_llm_config, ) from gns3server.agent.gns3_copilot.prompts import load_system_prompt +from gns3server.agent.gns3_copilot.skills import DeviceSkillsTool, InjectionSkillsTool, PacketAnalysisSkillsTool from gns3server.agent.gns3_copilot.skills.registry import get_prompt from gns3server.agent.gns3_copilot.tools_v2 import ( ExecuteMultipleDeviceCommands, -) -from gns3server.agent.gns3_copilot.tools_v2 import ( ExecuteMultipleDeviceConfigCommands, + GNS3CreateNodeTool, + GNS3LinkTool, + GNS3PacketFilterTool, + GNS3StartNodeTool, + GNS3StopNodeTool, + GNS3SuspendNodeTool, + GNS3TemplateTool, + GNS3UpdateNodeNameTool, + GNS3WaitTool, + PacketAnalysisTool, ) -from gns3server.agent.gns3_copilot.tools_v2 import GNS3CreateNodeTool -from gns3server.agent.gns3_copilot.tools_v2 import GNS3LinkTool -from gns3server.agent.gns3_copilot.tools_v2 import GNS3PacketFilterTool -from gns3server.agent.gns3_copilot.tools_v2 import GNS3StartNodeTool -from gns3server.agent.gns3_copilot.tools_v2 import GNS3StopNodeTool -from gns3server.agent.gns3_copilot.tools_v2 import GNS3SuspendNodeTool -from gns3server.agent.gns3_copilot.tools_v2 import GNS3TemplateTool -from gns3server.agent.gns3_copilot.tools_v2 import GNS3UpdateNodeNameTool -from gns3server.agent.gns3_copilot.tools_v2 import GNS3WaitTool from gns3server.agent.gns3_copilot.tools_v2.vpcs_tools_netmiko import VPCSCommands -from gns3server.agent.gns3_copilot.tools_v2 import PacketAnalysisTool -from gns3server.agent.gns3_copilot.skills import DeviceSkillsTool -from gns3server.agent.gns3_copilot.skills import InjectionSkillsTool -from gns3server.agent.gns3_copilot.skills import PacketAnalysisSkillsTool # Set up logger for GNS3-Copilot logger = logging.getLogger(__name__) diff --git a/gns3server/agent/gns3_copilot/agent/model_factory.py b/gns3server/agent/gns3_copilot/agent/model_factory.py index f790646bd..5700b38bc 100644 --- a/gns3server/agent/gns3_copilot/agent/model_factory.py +++ b/gns3server/agent/gns3_copilot/agent/model_factory.py @@ -32,8 +32,7 @@ Configuration is passed directly from the database. """ import logging -from typing import Any -from typing import Optional +from typing import Any, Optional from langchain.chat_models import init_chat_model diff --git a/gns3server/agent/gns3_copilot/agent_service.py b/gns3server/agent/gns3_copilot/agent_service.py index 32c71c981..be6ed201d 100644 --- a/gns3server/agent/gns3_copilot/agent_service.py +++ b/gns3server/agent/gns3_copilot/agent_service.py @@ -36,11 +36,7 @@ import json import logging import os from datetime import datetime -from typing import Any -from typing import AsyncGenerator -from typing import Dict -from typing import List -from typing import Optional +from typing import Any, AsyncGenerator, Dict, List, Optional from uuid import uuid4 import aiosqlite @@ -53,8 +49,6 @@ from gns3server.agent.gns3_copilot.chat_sessions_repository import ( ) from gns3server.agent.gns3_copilot.gns3_client.context_helpers import ( set_current_jwt_token, -) -from gns3server.agent.gns3_copilot.gns3_client.context_helpers import ( set_current_llm_config, ) from gns3server.agent.gns3_copilot.utils.error_handler import format_error_message diff --git a/gns3server/agent/gns3_copilot/chat_sessions_repository.py b/gns3server/agent/gns3_copilot/chat_sessions_repository.py index 41006f299..d263f35cf 100644 --- a/gns3server/agent/gns3_copilot/chat_sessions_repository.py +++ b/gns3server/agent/gns3_copilot/chat_sessions_repository.py @@ -33,10 +33,7 @@ checkpoint database. import json import logging from datetime import datetime -from typing import Any -from typing import Dict -from typing import List -from typing import Optional +from typing import Any, Dict, List, Optional import aiosqlite diff --git a/gns3server/agent/gns3_copilot/gns3_client/__init__.py b/gns3server/agent/gns3_copilot/gns3_client/__init__.py index 16fb366c5..326c0f886 100644 --- a/gns3server/agent/gns3_copilot/gns3_client/__init__.py +++ b/gns3server/agent/gns3_copilot/gns3_client/__init__.py @@ -47,14 +47,18 @@ The connector is adapted from the upstream gns3fy project from .api_handlers import build_gns3_ctx from .connector import Gns3Connector -from .connector_factory import get_gns3_connector -from .connector_factory import get_gns3_connector_with_llm_config -from .connector_factory import get_gns3_server_host -from .connector_factory import get_llm_config -from .context_helpers import get_current_jwt_token -from .context_helpers import get_current_llm_config -from .context_helpers import set_current_jwt_token -from .context_helpers import set_current_llm_config +from .connector_factory import ( + get_gns3_connector, + get_gns3_connector_with_llm_config, + get_gns3_server_host, + get_llm_config, +) +from .context_helpers import ( + get_current_jwt_token, + get_current_llm_config, + set_current_jwt_token, + set_current_llm_config, +) from .gns3_project_info import GNS3ProjectInfoTool from .gns3_topology_reader import GNS3TopologyTool diff --git a/gns3server/agent/gns3_copilot/gns3_client/api_handlers.py b/gns3server/agent/gns3_copilot/gns3_client/api_handlers.py index 7f6ca8c95..bce5ba091 100644 --- a/gns3server/agent/gns3_copilot/gns3_client/api_handlers.py +++ b/gns3server/agent/gns3_copilot/gns3_client/api_handlers.py @@ -47,16 +47,14 @@ Copilot-side callers build the context with :func:`build_gns3_ctx`, which pulls the request-scoped user JWT from the context variables. """ -from typing import Any -from concurrent.futures import ThreadPoolExecutor - import hashlib import logging - -from gns3server.services import access_ticket_service -from gns3server.services.access_tickets import DEFAULT_TICKET_TTL +from concurrent.futures import ThreadPoolExecutor +from typing import Any from gns3server.agent.gns3_copilot.gns3_client.connector import Gns3Connector +from gns3server.services import access_ticket_service +from gns3server.services.access_tickets import DEFAULT_TICKET_TTL log = logging.getLogger(__name__) diff --git a/gns3server/agent/gns3_copilot/gns3_client/connector_factory.py b/gns3server/agent/gns3_copilot/gns3_client/connector_factory.py index 32a4e2e45..0bf95f845 100644 --- a/gns3server/agent/gns3_copilot/gns3_client/connector_factory.py +++ b/gns3server/agent/gns3_copilot/gns3_client/connector_factory.py @@ -49,14 +49,13 @@ import logging from typing import Optional from uuid import UUID -from gns3server.agent.gns3_copilot.gns3_client.context_helpers import ( - get_current_jwt_token, -) - # Local imports from gns3server.agent.gns3_copilot.gns3_client.connector import ( Gns3Connector, ) +from gns3server.agent.gns3_copilot.gns3_client.context_helpers import ( + get_current_jwt_token, +) logger = logging.getLogger(__name__) diff --git a/gns3server/agent/gns3_copilot/project_agent_manager.py b/gns3server/agent/gns3_copilot/project_agent_manager.py index b9ade018a..7448a55b9 100644 --- a/gns3server/agent/gns3_copilot/project_agent_manager.py +++ b/gns3server/agent/gns3_copilot/project_agent_manager.py @@ -33,8 +33,7 @@ database. import asyncio import logging -from typing import Dict -from typing import Optional +from typing import Dict, Optional from gns3server.agent.gns3_copilot.agent_service import AgentService diff --git a/gns3server/agent/gns3_copilot/skills/__init__.py b/gns3server/agent/gns3_copilot/skills/__init__.py index 6dd522dca..891389d12 100644 --- a/gns3server/agent/gns3_copilot/skills/__init__.py +++ b/gns3server/agent/gns3_copilot/skills/__init__.py @@ -36,23 +36,23 @@ Directory Structure: - loader.py # SkillsLoader - YAML/Markdown file loading """ +from .loader import SkillsLoader +from .manager import SkillsManager from .registry import ( - SKILLS_REGISTRY, INJECTION_SKILLS_REGISTRY, - get_skill, - get_injection_skill, + SKILLS_REGISTRY, DeviceSkillsTool, InjectionSkillsTool, PacketAnalysisSkillsTool, - set_skills_manager, + get_injection_skill, + get_skill, get_skills_manager, - reload_injection_skills, - reload_forbidden_commands, - reload_skills_repository, get_skills_repository_info, + reload_forbidden_commands, + reload_injection_skills, + reload_skills_repository, + set_skills_manager, ) -from .manager import SkillsManager -from .loader import SkillsLoader __all__ = [ "INJECTION_SKILLS_REGISTRY", diff --git a/gns3server/agent/gns3_copilot/skills/manager.py b/gns3server/agent/gns3_copilot/skills/manager.py index 3195b4611..ef62562ce 100644 --- a/gns3server/agent/gns3_copilot/skills/manager.py +++ b/gns3server/agent/gns3_copilot/skills/manager.py @@ -33,7 +33,7 @@ including Git operations and hot reload of skills. import logging import os from pathlib import Path -from typing import Optional, Dict, Any +from typing import Any, Dict, Optional try: import git @@ -43,6 +43,7 @@ except ImportError: GIT_AVAILABLE = False from gns3server.config import Config + from .loader import SkillsLoader logger = logging.getLogger(__name__) diff --git a/gns3server/agent/gns3_copilot/skills/registry.py b/gns3server/agent/gns3_copilot/skills/registry.py index aa67cd51b..782215dd0 100644 --- a/gns3server/agent/gns3_copilot/skills/registry.py +++ b/gns3server/agent/gns3_copilot/skills/registry.py @@ -183,8 +183,8 @@ def reload_skills_repository() -> dict[str, Any]: protocols_ok = manager.reload_packet_analysis_protocols() # Reload forbidden commands (local import to avoid circular dependency) - from gns3server.agent.gns3_copilot.utils.command_filter import reload_forbidden_commands as _reload_fc from gns3server.agent.gns3_copilot.utils.command_filter import get_forbidden_commands + from gns3server.agent.gns3_copilot.utils.command_filter import reload_forbidden_commands as _reload_fc _reload_fc() forbidden_commands = get_forbidden_commands() @@ -300,8 +300,8 @@ def reload_forbidden_commands() -> dict[str, Any]: } """ try: - from gns3server.agent.gns3_copilot.utils.command_filter import reload_forbidden_commands as _reload from gns3server.agent.gns3_copilot.utils.command_filter import get_forbidden_commands + from gns3server.agent.gns3_copilot.utils.command_filter import reload_forbidden_commands as _reload _reload() commands = get_forbidden_commands() diff --git a/gns3server/agent/gns3_copilot/tools_v2/__init__.py b/gns3server/agent/gns3_copilot/tools_v2/__init__.py index aacf4423f..0818bd81c 100644 --- a/gns3server/agent/gns3_copilot/tools_v2/__init__.py +++ b/gns3server/agent/gns3_copilot/tools_v2/__init__.py @@ -58,8 +58,7 @@ from .gns3_create_link import GNS3LinkTool from .gns3_create_node import GNS3CreateNodeTool from .gns3_get_node_temp import GNS3TemplateTool from .gns3_packet_filter import GNS3PacketFilterTool -from .gns3_start_node import GNS3StartNodeQuickTool -from .gns3_start_node import GNS3StartNodeTool +from .gns3_start_node import GNS3StartNodeQuickTool, GNS3StartNodeTool from .gns3_stop_node import GNS3StopNodeTool from .gns3_suspend_node import GNS3SuspendNodeTool from .gns3_update_node_name import GNS3UpdateNodeNameTool diff --git a/gns3server/agent/gns3_copilot/tools_v2/config_tools_nornir.py b/gns3server/agent/gns3_copilot/tools_v2/config_tools_nornir.py index 250fb559d..79cd175f5 100644 --- a/gns3server/agent/gns3_copilot/tools_v2/config_tools_nornir.py +++ b/gns3server/agent/gns3_copilot/tools_v2/config_tools_nornir.py @@ -43,21 +43,18 @@ from langchain_core.callbacks import CallbackManagerForToolRun from netmiko.exceptions import ReadTimeout from nornir import InitNornir from nornir.core import Nornir -from nornir.core.task import AggregatedResult -from nornir.core.task import Result -from nornir.core.task import Task +from nornir.core.task import AggregatedResult, Result, Task from nornir_netmiko.tasks import netmiko_send_config from gns3server.agent.gns3_copilot.gns3_client import get_gns3_server_host -from gns3server.agent.gns3_copilot.utils import get_device_ports_from_topology -from gns3server.agent.gns3_copilot.utils.command_filter import ( - filter_forbidden_commands, -) # Import custom Netmiko device types for GNS3 emulation # This registers gns3_huawei_telnet_ce and other custom device types # NOTE: Must be imported BEFORE any Nornir operations to ensure device types are registered -from gns3server.agent.gns3_copilot.utils import custom_netmiko +from gns3server.agent.gns3_copilot.utils import custom_netmiko, get_device_ports_from_topology +from gns3server.agent.gns3_copilot.utils.command_filter import ( + filter_forbidden_commands, +) # Explicitly register custom device types to ensure they are available # This is a safety measure in case the auto-registration on import doesn't work diff --git a/gns3server/agent/gns3_copilot/tools_v2/display_tools_nornir.py b/gns3server/agent/gns3_copilot/tools_v2/display_tools_nornir.py index ab31e9978..428170794 100644 --- a/gns3server/agent/gns3_copilot/tools_v2/display_tools_nornir.py +++ b/gns3server/agent/gns3_copilot/tools_v2/display_tools_nornir.py @@ -43,21 +43,18 @@ from langchain_core.callbacks import CallbackManagerForToolRun from netmiko.exceptions import ReadTimeout from nornir import InitNornir from nornir.core import Nornir -from nornir.core.task import AggregatedResult -from nornir.core.task import Result -from nornir.core.task import Task +from nornir.core.task import AggregatedResult, Result, Task from nornir_netmiko.tasks import netmiko_multiline from gns3server.agent.gns3_copilot.gns3_client import get_gns3_server_host -from gns3server.agent.gns3_copilot.utils import get_device_ports_from_topology -from gns3server.agent.gns3_copilot.utils.command_filter import ( - filter_forbidden_commands, -) # Import custom Netmiko device types for GNS3 emulation # This registers gns3_huawei_telnet_ce and other custom device types # NOTE: Must be imported BEFORE any Nornir operations to ensure device types are registered -from gns3server.agent.gns3_copilot.utils import custom_netmiko +from gns3server.agent.gns3_copilot.utils import custom_netmiko, get_device_ports_from_topology +from gns3server.agent.gns3_copilot.utils.command_filter import ( + filter_forbidden_commands, +) # Explicitly register custom device types to ensure they are available # This is a safety measure in case the auto-registration on import doesn't work diff --git a/gns3server/agent/gns3_copilot/tools_v2/vpcs_tools_netmiko.py b/gns3server/agent/gns3_copilot/tools_v2/vpcs_tools_netmiko.py index 26a512dc5..e1ce51fa6 100644 --- a/gns3server/agent/gns3_copilot/tools_v2/vpcs_tools_netmiko.py +++ b/gns3server/agent/gns3_copilot/tools_v2/vpcs_tools_netmiko.py @@ -43,18 +43,15 @@ from langchain_core.callbacks import CallbackManagerForToolRun from netmiko.exceptions import ReadTimeout from nornir import InitNornir from nornir.core import Nornir -from nornir.core.task import AggregatedResult -from nornir.core.task import Result -from nornir.core.task import Task +from nornir.core.task import AggregatedResult, Result, Task from nornir_netmiko.tasks import netmiko_multiline from gns3server.agent.gns3_copilot.gns3_client import get_gns3_server_host -from gns3server.agent.gns3_copilot.utils import get_device_ports_from_topology # Import custom Netmiko device types for GNS3 emulation # This registers gns3_vpcs_telnet and other custom device types # NOTE: Must be imported BEFORE any Nornir operations to ensure device types are registered -from gns3server.agent.gns3_copilot.utils import custom_netmiko +from gns3server.agent.gns3_copilot.utils import custom_netmiko, get_device_ports_from_topology # Explicitly register VPCS device type to ensure it is available try: diff --git a/gns3server/agent/gns3_copilot/utils/__init__.py b/gns3server/agent/gns3_copilot/utils/__init__.py index 7beb1d745..59cfca0a7 100644 --- a/gns3server/agent/gns3_copilot/utils/__init__.py +++ b/gns3server/agent/gns3_copilot/utils/__init__.py @@ -39,9 +39,7 @@ Author: Yue Guobin (岳国宾) # Import main utility functions from .error_handler import format_error_message from .get_gns3_device_port import get_device_ports_from_topology -from .parse_tool_content import format_tool_response -from .parse_tool_content import normalize_tool_response -from .parse_tool_content import parse_tool_content +from .parse_tool_content import format_tool_response, normalize_tool_response, parse_tool_content # Dynamic version management try: diff --git a/gns3server/agent/gns3_copilot/utils/custom_netmiko/scripts/list_netmiko_telnet_devices.py b/gns3server/agent/gns3_copilot/utils/custom_netmiko/scripts/list_netmiko_telnet_devices.py index 3097dd954..de517249f 100644 --- a/gns3server/agent/gns3_copilot/utils/custom_netmiko/scripts/list_netmiko_telnet_devices.py +++ b/gns3server/agent/gns3_copilot/utils/custom_netmiko/scripts/list_netmiko_telnet_devices.py @@ -31,8 +31,8 @@ Features: import os import sys -from typing import Dict, List, Tuple from datetime import datetime +from typing import Dict, List, Tuple def get_netmiko_version() -> str: diff --git a/gns3server/agent/gns3_copilot/utils/custom_netmiko/tests/test_huawei_ce.py b/gns3server/agent/gns3_copilot/utils/custom_netmiko/tests/test_huawei_ce.py index 62343c3ef..4e231ae62 100644 --- a/gns3server/agent/gns3_copilot/utils/custom_netmiko/tests/test_huawei_ce.py +++ b/gns3server/agent/gns3_copilot/utils/custom_netmiko/tests/test_huawei_ce.py @@ -18,10 +18,10 @@ This script tests: Run with: python test_huawei_ce.py """ +import os import sys import unittest from unittest.mock import Mock, patch -import os # Add project root to path using relative path test_dir = os.path.dirname(os.path.abspath(__file__)) diff --git a/gns3server/agent/gns3_copilot/utils/custom_netmiko/tests/test_ruijie_telnet.py b/gns3server/agent/gns3_copilot/utils/custom_netmiko/tests/test_ruijie_telnet.py index adfbafea2..3ff869bf6 100644 --- a/gns3server/agent/gns3_copilot/utils/custom_netmiko/tests/test_ruijie_telnet.py +++ b/gns3server/agent/gns3_copilot/utils/custom_netmiko/tests/test_ruijie_telnet.py @@ -18,10 +18,10 @@ This script tests: Run with: python test_ruijie_telnet.py """ +import os import sys import unittest from unittest.mock import Mock, patch -import os # Add project root to path using relative path test_dir = os.path.dirname(os.path.abspath(__file__)) diff --git a/gns3server/agent/gns3_copilot/utils/custom_netmiko/tests/test_vpcs_telnet.py b/gns3server/agent/gns3_copilot/utils/custom_netmiko/tests/test_vpcs_telnet.py index f24565d85..36b173db7 100644 --- a/gns3server/agent/gns3_copilot/utils/custom_netmiko/tests/test_vpcs_telnet.py +++ b/gns3server/agent/gns3_copilot/utils/custom_netmiko/tests/test_vpcs_telnet.py @@ -20,10 +20,10 @@ This script tests: Run with: python test_vpcs_telnet.py """ +import os import sys import unittest -from unittest.mock import Mock, patch, MagicMock -import os +from unittest.mock import MagicMock, Mock, patch # Add project root to path using relative path test_dir = os.path.dirname(os.path.abspath(__file__)) diff --git a/gns3server/agent/gns3_copilot/utils/custom_netmiko/vpcs_telnet.py b/gns3server/agent/gns3_copilot/utils/custom_netmiko/vpcs_telnet.py index 0711f9c5d..863f52cdb 100644 --- a/gns3server/agent/gns3_copilot/utils/custom_netmiko/vpcs_telnet.py +++ b/gns3server/agent/gns3_copilot/utils/custom_netmiko/vpcs_telnet.py @@ -47,7 +47,6 @@ import time from netmiko.base_connection import BaseConnection - logger = logging.getLogger(__name__) # ANSI escape code pattern for stripping terminal formatting codes diff --git a/gns3server/agent/gns3_copilot/utils/gns3_drawing_utils.py b/gns3server/agent/gns3_copilot/utils/gns3_drawing_utils.py index 4fa48ee7d..50456cbf5 100644 --- a/gns3server/agent/gns3_copilot/utils/gns3_drawing_utils.py +++ b/gns3server/agent/gns3_copilot/utils/gns3_drawing_utils.py @@ -31,8 +31,7 @@ Supports ellipse and rectangle shapes for two-node annotations. """ import math -from typing import Any -from typing import Literal +from typing import Any, Literal # Default parameters DEFAULT_DEVICE_WIDTH = 50 diff --git a/gns3server/agent/gns3_copilot/utils/llm_config_helper.py b/gns3server/agent/gns3_copilot/utils/llm_config_helper.py index f0118c9d2..57e6658ae 100644 --- a/gns3server/agent/gns3_copilot/utils/llm_config_helper.py +++ b/gns3server/agent/gns3_copilot/utils/llm_config_helper.py @@ -43,9 +43,7 @@ Usage: """ import logging -from typing import Any -from typing import Dict -from typing import Optional +from typing import Any, Dict, Optional from uuid import UUID from fastapi import FastAPI diff --git a/gns3server/agent/gns3_copilot/utils/message_converters.py b/gns3server/agent/gns3_copilot/utils/message_converters.py index 367b2d956..eb0d9a825 100644 --- a/gns3server/agent/gns3_copilot/utils/message_converters.py +++ b/gns3server/agent/gns3_copilot/utils/message_converters.py @@ -31,14 +31,9 @@ Converts between LangChain messages and OpenAI-compatible format. import json import uuid -from typing import Any -from typing import Dict +from typing import Any, Dict -from langchain_core.messages import AIMessage -from langchain_core.messages import HumanMessage -from langchain_core.messages import SystemMessage -from langchain_core.messages import ToolCall -from langchain_core.messages import ToolMessage +from langchain_core.messages import AIMessage, HumanMessage, SystemMessage, ToolCall, ToolMessage def _ensure_string(content: Any) -> str: diff --git a/gns3server/agent/mcp/__init__.py b/gns3server/agent/mcp/__init__.py index e89babc77..f55cdfc6e 100644 --- a/gns3server/agent/mcp/__init__.py +++ b/gns3server/agent/mcp/__init__.py @@ -26,54 +26,65 @@ Implements the standard MCP protocol over SSE transport using FastMCP: Tools are registered via @mcp.tool() decorators. """ +import asyncio import contextvars import json -import asyncio import logging import socket -from uuid import UUID -import bcrypt -from typing import Any, Annotated +from typing import Annotated, Any from urllib.parse import parse_qs +from uuid import UUID +import bcrypt from fastapi import APIRouter from fastapi.responses import Response - -from pydantic import Field - from mcp.server.fastmcp import FastMCP from mcp.server.transport_security import TransportSecuritySettings - +from pydantic import Field from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession -from gns3server.config import Config -from gns3server.services.authentication import AuthService import gns3server.db.models as models -from gns3server.services import auth_service -from gns3server.utils.request_utils import extract_client_info +from gns3server.agent.gns3_copilot.gns3_client.api_handlers import ( + available_filters_handler, + create_link_handler, + create_node_handler, + delete_link_handler, + delete_node_file_handler, + delete_node_handler, + download_capture_file_handler, + duplicate_node_handler, + get_link_handler, + get_links_handler, + get_node_console_info_handler, + get_node_file_handler, + get_node_handler, + get_node_links_handler, + get_nodes_handler, + isolate_node_handler, + link_marker_handler, + list_node_files_handler, + marker_definition_handler, + reset_link_handler, + start_all_nodes_handler, + start_capture_handler, + start_node_handler, + stop_all_nodes_handler, + stop_capture_handler, + stop_node_handler, + suspend_all_nodes_handler, + suspend_node_handler, + unisolate_node_handler, + update_link_handler, + update_node_handler, + write_node_file_handler, +) +from gns3server.config import Config from gns3server.db.repositories.api_keys import ApiKeysRepository from gns3server.db.repositories.users import UsersRepository -from .projects import ( - list_projects_handler, - get_project_handler, - create_project_handler, - delete_project_handler, - open_project_handler, - close_project_handler, - get_project_stats_handler, - update_project_handler, - duplicate_project_handler, - get_project_readme_handler, - update_project_readme_handler, - lock_project_handler, - unlock_project_handler, - get_locked_project_handler, -) -from .server import ( - get_version_handler, - get_statistics_handler, -) +from gns3server.services import auth_service +from gns3server.services.authentication import AuthService +from gns3server.utils.request_utils import extract_client_info # Symbol tools are disabled for now: they require a vision-capable model to # be genuinely useful (the tools shuttle SVG content, which a text-only LLM @@ -84,80 +95,66 @@ from .server import ( # upload_symbol_handler, delete_symbol_handler, # ) from .appliances import ( - get_appliances_handler, get_appliance_handler, + get_appliances_handler, install_appliance_handler, ) -from .images import ( - get_images_handler, - get_image_handler, - delete_image_handler, - prune_images_handler, - install_images_handler, +from .computes import ( + get_compute_handler, + get_compute_images_handler, + list_computes_handler, ) from .device_config import ( device_config_send_handler, device_show_run_handler, vpcs_config_set_handler, ) -from gns3server.agent.gns3_copilot.gns3_client.api_handlers import ( - get_nodes_handler, - get_node_handler, - start_node_handler, - stop_node_handler, - suspend_node_handler, - create_node_handler, - delete_node_handler, - update_node_handler, - get_node_console_info_handler, - list_node_files_handler, - get_node_file_handler, - write_node_file_handler, - delete_node_file_handler, - start_all_nodes_handler, - stop_all_nodes_handler, - suspend_all_nodes_handler, - duplicate_node_handler, - isolate_node_handler, - unisolate_node_handler, - get_node_links_handler, - get_links_handler, - get_link_handler, - available_filters_handler, - create_link_handler, - delete_link_handler, - update_link_handler, - reset_link_handler, - start_capture_handler, - stop_capture_handler, - download_capture_file_handler, - link_marker_handler, - marker_definition_handler, +from .drawings import ( + create_drawing_handler, + delete_drawing_handler, + get_drawing_handler, + get_drawings_handler, + update_drawing_handler, ) -from .templates import ( - list_templates_handler, - get_template_handler, - create_template_handler, - update_template_handler, - delete_template_handler, +from .images import ( + delete_image_handler, + get_image_handler, + get_images_handler, + install_images_handler, + prune_images_handler, ) -from .computes import ( - list_computes_handler, - get_compute_handler, - get_compute_images_handler, +from .projects import ( + close_project_handler, + create_project_handler, + delete_project_handler, + duplicate_project_handler, + get_locked_project_handler, + get_project_handler, + get_project_readme_handler, + get_project_stats_handler, + list_projects_handler, + lock_project_handler, + open_project_handler, + unlock_project_handler, + update_project_handler, + update_project_readme_handler, +) +from .server import ( + get_statistics_handler, + get_version_handler, ) from .snapshots import ( - get_snapshots_handler, create_snapshot_handler, delete_snapshot_handler, + get_snapshots_handler, restore_snapshot_handler, ) -from .drawings import ( - get_drawings_handler, - create_drawing_handler, - get_drawing_handler, - update_drawing_handler, - delete_drawing_handler, +from .templates import ( + create_template_handler, + delete_template_handler, + get_template_handler, + list_templates_handler, + update_template_handler, ) log = logging.getLogger(__name__) diff --git a/gns3server/agent/mcp/appliances.py b/gns3server/agent/mcp/appliances.py index 6e3a3ecf5..35084ea6a 100644 --- a/gns3server/agent/mcp/appliances.py +++ b/gns3server/agent/mcp/appliances.py @@ -19,9 +19,8 @@ MCP tool handlers for GNS3 appliance management. """ -from typing import Any - import logging +from typing import Any log = logging.getLogger(__name__) diff --git a/gns3server/agent/mcp/computes.py b/gns3server/agent/mcp/computes.py index 1f744066c..722ed15ce 100644 --- a/gns3server/agent/mcp/computes.py +++ b/gns3server/agent/mcp/computes.py @@ -19,8 +19,8 @@ MCP tool handlers for GNS3 compute management. """ -from typing import Any import logging +from typing import Any log = logging.getLogger(__name__) diff --git a/gns3server/agent/mcp/device_config.py b/gns3server/agent/mcp/device_config.py index 278295071..be7f04356 100644 --- a/gns3server/agent/mcp/device_config.py +++ b/gns3server/agent/mcp/device_config.py @@ -33,7 +33,8 @@ import json import logging from typing import Any -from jinja2 import Template as JinjaTemplate, TemplateError as JinjaError +from jinja2 import Template as JinjaTemplate +from jinja2 import TemplateError as JinjaError log = logging.getLogger(__name__) diff --git a/gns3server/agent/mcp/drawings.py b/gns3server/agent/mcp/drawings.py index a3ca146f6..7ca6daeda 100644 --- a/gns3server/agent/mcp/drawings.py +++ b/gns3server/agent/mcp/drawings.py @@ -19,9 +19,8 @@ MCP tool handlers for GNS3 drawing management. """ -from typing import Any - import logging +from typing import Any log = logging.getLogger(__name__) diff --git a/gns3server/agent/mcp/images.py b/gns3server/agent/mcp/images.py index d2b7ed958..7d20bc2fa 100644 --- a/gns3server/agent/mcp/images.py +++ b/gns3server/agent/mcp/images.py @@ -19,9 +19,8 @@ MCP tool handlers for GNS3 image management. """ -from typing import Any - import logging +from typing import Any log = logging.getLogger(__name__) diff --git a/gns3server/agent/mcp/projects.py b/gns3server/agent/mcp/projects.py index ff6b5125c..57b9c01d3 100644 --- a/gns3server/agent/mcp/projects.py +++ b/gns3server/agent/mcp/projects.py @@ -22,9 +22,8 @@ Tool handlers receive (params, gns3_ctx) and call GNS3's REST API via Gns3Connector (from gns3_copilot.gns3_client.connector). """ -from typing import Any - import logging +from typing import Any log = logging.getLogger(__name__) diff --git a/gns3server/agent/mcp/server.py b/gns3server/agent/mcp/server.py index fe5c21dc3..0585a8566 100644 --- a/gns3server/agent/mcp/server.py +++ b/gns3server/agent/mcp/server.py @@ -19,9 +19,8 @@ MCP tool handlers for GNS3 server information. """ -from typing import Any - import logging +from typing import Any log = logging.getLogger(__name__) diff --git a/gns3server/agent/mcp/snapshots.py b/gns3server/agent/mcp/snapshots.py index 336c7a3e0..9f741a91e 100644 --- a/gns3server/agent/mcp/snapshots.py +++ b/gns3server/agent/mcp/snapshots.py @@ -19,9 +19,8 @@ MCP tool handlers for GNS3 snapshot management. """ -from typing import Any - import logging +from typing import Any log = logging.getLogger(__name__) diff --git a/gns3server/agent/mcp/symbols.py b/gns3server/agent/mcp/symbols.py index d363e042c..30f8f7d71 100644 --- a/gns3server/agent/mcp/symbols.py +++ b/gns3server/agent/mcp/symbols.py @@ -19,9 +19,8 @@ MCP tool handlers for GNS3 symbol management. """ -from typing import Any - import logging +from typing import Any from gns3server.services import access_ticket_service diff --git a/gns3server/agent/mcp/templates.py b/gns3server/agent/mcp/templates.py index 56e02680a..2a6130c78 100644 --- a/gns3server/agent/mcp/templates.py +++ b/gns3server/agent/mcp/templates.py @@ -22,9 +22,8 @@ Handlers receive (params, gns3_ctx) and call GNS3's REST API via Gns3Connector (from gns3_copilot.gns3_client.connector). """ -from typing import Any - import logging +from typing import Any log = logging.getLogger(__name__) diff --git a/gns3server/agent/web_wireshark/manage_wireshark.py b/gns3server/agent/web_wireshark/manage_wireshark.py index 5203a98d9..02445f477 100755 --- a/gns3server/agent/web_wireshark/manage_wireshark.py +++ b/gns3server/agent/web_wireshark/manage_wireshark.py @@ -67,12 +67,12 @@ For more information on each command, use: python manage_wireshark.py --help """ -import sys -import json import argparse -import logging import asyncio +import json +import logging import os +import sys from typing import Optional from gns3server.utils.uuid_validator import validate_uuid diff --git a/gns3server/agent/web_wireshark/manager.py b/gns3server/agent/web_wireshark/manager.py index 16f057d75..0791c0668 100644 --- a/gns3server/agent/web_wireshark/manager.py +++ b/gns3server/agent/web_wireshark/manager.py @@ -30,6 +30,7 @@ from urllib.parse import urlparse from gns3server.config import Config from gns3server.utils.port_allocator import link_id_to_display, link_id_to_port + from .docker_client import DockerHTTPClient logger = logging.getLogger(__name__) diff --git a/gns3server/agent/web_wireshark/setup_wireshark_image.py b/gns3server/agent/web_wireshark/setup_wireshark_image.py index 92949a923..01159fee3 100644 --- a/gns3server/agent/web_wireshark/setup_wireshark_image.py +++ b/gns3server/agent/web_wireshark/setup_wireshark_image.py @@ -23,12 +23,11 @@ This script pulls or builds the gns3/web-wireshark Docker image. Run with: pip install gns3server && gns3server-web-wireshark-setup """ +import argparse import os -import sys import shutil import subprocess -import argparse - +import sys DOCKER_IMAGE = "gns3/web-wireshark:latest" DOCKERFILE_NAME = "Dockerfile" diff --git a/gns3server/api/server.py b/gns3server/api/server.py index 7827aec7c..4ea037e83 100644 --- a/gns3server/api/server.py +++ b/gns3server/api/server.py @@ -21,36 +21,33 @@ FastAPI app from typing import cast -from fastapi import FastAPI, Request, HTTPException, status -from fastapi.middleware.cors import CORSMiddleware -from fastapi.responses import JSONResponse +from fastapi import FastAPI, HTTPException, Request, status from fastapi.exceptions import RequestValidationError -from fastapi.staticfiles import StaticFiles -from sqlalchemy.exc import SQLAlchemyError -from uvicorn.main import Server as UvicornServer - +from fastapi.middleware.cors import CORSMiddleware from fastapi.openapi.docs import ( get_redoc_html, get_swagger_ui_html, get_swagger_ui_oauth2_redirect_html, ) - -from gns3server.controller.controller_error import ( - ControllerError, - ControllerNotFoundError, - ControllerBadRequestError, - ControllerTimeoutError, - ControllerForbiddenError, - ControllerUnauthorizedError, - ComputeConflictError, -) - -from gns3server.api.routes import controller, index -from gns3server.api.routes.compute import compute_api -from gns3server.core import tasks +from fastapi.responses import JSONResponse +from fastapi.staticfiles import StaticFiles +from sqlalchemy.exc import SQLAlchemyError +from uvicorn.main import Server as UvicornServer # MCP is an optional feature — import only if dependencies are installed from gns3server.agent import MCP_AVAILABLE +from gns3server.api.routes import controller, index +from gns3server.api.routes.compute import compute_api +from gns3server.controller.controller_error import ( + ComputeConflictError, + ControllerBadRequestError, + ControllerError, + ControllerForbiddenError, + ControllerNotFoundError, + ControllerTimeoutError, + ControllerUnauthorizedError, +) +from gns3server.core import tasks if MCP_AVAILABLE: from gns3server.agent import mcp diff --git a/gns3server/compute/notification_manager.py b/gns3server/compute/notification_manager.py index f4eecf76c..5a4e5d603 100644 --- a/gns3server/compute/notification_manager.py +++ b/gns3server/compute/notification_manager.py @@ -17,6 +17,7 @@ import asyncio from contextlib import contextmanager + from gns3server.utils.notification_queue import NotificationQueue diff --git a/gns3server/compute/project_manager.py b/gns3server/compute/project_manager.py index 91f4757b4..1bcbae019 100644 --- a/gns3server/compute/project_manager.py +++ b/gns3server/compute/project_manager.py @@ -15,14 +15,15 @@ # along with this program. If not, see . -import psutil +import logging import platform -from .project import Project from uuid import UUID +import psutil + from gns3server.compute.compute_error import ComputeError, ComputeNotFoundError -import logging +from .project import Project log = logging.getLogger(__name__) diff --git a/gns3server/compute/ubridge/hypervisor.py b/gns3server/compute/ubridge/hypervisor.py index addc5cc9f..66dddd99a 100644 --- a/gns3server/compute/ubridge/hypervisor.py +++ b/gns3server/compute/ubridge/hypervisor.py @@ -18,20 +18,19 @@ Represents a uBridge hypervisor and starts/stops the associated uBridge process. """ +import asyncio +import logging import os +import re import socket import subprocess -import asyncio import tempfile -import re from gns3server.utils import parse_version -from gns3server.utils.asyncio import wait_for_process_termination -from gns3server.utils.asyncio import subprocess_check_output -from .ubridge_hypervisor import UBridgeHypervisor -from .ubridge_error import UbridgeError +from gns3server.utils.asyncio import subprocess_check_output, wait_for_process_termination -import logging +from .ubridge_error import UbridgeError +from .ubridge_hypervisor import UBridgeHypervisor log = logging.getLogger(__name__) diff --git a/gns3server/compute/ubridge/ubridge_hypervisor.py b/gns3server/compute/ubridge/ubridge_hypervisor.py index b03752b69..6256f0142 100644 --- a/gns3server/compute/ubridge/ubridge_hypervisor.py +++ b/gns3server/compute/ubridge/ubridge_hypervisor.py @@ -14,12 +14,13 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import asyncio +import logging import re import time -import logging -import asyncio from gns3server.utils.asyncio import locking + from .ubridge_error import UbridgeError log = logging.getLogger(__name__) diff --git a/gns3server/crash_report.py b/gns3server/crash_report.py index cba709469..5d407db4a 100644 --- a/gns3server/crash_report.py +++ b/gns3server/crash_report.py @@ -23,17 +23,17 @@ except ImportError: # Sentry SDK is not installed with deb package in order to simplify packaging SENTRY_SDK_AVAILABLE = False -import os -import sys -import struct -import platform import locale +import logging +import os +import platform +import struct +import sys + import distro -from .version import __version__, __version_info__ from .config import Config - -import logging +from .version import __version__, __version_info__ log = logging.getLogger(__name__)