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:
Aakash Bakhle
2026-02-20 12:16:43 +05:30
committed by GitHub
parent dbab0fe8da
commit db7e02b93b
4 changed files with 27 additions and 11 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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