perf: cherry-pick PR #172 性能优化(不含 orjson)

- tiktoken 编码器改为 @lru_cache 全局缓存,避免多实例重复初始化
- 前缀匹配按长度排序,避免短前缀抢先匹配
- AuthService 更新 last_used_at 时临时关闭 expire_on_commit,减少额外 SELECT
- UsageService.record_usage_batch 改用 bulk_insert_mappings 批量插入
- 已有记录查询增加 selectinload 预加载,避免 N+1
- Gemini normalizer 长行格式化

Co-Authored-By: AAEE86 <33052466+AAEE86@users.noreply.github.com>
This commit is contained in:
fawney19
2026-02-12 10:58:14 +08:00
parent f80deea110
commit 483d536e2c
7 changed files with 296 additions and 56 deletions

View File

@@ -0,0 +1,41 @@
import pytest
class _DummyEncoder:
def encode(self, text: str) -> list[int]:
return [0] * len(text)
class _DummyTiktoken:
def __init__(self, calls: dict[str, int]) -> None:
self._calls = calls
def get_encoding(self, _name: str) -> _DummyEncoder:
self._calls["get_encoding"] += 1
return _DummyEncoder()
def encoding_for_model(self, _model: str) -> _DummyEncoder:
self._calls["encoding_for_model"] += 1
return _DummyEncoder()
@pytest.mark.asyncio
async def test_encoder_is_globally_cached(monkeypatch: pytest.MonkeyPatch) -> None:
import src.plugins.token.tiktoken_counter as tc
# 清理缓存,避免受其他测试影响
tc._get_encoder_cached.cache_clear()
calls = {"get_encoding": 0, "encoding_for_model": 0}
monkeypatch.setattr(tc, "TIKTOKEN_AVAILABLE", True)
monkeypatch.setattr(tc, "tiktoken", _DummyTiktoken(calls))
p1 = tc.TiktokenCounterPlugin()
p2 = tc.TiktokenCounterPlugin()
# 两个实例对同一 model 请求编码器,底层 get_encoding 应只触发一次
await p1.count_tokens("hi", model="gpt-4")
await p2.count_tokens("hi", model="gpt-4")
assert calls["get_encoding"] == 1