From 077d0e2f9a68bb245ee3dae1e0415b4f2502bdc3 Mon Sep 17 00:00:00 2001 From: Kian Jones <11655409+kianjones9@users.noreply.github.com> Date: Tue, 11 Nov 2025 18:53:02 -0800 Subject: [PATCH] feat: dump stack trace on segfault (#6121) * dump stack trace on segfault * log tokenizer --- letta/server/rest_api/app.py | 4 ++ letta/services/agent_manager.py | 10 +++++ .../token_counter.py | 38 +++++++++++++++++-- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/letta/server/rest_api/app.py b/letta/server/rest_api/app.py index dbeeec8b..e6fd174f 100644 --- a/letta/server/rest_api/app.py +++ b/letta/server/rest_api/app.py @@ -1,3 +1,4 @@ +import faulthandler import importlib.util import json import logging @@ -10,6 +11,9 @@ from pathlib import Path from typing import Optional import uvicorn + +# Enable Python fault handler to get stack traces on segfaults +faulthandler.enable() from fastapi import FastAPI, Request from fastapi.exceptions import RequestValidationError from fastapi.responses import JSONResponse diff --git a/letta/services/agent_manager.py b/letta/services/agent_manager.py index 9e8645d6..7db7e626 100644 --- a/letta/services/agent_manager.py +++ b/letta/services/agent_manager.py @@ -3239,8 +3239,18 @@ class AgentManager: model = agent_state.llm_config.model if agent_state.llm_config.model_endpoint_type == "anthropic" else None token_counter = AnthropicTokenCounter(anthropic_client, model) # noqa + logger.info( + f"Using AnthropicTokenCounter for agent_id={agent_id}, model={model}, " + f"model_endpoint_type={agent_state.llm_config.model_endpoint_type}, " + f"environment={settings.environment}" + ) else: token_counter = TiktokenCounter(agent_state.llm_config.model) + logger.info( + f"Using TiktokenCounter for agent_id={agent_id}, model={agent_state.llm_config.model}, " + f"model_endpoint_type={agent_state.llm_config.model_endpoint_type}, " + f"environment={settings.environment}" + ) return await calculator.calculate_context_window( agent_state=agent_state, diff --git a/letta/services/context_window_calculator/token_counter.py b/letta/services/context_window_calculator/token_counter.py index a940cf55..12833fda 100644 --- a/letta/services/context_window_calculator/token_counter.py +++ b/letta/services/context_window_calculator/token_counter.py @@ -90,9 +90,24 @@ class TiktokenCounter(TokenCounter): ttl_s=3600, # cache for 1 hour ) async def count_text_tokens(self, text: str) -> int: + from letta.log import get_logger + + logger = get_logger(__name__) + if not text: return 0 - return count_tokens(text) + + text_length = len(text) + text_preview = text[:100] + "..." if len(text) > 100 else text + logger.debug(f"TiktokenCounter.count_text_tokens: model={self.model}, text_length={text_length}, preview={repr(text_preview)}") + + try: + result = count_tokens(text) + logger.debug(f"TiktokenCounter.count_text_tokens: completed successfully, tokens={result}") + return result + except Exception as e: + logger.error(f"TiktokenCounter.count_text_tokens: FAILED with {type(e).__name__}: {e}, text_length={text_length}") + raise @trace_method @async_redis_cache( @@ -102,11 +117,28 @@ class TiktokenCounter(TokenCounter): ttl_s=3600, # cache for 1 hour ) async def count_message_tokens(self, messages: List[Dict[str, Any]]) -> int: + from letta.log import get_logger + + logger = get_logger(__name__) + if not messages: return 0 - from letta.local_llm.utils import num_tokens_from_messages - return num_tokens_from_messages(messages=messages, model=self.model) + num_messages = len(messages) + total_content_length = sum(len(str(m.get("content", ""))) for m in messages) + logger.debug( + f"TiktokenCounter.count_message_tokens: model={self.model}, num_messages={num_messages}, total_content_length={total_content_length}" + ) + + try: + from letta.local_llm.utils import num_tokens_from_messages + + result = num_tokens_from_messages(messages=messages, model=self.model) + logger.debug(f"TiktokenCounter.count_message_tokens: completed successfully, tokens={result}") + return result + except Exception as e: + logger.error(f"TiktokenCounter.count_message_tokens: FAILED with {type(e).__name__}: {e}, num_messages={num_messages}") + raise @trace_method @async_redis_cache(