Skip to content

[BUG] Duplicate DOM ids in chat_ui tool-call partials #804

Description

@edudepetris

Basic checks

  • I searched existing issues - this hasn't been reported
  • I can reproduce this consistently
  • This is a RubyLLM bug, not my application code

What's broken?

The chat-ui is generating duplicated ids on HTML when tools are called in parallel.

Notice the message_3 (these are 3 tool calls in parallel)

bug
Image

How to reproduce

Using a model that support parallel tool calling,

  1. install the ruby_llm:chat_ui
  2. create 3 tools A,B and C using the rails generator ruby_llm:tool command.
      class ATool < RubyLLM::Tool                                               
        desc <<~DESC                                                            
          Returns the current weather forecast for a location. Independent and  
          self-contained: it needs only the location given in the user's        
  request and                                                                   
          does NOT depend on the output of any other tool. Safe to call in      
  parallel                                                                      
          with other tools.                                                     
        DESC                                                                    
                                                                                
        param :location, desc: "The location, e.g. a city name like 'Rio        
  Cuarto'"                                                                      
                                                                                
        def execute(location:)                                                  
          "The weather in #{location} will be sunny with a high of 25°C and a   
  low of 15°C."                                                                 
        end                                                                     
      end                                                                       
                                                                                
      class BTool < RubyLLM::Tool                                               
        desc <<~DESC                                                            
          Returns the current air quality index (AQI) for a location.           
  Independent:                                                                  
          it needs only the location from the user's request and does NOT       
  depend on                                                                     
          any other tool's output. Safe to call in parallel with other tools.   
        DESC                                                                    
                                                                                
        param :location, desc: "The location, e.g. a city name like 'Rio        
  Cuarto'"                                                                      
                                                                                
        def execute(location:)                                                  
          "The air quality index in #{location} is 42 (Good)."                  
        end                                                                     
      end                                                                       
                                                                                
      class CTool < RubyLLM::Tool                                               
        desc <<~DESC                                                            
          Returns the current UV index for a location. Independent and          
  self-contained:                                                               
          it needs only the location from the user's request and does NOT       
  depend on                                                                     
          any other tool's output. Safe to call in parallel with other tools.   
        DESC                                                                    
                                                                                
        param :location, desc: "The location, e.g. a city name like 'Rio        
  Cuarto'"                                                                      
                                                                                
        def execute(location:)                                                  
          "The UV index in #{location} is 6 (High)."                            
        end                                                                     
      end
  1. create an agent using the rails generator ruby_llm:agent command that

calls tools A, B, and C in parallel. These is how the agent looks like:

      class WeatherAssistantAgent < RubyLLM::Agent                              
        # Change `Chat` to your app's chat model for Rails persistence.         
        # Remove this line to skip persistence and use plain RubyLLM chats.     
        chat_model Chat                                                         
        instructions                                                            
        tools ATool, BTool, CTool                                               
      end                                                                       
                                                                                
      Instructions: You are a helpful assistant that provides weather           
  forecasts. You have access to three tools: A, B, and C                        
        instructions
        tools ATool, BTool, CTool
      end

     # Instructions: You are a helpful assistant that provides weather forecasts. You have access to three tools: A, B, and C
  1. Update the chat response job with this:
      class ChatResponseJob < ApplicationJob
        def perform(chat_id, content)
          chat = Chat.find(chat_id)
          agent = WeatherAssistantAgent.new(chat: chat)

          agent.ask(content) do |chunk|
            if chunk.content && !chunk.content.empty?
              message = chat.messages.last
              message.broadcast_append_chunk(chunk.content)
            end
          end
        end
      end
  1. Test the tool with a model that support parallel tool calls

chat: "For Rio Cuarto, give me the weather forecast, the air quality, and the UV index."

Expected behavior

  1. don't have duplicated ids in HTML

What actually happened

Generates duplicated ids on HTML

Environment

  • Latest ruby version
  • Ruby LLM 978d5f2 and before versions

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions