86 lines
2.3 KiB
Ruby
86 lines
2.3 KiB
Ruby
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
|