mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-06 07:11:13 +02:00
Merge branch '3.1' into mypy-agent-copilot-tools-nornir
This commit is contained in:
24 files changed
+208
-187
No files matched your search
@@ -140,7 +140,7 @@ def estimate_tool_tokens(tools: list[Any]) -> int:
|
||||
for tool in tools:
|
||||
try:
|
||||
# Build tool schema in OpenAI format
|
||||
tool_schema = {
|
||||
tool_schema: dict[str, Any] = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": tool.name,
|
||||
|
||||
@@ -110,7 +110,7 @@ def create_base_model(
|
||||
|
||||
try:
|
||||
# Prepare parameters for init_chat_model
|
||||
init_params = {
|
||||
init_params: dict[str, Any] = {
|
||||
"model": config_vars["model_name"],
|
||||
"model_provider": config_vars["model_provider"],
|
||||
"api_key": config_vars["api_key"],
|
||||
@@ -177,7 +177,7 @@ def create_title_model(
|
||||
|
||||
try:
|
||||
# Prepare parameters for init_chat_model
|
||||
init_params = {
|
||||
init_params: dict[str, Any] = {
|
||||
"model": config_vars["model_name"],
|
||||
"model_provider": config_vars["model_provider"],
|
||||
"api_key": config_vars["api_key"],
|
||||
|
||||
@@ -256,17 +256,16 @@ class AgentService:
|
||||
)
|
||||
|
||||
# Ensure checkpointer is initialized
|
||||
if not self._checkpointer_conn:
|
||||
await self._get_checkpointer()
|
||||
checkpointer = await self._get_checkpointer()
|
||||
|
||||
# Get or create chat session
|
||||
repo = ChatSessionsRepository(self._checkpointer_conn)
|
||||
repo = ChatSessionsRepository(checkpointer.conn)
|
||||
session = await repo.get_session_by_thread(session_id)
|
||||
is_new_session = session is None
|
||||
|
||||
if is_new_session:
|
||||
# Create new session
|
||||
copilot_mode = llm_config.get("copilot_mode", "teaching_assistant").lower()
|
||||
copilot_mode = (llm_config or {}).get("copilot_mode", "teaching_assistant").lower()
|
||||
session = await repo.create_session(
|
||||
thread_id=session_id,
|
||||
user_id=user_id or "",
|
||||
@@ -290,7 +289,7 @@ class AgentService:
|
||||
|
||||
# Build config - only thread-safe identifiers
|
||||
# Determine recursion_limit based on copilot_mode
|
||||
copilot_mode = llm_config.get("copilot_mode", "teaching_assistant").lower()
|
||||
copilot_mode = (llm_config or {}).get("copilot_mode", "teaching_assistant").lower()
|
||||
if copilot_mode == "troubleshooting_injection":
|
||||
recursion_limit = 100 # Need more recursion depth for fault injection workflow
|
||||
log.debug("Using extended recursion_limit for troubleshooting_injection mode: 100")
|
||||
@@ -453,10 +452,10 @@ class AgentService:
|
||||
yield chunk
|
||||
else:
|
||||
# Use stateless converter for other events
|
||||
chunk = self._convert_event_to_chunk(event, session_id)
|
||||
if chunk:
|
||||
log.debug("Yielding chunk: type=%s", chunk.get("type"))
|
||||
yield chunk
|
||||
converted = self._convert_event_to_chunk(event, session_id)
|
||||
if converted:
|
||||
log.debug("Yielding chunk: type=%s", converted.get("type"))
|
||||
yield converted
|
||||
|
||||
# Check if stream was aborted and yield tool_end events for aborted tools
|
||||
from gns3server.agent.gns3_copilot.agent.gns3_copilot import (
|
||||
@@ -624,10 +623,9 @@ class AgentService:
|
||||
Returns:
|
||||
List of session dictionaries
|
||||
"""
|
||||
if not self._checkpointer_conn:
|
||||
await self._get_checkpointer()
|
||||
checkpointer = await self._get_checkpointer()
|
||||
|
||||
repo = ChatSessionsRepository(self._checkpointer_conn)
|
||||
repo = ChatSessionsRepository(checkpointer.conn)
|
||||
sessions = await repo.list_sessions(user_id=user_id, copilot_mode=copilot_mode, limit=limit)
|
||||
return [s.to_dict() for s in sessions]
|
||||
|
||||
@@ -641,10 +639,9 @@ class AgentService:
|
||||
Returns:
|
||||
True if deleted, False if not found
|
||||
"""
|
||||
if not self._checkpointer_conn:
|
||||
await self._get_checkpointer()
|
||||
checkpointer = await self._get_checkpointer()
|
||||
|
||||
repo = ChatSessionsRepository(self._checkpointer_conn)
|
||||
repo = ChatSessionsRepository(checkpointer.conn)
|
||||
return await repo.delete_session(session_id)
|
||||
|
||||
async def rename_session(self, session_id: str, new_title: str) -> Optional[Dict[str, Any]]:
|
||||
@@ -658,10 +655,9 @@ class AgentService:
|
||||
Returns:
|
||||
Updated session dictionary or None
|
||||
"""
|
||||
if not self._checkpointer_conn:
|
||||
await self._get_checkpointer()
|
||||
checkpointer = await self._get_checkpointer()
|
||||
|
||||
repo = ChatSessionsRepository(self._checkpointer_conn)
|
||||
repo = ChatSessionsRepository(checkpointer.conn)
|
||||
session = await repo.update_session(thread_id=session_id, title=new_title)
|
||||
return session.to_dict() if session else None
|
||||
|
||||
@@ -676,10 +672,9 @@ class AgentService:
|
||||
Returns:
|
||||
Updated session dictionary or None
|
||||
"""
|
||||
if not self._checkpointer_conn:
|
||||
await self._get_checkpointer()
|
||||
checkpointer = await self._get_checkpointer()
|
||||
|
||||
repo = ChatSessionsRepository(self._checkpointer_conn)
|
||||
repo = ChatSessionsRepository(checkpointer.conn)
|
||||
session = await repo.pin_session(thread_id=session_id, pinned=pinned)
|
||||
return session.to_dict() if session else None
|
||||
|
||||
|
||||
@@ -347,7 +347,7 @@ def get_gns3_server_host() -> str:
|
||||
host = get_gns3_server_host()
|
||||
print(f"GNS3 server host: {host}")
|
||||
"""
|
||||
url = _detect_url_for_api()
|
||||
url = _detect_url_for_api() or DEFAULT_GNS3_URL
|
||||
|
||||
# Extract host from URL
|
||||
# URL format: protocol://host:port
|
||||
|
||||
@@ -49,7 +49,7 @@ def build_nodes_inventory(nodes: list[dict[str, Any]], server_host: str | None)
|
||||
"""
|
||||
inventory: dict[str, Any] = {}
|
||||
for n in nodes:
|
||||
inventory[n.get("name")] = {
|
||||
inventory[n.get("name") or ""] = {
|
||||
"server": server_host,
|
||||
"name": n.get("name"),
|
||||
"node_id": n.get("node_id"),
|
||||
@@ -68,7 +68,7 @@ def build_nodes_inventory(nodes: list[dict[str, Any]], server_host: str | None)
|
||||
return inventory
|
||||
|
||||
|
||||
def build_links_summary(nodes: list[dict[str, Any]], links: list[dict[str, Any]]) -> list[dict[str, str]]:
|
||||
def build_links_summary(nodes: list[dict[str, Any]], links: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
"""
|
||||
Build a human/LLM-friendly link list resolving node and port names.
|
||||
|
||||
@@ -76,7 +76,7 @@ def build_links_summary(nodes: list[dict[str, Any]], links: list[dict[str, Any]]
|
||||
Links whose endpoints cannot be resolved are skipped, mirroring the
|
||||
original Project.links_summary behavior.
|
||||
"""
|
||||
summary: list[dict[str, str]] = []
|
||||
summary: list[dict[str, Any]] = []
|
||||
for link in links:
|
||||
if not link.get("nodes"):
|
||||
continue
|
||||
|
||||
@@ -248,7 +248,7 @@ class SkillsLoader:
|
||||
continue
|
||||
# Use device_type from YAML content as the key
|
||||
# Fallback to filename stem if device_type not present
|
||||
skill_key = skill_data.get("device_type") if isinstance(skill_data, dict) else None
|
||||
skill_key = skill_data.get("device_type")
|
||||
if not skill_key:
|
||||
skill_key = yaml_file.stem
|
||||
logger.warning(f"No device_type in {yaml_file}, using filename '{skill_key}' as key")
|
||||
|
||||
@@ -37,8 +37,10 @@ from typing import Optional, Dict, Any
|
||||
|
||||
try:
|
||||
import git
|
||||
|
||||
GIT_AVAILABLE = True
|
||||
except ImportError:
|
||||
git = None
|
||||
GIT_AVAILABLE = False
|
||||
|
||||
from gns3server.config import Config
|
||||
from .loader import SkillsLoader
|
||||
@@ -66,7 +68,7 @@ class SkillsManager:
|
||||
- Version tracking
|
||||
"""
|
||||
|
||||
def __init__(self, repo_url: str = None, branch: str = "main", auto_update: bool = False):
|
||||
def __init__(self, repo_url: Optional[str] = None, branch: str = "main", auto_update: bool = False):
|
||||
"""
|
||||
Initialize the skills manager.
|
||||
|
||||
@@ -90,7 +92,7 @@ class SkillsManager:
|
||||
self._repo: Optional["git.Repo"] = None
|
||||
self._prompt_count = 0
|
||||
|
||||
if git is None:
|
||||
if not GIT_AVAILABLE:
|
||||
logger.warning("GitPython is not installed. Skills management features will be limited.")
|
||||
|
||||
def initialize(self) -> bool:
|
||||
@@ -134,7 +136,7 @@ class SkillsManager:
|
||||
- Up to date → nothing
|
||||
- Network error → use existing files, log warning
|
||||
"""
|
||||
if git is None or self._repo is None:
|
||||
if not GIT_AVAILABLE or self._repo is None:
|
||||
return
|
||||
|
||||
# Check for uncommitted changes
|
||||
@@ -173,7 +175,7 @@ class SkillsManager:
|
||||
Returns:
|
||||
True if successful, False otherwise
|
||||
"""
|
||||
if git is None:
|
||||
if not GIT_AVAILABLE:
|
||||
logger.error("GitPython is not installed. Cannot clone repository.")
|
||||
return False
|
||||
|
||||
@@ -347,7 +349,7 @@ class SkillsManager:
|
||||
Returns:
|
||||
Commit hash as string, or empty string if not available
|
||||
"""
|
||||
if git is None or self._repo is None:
|
||||
if not GIT_AVAILABLE or self._repo is None:
|
||||
try:
|
||||
self._repo = git.Repo(self.local_path)
|
||||
except Exception:
|
||||
@@ -409,7 +411,7 @@ class SkillsManager:
|
||||
Returns:
|
||||
True if successful, False otherwise
|
||||
"""
|
||||
if git is None:
|
||||
if not GIT_AVAILABLE:
|
||||
logger.error("GitPython is not installed. Cannot rollback.")
|
||||
return False
|
||||
|
||||
@@ -436,7 +438,7 @@ class SkillsManager:
|
||||
Returns:
|
||||
List of commit information dictionaries
|
||||
"""
|
||||
if git is None:
|
||||
if not GIT_AVAILABLE:
|
||||
return []
|
||||
|
||||
try:
|
||||
|
||||
Reference in new issue
Block a user