Fix tiktoken SSL/gevent conflict in tests: use fallback token counting

This commit is contained in:
Russell Ballestrini 2026-01-11 08:42:51 -05:00
parent 4cc7e61c8b
commit 9fb7047866
2 changed files with 68 additions and 3 deletions

View file

@ -1,7 +1,13 @@
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime, timedelta
import tiktoken
import os
try:
import tiktoken
TIKTOKEN_AVAILABLE = True
except Exception:
TIKTOKEN_AVAILABLE = False
tiktoken = None
import json
@ -119,9 +125,16 @@ class Message(db.Model):
if self.token_count is None:
if self.is_base64_image():
self.token_count = 0
elif not TIKTOKEN_AVAILABLE or os.environ.get("TESTING"):
# Fallback: estimate ~4 chars per token when tiktoken unavailable
self.token_count = len(self.content) // 4 + 1
else:
encoding = tiktoken.encoding_for_model("gpt-4")
self.token_count = len(encoding.encode(self.content))
try:
encoding = tiktoken.encoding_for_model("gpt-4")
self.token_count = len(encoding.encode(self.content))
except Exception:
# Fallback on any tiktoken error (network, SSL, etc.)
self.token_count = len(self.content) // 4 + 1
return self.token_count
def is_base64_image(self):

View file

@ -5,7 +5,59 @@ pytest configuration and fixtures for OpenCompletion testing
Sets up common test environment variables and fixtures used across all tests.
"""
import sys
import os
# =============================================================================
# CRITICAL: Mock tiktoken BEFORE any other imports
# tiktoken tries to download encoding files over HTTPS which conflicts
# with gevent's monkey-patching of SSL, causing RecursionError
# =============================================================================
class MockTiktokenEncoding:
"""Mock tiktoken encoding that doesn't make network requests"""
def encode(self, text):
# Simple approximation: ~4 chars per token
return list(range(len(text) // 4 + 1))
class MockTiktoken:
"""Mock tiktoken module"""
_encoding = MockTiktokenEncoding()
@staticmethod
def encoding_for_model(model_name):
return MockTiktoken._encoding
@staticmethod
def get_encoding(encoding_name):
return MockTiktoken._encoding
# Insert mock tiktoken into sys.modules BEFORE any imports
if 'tiktoken' not in sys.modules:
sys.modules['tiktoken'] = MockTiktoken()
def pytest_configure(config):
"""
Called early in pytest startup, before test collection.
Ensures tiktoken is mocked before any test imports happen.
"""
if 'tiktoken' not in sys.modules:
sys.modules['tiktoken'] = MockTiktoken()
else:
# If tiktoken was already imported, patch its functions
import tiktoken
tiktoken.encoding_for_model = MockTiktoken.encoding_for_model
tiktoken.get_encoding = MockTiktoken.get_encoding
# =============================================================================
# Now safe to do other imports
# =============================================================================
import pytest
import tempfile
from unittest.mock import patch, MagicMock