feat: captain channel type langfuse metadata (#13574)
# Pull Request Template ## Description Adds channel type to Captain assistant traces in Langfuse ## Type of change - [x] New feature (non-breaking change which adds functionality) ## How Has This Been Tested? Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration. <img width="906" height="672" alt="image" src="https://github.com/user-attachments/assets/224cee95-56aa-4672-8f74-0c0052251db9" /> <img width="908" height="611" alt="image" src="https://github.com/user-attachments/assets/ddd8ef0d-47c1-450c-a09f-27e82a34d04d" /> ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] Any dependent changes have been merged and published in downstream modules --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -53,15 +53,13 @@ module Captain::ChatHelper
|
||||
chat.on_end_message { |message| record_llm_generation(chat, message) }
|
||||
chat.on_tool_call { |tool_call| handle_tool_call(tool_call) }
|
||||
chat.on_tool_result { |result| handle_tool_result(result) }
|
||||
|
||||
chat
|
||||
end
|
||||
|
||||
def handle_tool_call(tool_call)
|
||||
persist_thinking_message(tool_call)
|
||||
start_tool_span(tool_call)
|
||||
@pending_tool_calls ||= []
|
||||
@pending_tool_calls.push(tool_call)
|
||||
(@pending_tool_calls ||= []).push(tool_call)
|
||||
end
|
||||
|
||||
def handle_tool_result(result)
|
||||
@@ -87,8 +85,9 @@ module Captain::ChatHelper
|
||||
messages: chat ? chat.messages.map { |m| { role: m.role.to_s, content: m.content.to_s } } : @messages,
|
||||
temperature: temperature,
|
||||
metadata: {
|
||||
assistant_id: @assistant&.id
|
||||
}
|
||||
assistant_id: @assistant&.id,
|
||||
channel_type: resolved_channel_type
|
||||
}.compact
|
||||
}
|
||||
end
|
||||
|
||||
@@ -104,6 +103,10 @@ module Captain::ChatHelper
|
||||
@account&.id || @assistant&.account_id
|
||||
end
|
||||
|
||||
def resolved_channel_type
|
||||
Conversation.find_by(account_id: resolved_account_id, display_id: @conversation_id)&.inbox&.channel_type if @conversation_id
|
||||
end
|
||||
|
||||
# Ensures all LLM calls and tool executions within an agentic loop
|
||||
# are grouped under a single trace/session in Langfuse.
|
||||
#
|
||||
@@ -127,10 +130,7 @@ module Captain::ChatHelper
|
||||
end
|
||||
|
||||
def log_chat_completion_request
|
||||
Rails.logger.info(
|
||||
"#{self.class.name} Assistant: #{@assistant.id}, Requesting chat completion
|
||||
for messages #{@messages} with #{@tools&.length || 0} tools
|
||||
"
|
||||
)
|
||||
Rails.logger.info("#{self.class.name} Assistant: #{@assistant.id}, Requesting chat completion " \
|
||||
"for messages #{@messages} with #{@tools&.length || 0} tools")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -114,6 +114,7 @@ class Captain::Assistant::AgentRunnerService
|
||||
|
||||
if @conversation
|
||||
state[:conversation] = @conversation.attributes.symbolize_keys.slice(*CONVERSATION_STATE_ATTRIBUTES)
|
||||
state[:channel_type] = @conversation.inbox&.channel_type
|
||||
state[:contact] = @conversation.contact.attributes.symbolize_keys.slice(*CONTACT_STATE_ATTRIBUTES) if @conversation.contact
|
||||
end
|
||||
|
||||
@@ -151,7 +152,8 @@ class Captain::Assistant::AgentRunnerService
|
||||
ATTR_LANGFUSE_USER_ID => state[:account_id],
|
||||
format(ATTR_LANGFUSE_METADATA, 'assistant_id') => state[:assistant_id],
|
||||
format(ATTR_LANGFUSE_METADATA, 'conversation_id') => conversation[:id],
|
||||
format(ATTR_LANGFUSE_METADATA, 'conversation_display_id') => conversation[:display_id]
|
||||
format(ATTR_LANGFUSE_METADATA, 'conversation_display_id') => conversation[:display_id],
|
||||
format(ATTR_LANGFUSE_METADATA, 'channel_type') => state[:channel_type]
|
||||
}.compact.transform_values(&:to_s)
|
||||
end
|
||||
|
||||
|
||||
@@ -278,6 +278,7 @@ RSpec.describe Captain::Assistant::AgentRunnerService do
|
||||
contact_id: contact.id,
|
||||
status: conversation.status
|
||||
)
|
||||
expect(state[:channel_type]).to eq(inbox.channel_type)
|
||||
end
|
||||
|
||||
it 'includes contact attributes when contact is present' do
|
||||
|
||||
@@ -28,6 +28,19 @@ RSpec.describe Captain::Llm::AssistantChatService do
|
||||
allow(mock_chat).to receive(:messages).and_return([])
|
||||
end
|
||||
|
||||
describe 'instrumentation metadata' do
|
||||
it 'passes channel_type to the agent session instrumentation' do
|
||||
service = described_class.new(assistant: assistant, conversation_id: conversation.display_id)
|
||||
|
||||
expect(service).to receive(:instrument_agent_session).with(
|
||||
hash_including(metadata: hash_including(channel_type: conversation.inbox.channel_type))
|
||||
).and_yield
|
||||
|
||||
allow(mock_chat).to receive(:ask).and_return(mock_response)
|
||||
service.generate_response(message_history: [{ role: 'user', content: 'Hello' }])
|
||||
end
|
||||
end
|
||||
|
||||
describe 'image analysis' do
|
||||
context 'when user sends a message with an image attachment' do
|
||||
let(:message_history) do
|
||||
|
||||
Reference in New Issue
Block a user