mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-09-12 21:15:27 +03:00
feat(copilot): replace LangGraph config with request-scoped context variables
- Refactor `llm_call` and `generate_title` nodes to retrieve `llm_config` from request-scoped context variable instead of LangGraph config - Remove `jwt_token` and `llm_config` from LangGraph configurable parameters in `AgentService.stream` - Add `set_current_llm_config` and `get_current_llm_config` functions to `connector_factory` and export them in `__init__.py` - Update `tool_node` to no longer extract `jwt_token` from config as it is now handled via context variable - Improves thread safety and decouples configuration from LangGraph's state management
This commit is contained in:
parent
b05e6a71b4
commit
eec4ebe3fd
@ -128,10 +128,17 @@ class MessagesState(TypedDict):
|
|||||||
def llm_call(state: dict, config: RunnableConfig | None = None):
|
def llm_call(state: dict, config: RunnableConfig | None = None):
|
||||||
"""LLM decides whether to call a tool or not"""
|
"""LLM decides whether to call a tool or not"""
|
||||||
|
|
||||||
# Extract config from LangGraph config
|
# Get llm_config from request-scoped context variable
|
||||||
configurable = config.get("configurable", {}) if config else {}
|
from gns3server.agent.gns3_copilot.gns3_client import get_current_llm_config
|
||||||
jwt_token = configurable.get("jwt_token")
|
llm_config = get_current_llm_config()
|
||||||
llm_config = configurable.get("llm_config")
|
|
||||||
|
if not llm_config:
|
||||||
|
logger.error("LLM config not found in context")
|
||||||
|
return {
|
||||||
|
"messages": [],
|
||||||
|
"llm_calls": state.get("llm_calls", 0),
|
||||||
|
"topology_info": None,
|
||||||
|
}
|
||||||
|
|
||||||
# Defensive check: skip LLM call if no user messages
|
# Defensive check: skip LLM call if no user messages
|
||||||
messages = state.get("messages", [])
|
messages = state.get("messages", [])
|
||||||
@ -228,9 +235,13 @@ def generate_title(state: MessagesState, config: RunnableConfig | None = None) -
|
|||||||
This node is only executed when no title has been set yet (first round only).
|
This node is only executed when no title has been set yet (first round only).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Extract config from LangGraph config
|
# Get llm_config from request-scoped context variable
|
||||||
configurable = config.get("configurable", {}) if config else {}
|
from gns3server.agent.gns3_copilot.gns3_client import get_current_llm_config
|
||||||
llm_config = configurable.get("llm_config")
|
llm_config = get_current_llm_config()
|
||||||
|
|
||||||
|
if not llm_config:
|
||||||
|
logger.error("LLM config not found in context, cannot generate title")
|
||||||
|
return {"conversation_title": UNTITLED_SESSION_FALLBACK}
|
||||||
|
|
||||||
# Only generate a title if it hasn't been set yet
|
# Only generate a title if it hasn't been set yet
|
||||||
current_title = state.get("conversation_title")
|
current_title = state.get("conversation_title")
|
||||||
@ -306,15 +317,6 @@ def generate_title(state: MessagesState, config: RunnableConfig | None = None) -
|
|||||||
def tool_node(state: dict, config: RunnableConfig | None = None):
|
def tool_node(state: dict, config: RunnableConfig | None = None):
|
||||||
"""Performs the tool call"""
|
"""Performs the tool call"""
|
||||||
|
|
||||||
# Extract jwt_token from config for tools to use
|
|
||||||
configurable = config.get("configurable", {}) if config else {}
|
|
||||||
jwt_token = configurable.get("jwt_token")
|
|
||||||
|
|
||||||
# Set jwt_token for connector_factory to access
|
|
||||||
if jwt_token:
|
|
||||||
from gns3server.agent.gns3_copilot.gns3_client import set_current_jwt_token
|
|
||||||
set_current_jwt_token(jwt_token)
|
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
for tool_call in state["messages"][-1].tool_calls:
|
for tool_call in state["messages"][-1].tool_calls:
|
||||||
tool = tools_by_name[tool_call["name"]]
|
tool = tools_by_name[tool_call["name"]]
|
||||||
|
|||||||
@ -123,12 +123,18 @@ class AgentService:
|
|||||||
Yields:
|
Yields:
|
||||||
Dict containing SSE-compatible response chunks
|
Dict containing SSE-compatible response chunks
|
||||||
"""
|
"""
|
||||||
# Build config with LLM configuration and metadata
|
# Set request-scoped context variables (memory-only, not persisted)
|
||||||
|
if jwt_token:
|
||||||
|
from gns3server.agent.gns3_copilot.gns3_client import set_current_jwt_token
|
||||||
|
set_current_jwt_token(jwt_token)
|
||||||
|
if llm_config:
|
||||||
|
from gns3server.agent.gns3_copilot.gns3_client import set_current_llm_config
|
||||||
|
set_current_llm_config(llm_config)
|
||||||
|
|
||||||
|
# Build config - only thread-safe identifiers
|
||||||
config = {
|
config = {
|
||||||
"configurable": {
|
"configurable": {
|
||||||
"thread_id": session_id,
|
"thread_id": session_id,
|
||||||
"jwt_token": jwt_token,
|
|
||||||
"llm_config": llm_config,
|
|
||||||
},
|
},
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"user_id": user_id,
|
"user_id": user_id,
|
||||||
|
|||||||
@ -27,6 +27,8 @@ from .connector_factory import (
|
|||||||
get_llm_config,
|
get_llm_config,
|
||||||
set_current_jwt_token,
|
set_current_jwt_token,
|
||||||
get_current_jwt_token,
|
get_current_jwt_token,
|
||||||
|
set_current_llm_config,
|
||||||
|
get_current_llm_config,
|
||||||
)
|
)
|
||||||
from .custom_gns3fy import (
|
from .custom_gns3fy import (
|
||||||
CONSOLE_TYPES,
|
CONSOLE_TYPES,
|
||||||
@ -66,4 +68,6 @@ __all__ = [
|
|||||||
"get_llm_config",
|
"get_llm_config",
|
||||||
"set_current_jwt_token",
|
"set_current_jwt_token",
|
||||||
"get_current_jwt_token",
|
"get_current_jwt_token",
|
||||||
|
"set_current_llm_config",
|
||||||
|
"get_current_llm_config",
|
||||||
]
|
]
|
||||||
|
|||||||
@ -30,9 +30,10 @@ import logging
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
from contextvars import ContextVar
|
from contextvars import ContextVar
|
||||||
|
|
||||||
# Context variable for request-scoped jwt_token
|
# Context variables for request-scoped data
|
||||||
# Automatically cleaned up when request context ends
|
# Automatically cleaned up when request context ends
|
||||||
_jwt_token_context: ContextVar[Optional[str]] = ContextVar("_jwt_token_context", default=None)
|
_jwt_token_context: ContextVar[Optional[str]] = ContextVar("_jwt_token_context", default=None)
|
||||||
|
_llm_config_context: ContextVar[Optional[dict]] = ContextVar("_llm_config_context", default=None)
|
||||||
|
|
||||||
|
|
||||||
def set_current_jwt_token(token: str) -> None:
|
def set_current_jwt_token(token: str) -> None:
|
||||||
@ -43,6 +44,16 @@ def set_current_jwt_token(token: str) -> None:
|
|||||||
def get_current_jwt_token() -> Optional[str]:
|
def get_current_jwt_token() -> Optional[str]:
|
||||||
"""Get the JWT token for the current request context."""
|
"""Get the JWT token for the current request context."""
|
||||||
return _jwt_token_context.get()
|
return _jwt_token_context.get()
|
||||||
|
|
||||||
|
|
||||||
|
def set_current_llm_config(config: dict) -> None:
|
||||||
|
"""Set the LLM config for the current request context."""
|
||||||
|
_llm_config_context.set(config)
|
||||||
|
|
||||||
|
|
||||||
|
def get_current_llm_config() -> Optional[dict]:
|
||||||
|
"""Get the LLM config for the current request context."""
|
||||||
|
return _llm_config_context.get()
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from gns3server.agent.gns3_copilot.gns3_client.custom_gns3fy import Gns3Connector
|
from gns3server.agent.gns3_copilot.gns3_client.custom_gns3fy import Gns3Connector
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user