YueGuobin e68cc17ed5
feat: add fault injection system and external skills repository
## Summary

Add a complete fault injection system for GNS3 Copilot, migrate all
skills from local Python files to an external Git repository with
hot reload support, and restructure Copilot API under /copilot/.

## Key Changes

### Fault Injection
- New troubleshooting_injection mode with InjectionSkillsTool
- 368 fault scenarios across 39 protocol categories
- Context-based filtering (LLM must pass topology protocols)

### External Skills Repository
- SkillsManager: Git clone/pull, version tracking, smart updates
- SkillsLoader: YAML skills + Markdown prompts from external repo
- Hot reload via POST /copilot/reload/skills
- Configurable via gns3_server.conf

### Architecture
- API unified under /copilot/ prefix
- SkillsManager moved from Controller to agent module
- Lazy initialization with startup background preload
- Per-command Git timeout, smart update checks
- Forbidden commands hot-reloadable from external repo
- 32 INFO logs downgraded to DEBUG
2026-05-11 01:24:55 +08:00

195 lines
4.6 KiB
Python

#
# Copyright (C) 2020 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from fastapi import APIRouter, Depends, HTTPException, status
# Check AI Copilot availability
from gns3server.agent import AI_COPILOT_AVAILABLE
# Conditionally import AI-dependent routes
if AI_COPILOT_AVAILABLE:
from . import chat
from . import copilot
from . import llm_model_configs
_chat_router = chat.router
_copilot_router = copilot.router
_llm_router = llm_model_configs.router
else:
# Create stub routers that return 501 for all AI endpoints
_chat_router = APIRouter()
_copilot_router = APIRouter()
_llm_router = APIRouter()
@_chat_router.api_route("/{path:path}", methods=["GET", "POST", "DELETE", "PATCH", "PUT"])
@_copilot_router.api_route("/{path:path}", methods=["GET", "POST", "DELETE", "PATCH", "PUT"])
@_llm_router.api_route("/{path:path}", methods=["GET", "POST", "DELETE", "PATCH", "PUT"])
async def ai_not_available(path: str = ""):
raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED,
detail="AI Copilot is not available. Install AI dependencies with: pip install gns3-server[ai-copilot]"
)
from . import controller
from . import appliances
from . import computes
from . import drawings
from . import gns3vm
from . import links
from . import nodes
from . import projects
from . import snapshots
from . import symbols
from . import templates
from . import images
from . import users
from . import groups
from . import roles
from . import acl
from . import pools
from . import privileges
from .dependencies.authentication import get_current_active_user
router = APIRouter()
router.include_router(
controller.router,
tags=["Controller"]
)
router.include_router(
users.router,
prefix="/access/users",
tags=["Users"]
)
router.include_router(
groups.router,
prefix="/access/groups",
tags=["Users groups"]
)
router.include_router(
roles.router,
prefix="/access/roles",
tags=["Roles"]
)
router.include_router(
privileges.router,
dependencies=[Depends(get_current_active_user)],
prefix="/access/privileges",
tags=["Privileges"]
)
router.include_router(
acl.router,
prefix="/access/acl",
tags=["ACL"]
)
router.include_router(
images.router,
prefix="/images",
tags=["Images"]
)
router.include_router(
templates.router,
prefix="/templates",
tags=["Templates"]
)
router.include_router(
projects.router,
prefix="/projects",
tags=["Projects"])
router.include_router(
nodes.router,
prefix="/projects/{project_id}/nodes",
tags=["Nodes"]
)
router.include_router(
links.router,
prefix="/projects/{project_id}/links",
tags=["Links"]
)
router.include_router(
drawings.router,
prefix="/projects/{project_id}/drawings",
tags=["Drawings"])
router.include_router(
symbols.router,
prefix="/symbols", tags=["Symbols"]
)
router.include_router(
snapshots.router,
prefix="/projects/{project_id}/snapshots",
tags=["Snapshots"])
router.include_router(
computes.router,
dependencies=[Depends(get_current_active_user)],
prefix="/computes",
tags=["Computes"]
)
router.include_router(
appliances.router,
prefix="/appliances",
tags=["Appliances"]
)
router.include_router(
pools.router,
prefix="/pools",
tags=["Resource pools"]
)
router.include_router(
gns3vm.router,
dependencies=[Depends(get_current_active_user)],
deprecated=True,
prefix="/gns3vm",
tags=["GNS3 VM"]
)
router.include_router(
_llm_router,
prefix="/access",
dependencies=[Depends(get_current_active_user)],
tags=["LLM Model Configurations"]
)
router.include_router(
_copilot_router,
prefix="/copilot",
dependencies=[Depends(get_current_active_user)],
tags=["GNS3 Copilot"]
)
router.include_router(
_chat_router,
prefix="/copilot/projects/{project_id}/chat",
dependencies=[Depends(get_current_active_user)],
tags=["GNS3 Copilot"]
)