3131from deepagents import create_deep_agent
3232from django .db .models import QuerySet
3333from django .http import StreamingHttpResponse
34- from langchain_core .messages import BaseMessageChunk , BaseMessage , ToolMessage , AIMessageChunk
34+ from langchain_core .messages import BaseMessageChunk , BaseMessage , ToolMessage , AIMessageChunk , SystemMessage
3535from langchain_mcp_adapters .client import MultiServerMCPClient
3636from langgraph .checkpoint .memory import MemorySaver
3737
@@ -410,11 +410,45 @@ async def _yield_mcp_response(chat_model, message_list, mcp_servers, mcp_output_
410410 if extra_tools :
411411 for tool in extra_tools :
412412 tools .append (tool )
413+
414+ # ---------------------------------------------------------------------------
415+ # Fix: vLLM (and Qwen chat templates) reject conversations that contain more
416+ # than one SystemMessage, or a SystemMessage that is not the very first
417+ # message. create_deep_agent always prepends its own BASE_AGENT_PROMPT as a
418+ # SystemMessage before calling the model (factory.py line ~1319). If
419+ # message_list already contains a SystemMessage (built in base_chat_node.py
420+ # via generate_message_list), the API receives two system messages and raises
421+ # "System message must be at the beginning."
422+ #
423+ # Solution: strip the user-supplied SystemMessage out of message_list and
424+ # pass its text as the system_prompt argument of create_deep_agent.
425+ # deepagents will then merge it with BASE_AGENT_PROMPT into a single
426+ # combined system message, so the model only ever sees one.
427+ # ---------------------------------------------------------------------------
428+ user_system_prompt = None
429+ filtered_message_list = []
430+ for msg in message_list :
431+ if isinstance (msg , SystemMessage ):
432+ # Normalise content to plain string regardless of whether the
433+ # message was built with a str or a list of content blocks.
434+ if isinstance (msg .content , str ):
435+ user_system_prompt = msg .content
436+ elif isinstance (msg .content , list ):
437+ user_system_prompt = '' .join (
438+ item .get ('text' , '' ) if isinstance (item , dict ) else str (item )
439+ for item in msg .content
440+ )
441+ else :
442+ user_system_prompt = str (msg .content )
443+ else :
444+ filtered_message_list .append (msg )
445+
413446 agent = create_deep_agent (
414447 model = chat_model ,
415448 backend = SandboxShellBackend (root_dir = temp_dir , virtual_mode = True ),
416449 skills = ['/skills' ],
417450 tools = tools ,
451+ system_prompt = user_system_prompt ,
418452 interrupt_on = {
419453 "write_file" : False ,
420454 "read_file" : False ,
@@ -425,7 +459,7 @@ async def _yield_mcp_response(chat_model, message_list, mcp_servers, mcp_output_
425459 recursion_limit = int (CONFIG .get (
426460 "LANGCHAIN_GRAPH_RECURSION_LIMIT" , '100' ))
427461 response = agent .astream (
428- {"messages" : message_list },
462+ {"messages" : filtered_message_list },
429463 config = {"recursion_limit" : recursion_limit ,
430464 "configurable" : {"thread_id" : chat_id }},
431465 stream_mode = 'messages'
0 commit comments