ryer.io

Reviewing the Duo Agentic Chat Fallback MR

TL;DR

  • Reviewing an MR where someone used Duo to one-shot a solution for falling back from agentic chat to classic chat when agentic chat isn’t enabled
  • Questioning whether self-managed instances should just fall back to classic mode, while SaaS instances should be shown an option to purchase credits instead
  • Hit a GDK self-managed config issue where an environment variable in our unit .env was overriding my GitLab SaaS env var, blocking GDK restart
  • Dug into Rails/Sidekiq code to trace the GitLab LLM Duo Chat class, eventually finding it under ee/lib/GitLab/LLM
  • Explored the chat component (Hamlit/Ruby) architecture that mounts backend data into frontend components, and traced how agentic_mode_available uses a policy check (user.can?) with instance variables for user and container
  • Learned a bit of Ruby method signature syntax where a keyword argument like user: with nothing else means it must be passed by name with no default

The MR: Fallback Between Agentic and Classic Chat

Someone used Duo to one-shot a solution around an instance lab instance. The Duo agentic platform can be enabled or not, and the Duo chat sidebar can point to either an agentic chatbot or a classic chatbot (still an LLM, but without the agentic subsystem of prompts). The default chat interface was agentic, but the instance accessed may not have had agentic chat enabled as an actual endpoint or service. The MR’s solution is to fall back to classic mode when agentic chat isn’t enabled under the hood.

My Questions on the Fallback Logic

What reasons would agentic chat not be enabled? If it’s because they’re not paying, should we just flip back to classic mode, or show them something like ‘you need to purchase credits’? On a self-managed instance, if they didn’t purchase that product, falling back to classic chat makes sense since they’ve already decided not to purchase. But on SaaS, it seems better to show them the option to purchase credits to enable agentic chat, or flip back to classic chat. I see two different situations here, and I’m exploring the code under the hood to determine whether this point has validity and is worth bringing up.

GDK Self-Managed Config Snag

Switching over to GDK in self-managed mode was fun, apparently. Our unit .env file had been overriding my environment variable for setting GitLab SaaS to true/one. I was trying to reconfigure and restart the GDK, but it wasn’t working because of that, so I had to get rid of that override. There’s something nagging at me that I set that for a reason at some point, so I need to take note and make sure I haven’t broken something else.

Tracking Down the Duo Chat Class in Rails

Learning about Ruby/Rails routes while tracking down a specific class prefixed with two colons, meaning it’s at the root. Ruby considers /app a root automatically, and additional roots can be configured and merged (not fully sure how). This is in an application.rb file within config, which defines eager load paths, related to Sidekiq (the remote processing service for long-running processes off the main threads). The class I was looking for is GitLab LLM Duo Chat. I had to search through lib, ee/lib, app, and ee/app and everything underneath. Eventually found it under ee/lib/GitLab/LLM.

Chat Component Architecture and Policy Check

Looking at the chat component, a Hamlit/Ruby entry point that mounts an HTML file to a specific DOM ID, passing data from backend to frontend. The architecture allows multiple app mounts to fetch their own data in parallel rather than in a cascade/waterfall. Looking at the Duo chat panel’s chat component, which instantiates a Duo chat class instance with several methods. I’m researching agentic_mode_available, which comes from duo_chat.agentic_mode_available. That class does a policy check: user.can?(:access_duo_chat_agentic_chat, container), where container is an instance variable (could be container, project, or group) and user is also an instance variable that can be nil.

Ruby Method Signature Syntax

Figured out that when a method signature has something like user: with nothing else, it means the parameter must be passed by name (as a keyword argument) with no default value.