free-claude-code/api/request_utils.py
Cursor Agent 25c7123e33 Phase 7 & 8: Routes optimization refactor and request utils split
Phase 7 - Optimization handlers:
- Create api/optimization_handlers.py with try_prefix_detection,
  try_quota_mock, try_title_skip, try_suggestion_skip, try_filepath_mock
- Add try_optimizations() that runs handlers in order
- Refactor routes.create_message to use try_optimizations()
- Update test_routes_optimizations patch targets

Phase 8 - Request utils split:
- Create api/detection.py: is_quota_check_request, is_title_generation_request,
  is_prefix_detection_request, is_suggestion_mode_request,
  is_filepath_extraction_request
- Create api/command_utils.py: extract_command_prefix, extract_filepaths_from_command
- Slim request_utils.py to get_token_count + re-exports for backward compat

Co-authored-by: Ali Khokhar <alishahryar2@gmail.com>
2026-02-15 01:41:35 +00:00

93 lines
3 KiB
Python

"""Request utility functions for API route handlers.
Contains token counting and re-exports detection/command utilities.
"""
import json
import logging
from typing import List, Optional, Union
import tiktoken
from .models.anthropic import MessagesRequest
from .detection import (
is_quota_check_request,
is_title_generation_request,
is_prefix_detection_request,
is_suggestion_mode_request,
is_filepath_extraction_request,
)
from .command_utils import extract_command_prefix, extract_filepaths_from_command
logger = logging.getLogger(__name__)
ENCODER = tiktoken.get_encoding("cl100k_base")
__all__ = [
"is_quota_check_request",
"is_title_generation_request",
"is_prefix_detection_request",
"is_suggestion_mode_request",
"is_filepath_extraction_request",
"extract_command_prefix",
"extract_filepaths_from_command",
"get_token_count",
]
def get_token_count(
messages: List,
system: Optional[Union[str, List]] = None,
tools: Optional[List] = None,
) -> int:
"""Estimate token count for a request.
Uses tiktoken cl100k_base encoding to estimate token usage.
Includes system prompt, messages, tools, and per-message overhead.
"""
total_tokens = 0
if system:
if isinstance(system, str):
total_tokens += len(ENCODER.encode(system))
elif isinstance(system, list):
for block in system:
if hasattr(block, "text"):
total_tokens += len(ENCODER.encode(block.text))
for msg in messages:
if isinstance(msg.content, str):
total_tokens += len(ENCODER.encode(msg.content))
elif isinstance(msg.content, list):
for block in msg.content:
b_type = getattr(block, "type", None)
if b_type == "text":
total_tokens += len(ENCODER.encode(getattr(block, "text", "")))
elif b_type == "thinking":
total_tokens += len(ENCODER.encode(getattr(block, "thinking", "")))
elif b_type == "tool_use":
name = getattr(block, "name", "")
inp = getattr(block, "input", {})
total_tokens += len(ENCODER.encode(name))
total_tokens += len(ENCODER.encode(json.dumps(inp)))
total_tokens += 10
elif b_type == "tool_result":
content = getattr(block, "content", "")
if isinstance(content, str):
total_tokens += len(ENCODER.encode(content))
else:
total_tokens += len(ENCODER.encode(json.dumps(content)))
total_tokens += 5
if tools:
for tool in tools:
tool_str = (
tool.name + (tool.description or "") + json.dumps(tool.input_schema)
)
total_tokens += len(ENCODER.encode(tool_str))
total_tokens += len(messages) * 3
if tools:
total_tokens += len(tools) * 5
return max(1, total_tokens)