module Captain::ChatHelper include Integrations::LlmInstrumentation include Captain::ToolExecutionHelper def request_chat_completion log_chat_completion_request with_agent_session do response = instrument_llm_call(instrumentation_params) do @client.chat( parameters: chat_parameters ) end handle_response(response) end rescue StandardError => e Rails.logger.error "#{self.class.name} Assistant: #{@assistant.id}, Error in chat completion: #{e}" raise e end def instrumentation_params { span_name: "llm.captain.#{feature_name}", account_id: resolved_account_id, conversation_id: @conversation_id, feature_name: feature_name, model: @model, messages: @messages, temperature: temperature, metadata: { assistant_id: @assistant&.id } } end def chat_parameters { model: @model, messages: @messages, tools: @tool_registry&.registered_tools || [], response_format: { type: 'json_object' }, temperature: temperature } end def temperature @assistant&.config&.[]('temperature').to_f || 1 end def resolved_account_id @account&.id || @assistant&.account_id end private # Ensures all LLM calls and tool executions within an agentic loop # are grouped under a single trace/session in Langfuse. # # Without this guard, each recursive call to request_chat_completion # (triggered by tool calls) would create a separate trace instead of # nesting within the existing session span. def with_agent_session(&) already_active = @agent_session_active return yield if already_active @agent_session_active = true instrument_agent_session(instrumentation_params, &) ensure @agent_session_active = false unless already_active end # Must be implemented by including class to identify the feature for instrumentation. # Used for Langfuse tagging and span naming. def feature_name raise NotImplementedError, "#{self.class.name} must implement #feature_name" end def log_chat_completion_request Rails.logger.info( "#{self.class.name} Assistant: #{@assistant.id}, Requesting chat completion for messages #{@messages} with #{@tool_registry&.registered_tools&.length || 0} tools " ) end end