toonify-mcp 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.de.md +270 -0
- package/README.es.md +1 -1
- package/README.fr.md +270 -0
- package/README.id.md +1 -1
- package/README.ja.md +1 -1
- package/README.ko.md +270 -0
- package/README.md +31 -1
- package/README.pt.md +270 -0
- package/README.ru.md +270 -0
- package/README.vi.md +1 -1
- package/README.zh-CN.md +1 -1
- package/README.zh-TW.md +1 -1
- package/dist/optimizer/caching/cache-types.d.ts +34 -0
- package/dist/optimizer/caching/cache-types.d.ts.map +1 -1
- package/dist/optimizer/caching/index.d.ts +2 -0
- package/dist/optimizer/caching/index.d.ts.map +1 -1
- package/dist/optimizer/caching/index.js +2 -0
- package/dist/optimizer/caching/index.js.map +1 -1
- package/dist/optimizer/caching/lru-cache.d.ts +70 -0
- package/dist/optimizer/caching/lru-cache.d.ts.map +1 -0
- package/dist/optimizer/caching/lru-cache.js +230 -0
- package/dist/optimizer/caching/lru-cache.js.map +1 -0
- package/dist/optimizer/caching/persistent-cache.d.ts +82 -0
- package/dist/optimizer/caching/persistent-cache.d.ts.map +1 -0
- package/dist/optimizer/caching/persistent-cache.js +237 -0
- package/dist/optimizer/caching/persistent-cache.js.map +1 -0
- package/dist/optimizer/token-optimizer.d.ts +31 -1
- package/dist/optimizer/token-optimizer.d.ts.map +1 -1
- package/dist/optimizer/token-optimizer.js +100 -5
- package/dist/optimizer/token-optimizer.js.map +1 -1
- package/dist/optimizer/types.d.ts +3 -2
- package/dist/optimizer/types.d.ts.map +1 -1
- package/dist/server/mcp-server.d.ts +8 -0
- package/dist/server/mcp-server.d.ts.map +1 -1
- package/dist/server/mcp-server.js +112 -40
- package/dist/server/mcp-server.js.map +1 -1
- package/package.json +1 -1
package/README.ru.md
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# 🎯 Toonify MCP
|
|
2
|
+
|
|
3
|
+
**[English](README.md) | [繁體中文](README.zh-TW.md) | [简体中文](README.zh-CN.md) | [日本語](README.ja.md) | [Español](README.es.md) | [Français](README.fr.md) | [Deutsch](README.de.md) | [한국어](README.ko.md) | [Русский](README.ru.md) | [Português](README.pt.md) | [Tiếng Việt](README.vi.md) | [Bahasa Indonesia](README.id.md)**
|
|
4
|
+
|
|
5
|
+
MCP-сервер + плагин Claude Code, обеспечивающий автоматическую оптимизацию токенов для структурированных данных.
|
|
6
|
+
Сокращает использование токенов Claude API на **30-65% в зависимости от структуры данных** благодаря прозрачному преобразованию в формат TOON, с типичной экономией **50-55%** для структурированных данных.
|
|
7
|
+
|
|
8
|
+
## Что нового в v0.3.0
|
|
9
|
+
|
|
10
|
+
✨ **Многоязычная оптимизация токенов!**
|
|
11
|
+
- ✅ Точный подсчет токенов для 15+ языков (китайский, японский, корейский, испанский, арабский и др.)
|
|
12
|
+
- ✅ Языково-зависимые множители токенов (2x для китайского, 2.5x для японского, 3x для арабского)
|
|
13
|
+
- ✅ Поддержка смешанных языков с автоматическим определением
|
|
14
|
+
- ✅ Улучшенная точность оптимизации на основе реальных тестов
|
|
15
|
+
- ✅ Подтвержденные данными заявления об экономии токенов (диапазон 30-65%, обычно 50-55%)
|
|
16
|
+
|
|
17
|
+
## Возможности
|
|
18
|
+
|
|
19
|
+
- **Сокращение токенов на 30-65%** (обычно 50-55%) для данных JSON, CSV, YAML
|
|
20
|
+
- **Многоязычная поддержка** - точный подсчет токенов для 15+ языков
|
|
21
|
+
- **Полностью автоматический режим** - хук PostToolUse перехватывает результаты инструментов
|
|
22
|
+
- **Нулевая конфигурация** - работает из коробки с разумными настройками по умолчанию
|
|
23
|
+
- **Двойной режим** - работает как плагин (авто) или MCP-сервер (вручную)
|
|
24
|
+
- **Встроенные метрики** - отслеживание экономии токенов локально
|
|
25
|
+
- **Тихий откат** - никогда не нарушает ваш рабочий процесс
|
|
26
|
+
|
|
27
|
+
## Установка
|
|
28
|
+
|
|
29
|
+
### Вариант A: Плагин Claude Code (Рекомендуется) ⭐
|
|
30
|
+
|
|
31
|
+
**Автоматическая оптимизация токенов без ручных вызовов:**
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# 1. Установить глобально
|
|
35
|
+
npm install -g toonify-mcp
|
|
36
|
+
|
|
37
|
+
# 2. Добавить как плагин (автоматический режим)
|
|
38
|
+
claude plugin add toonify-mcp
|
|
39
|
+
|
|
40
|
+
# 3. Проверить установку
|
|
41
|
+
claude plugin list
|
|
42
|
+
# Должно показать: toonify-mcp ✓
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Вот и всё!** Хук PostToolUse теперь будет автоматически перехватывать и оптимизировать структурированные данные из инструментов Read, Grep и других файловых инструментов.
|
|
46
|
+
|
|
47
|
+
### Вариант B: MCP-сервер (Ручной режим)
|
|
48
|
+
|
|
49
|
+
**Для явного контроля или других MCP-клиентов:**
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# 1. Установить глобально
|
|
53
|
+
npm install -g toonify-mcp
|
|
54
|
+
|
|
55
|
+
# 2. Зарегистрировать как MCP-сервер
|
|
56
|
+
claude mcp add toonify -- toonify-mcp
|
|
57
|
+
|
|
58
|
+
# 3. Проверить
|
|
59
|
+
claude mcp list
|
|
60
|
+
# Должно показать: toonify: toonify-mcp - ✓ Connected
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Затем вызывайте инструменты явно:
|
|
64
|
+
```bash
|
|
65
|
+
claude mcp call toonify optimize_content '{"content": "..."}'
|
|
66
|
+
claude mcp call toonify get_stats '{}'
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Как это работает
|
|
70
|
+
|
|
71
|
+
### Режим плагина (Автоматический)
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
Пользователь: Прочитать большой JSON-файл
|
|
75
|
+
↓
|
|
76
|
+
Claude Code вызывает инструмент Read
|
|
77
|
+
↓
|
|
78
|
+
Хук PostToolUse перехватывает результат
|
|
79
|
+
↓
|
|
80
|
+
Хук определяет JSON, преобразует в TOON
|
|
81
|
+
↓
|
|
82
|
+
Оптимизированный контент отправляется в Claude API
|
|
83
|
+
↓
|
|
84
|
+
Достигается типичное сокращение токенов на 50-55% ✨
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Режим MCP-сервера (Ручной)
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
Пользователь: явно вызывает mcp__toonify__optimize_content
|
|
91
|
+
↓
|
|
92
|
+
Контент преобразуется в формат TOON
|
|
93
|
+
↓
|
|
94
|
+
Возвращается оптимизированный результат
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Конфигурация
|
|
98
|
+
|
|
99
|
+
Создайте `~/.claude/toonify-config.json` (необязательно):
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"enabled": true,
|
|
104
|
+
"minTokensThreshold": 50,
|
|
105
|
+
"minSavingsThreshold": 30,
|
|
106
|
+
"skipToolPatterns": ["Bash", "Write", "Edit"]
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Параметры
|
|
111
|
+
|
|
112
|
+
- **enabled**: Включить/отключить автоматическую оптимизацию (по умолчанию: `true`)
|
|
113
|
+
- **minTokensThreshold**: Минимальное количество токенов перед оптимизацией (по умолчанию: `50`)
|
|
114
|
+
- **minSavingsThreshold**: Минимальный требуемый процент экономии (по умолчанию: `30%`)
|
|
115
|
+
- **skipToolPatterns**: Инструменты, которые никогда не оптимизировать (по умолчанию: `["Bash", "Write", "Edit"]`)
|
|
116
|
+
|
|
117
|
+
### Переменные окружения
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
export TOONIFY_ENABLED=true
|
|
121
|
+
export TOONIFY_MIN_TOKENS=50
|
|
122
|
+
export TOONIFY_MIN_SAVINGS=30
|
|
123
|
+
export TOONIFY_SKIP_TOOLS="Bash,Write"
|
|
124
|
+
export TOONIFY_SHOW_STATS=true # Показывать статистику оптимизации в выводе
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Примеры
|
|
128
|
+
|
|
129
|
+
### До оптимизации (142 токена)
|
|
130
|
+
|
|
131
|
+
```json
|
|
132
|
+
{
|
|
133
|
+
"products": [
|
|
134
|
+
{"id": 101, "name": "Laptop Pro", "price": 1299},
|
|
135
|
+
{"id": 102, "name": "Magic Mouse", "price": 79}
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### После оптимизации (57 токенов, -60%)
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
[TOON-JSON]
|
|
144
|
+
products[2]{id,name,price}:
|
|
145
|
+
101,Laptop Pro,1299
|
|
146
|
+
102,Magic Mouse,79
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**Автоматически применяется в режиме плагина - ручные вызовы не нужны!**
|
|
150
|
+
|
|
151
|
+
## Советы по использованию
|
|
152
|
+
|
|
153
|
+
### Когда срабатывает автоматическая оптимизация?
|
|
154
|
+
|
|
155
|
+
Хук PostToolUse автоматически оптимизирует, когда:
|
|
156
|
+
- ✅ Контент является валидным JSON, CSV или YAML
|
|
157
|
+
- ✅ Размер контента ≥ `minTokensThreshold` (по умолчанию: 50 токенов)
|
|
158
|
+
- ✅ Предполагаемая экономия ≥ `minSavingsThreshold` (по умолчанию: 30%)
|
|
159
|
+
- ✅ Инструмент НЕ входит в `skipToolPatterns` (например, не Bash/Write/Edit)
|
|
160
|
+
|
|
161
|
+
### Просмотр статистики оптимизации
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
# В режиме плагина
|
|
165
|
+
claude mcp call toonify get_stats '{}'
|
|
166
|
+
|
|
167
|
+
# Или проверьте вывод Claude Code для статистики (если TOONIFY_SHOW_STATS=true)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Устранение неполадок
|
|
171
|
+
|
|
172
|
+
### Хук не срабатывает
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
# 1. Проверьте, что плагин установлен
|
|
176
|
+
claude plugin list | grep toonify
|
|
177
|
+
|
|
178
|
+
# 2. Проверьте конфигурацию
|
|
179
|
+
cat ~/.claude/toonify-config.json
|
|
180
|
+
|
|
181
|
+
# 3. Включите статистику для просмотра попыток оптимизации
|
|
182
|
+
export TOONIFY_SHOW_STATS=true
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Оптимизация не применяется
|
|
186
|
+
|
|
187
|
+
- Проверьте `minTokensThreshold` - контент может быть слишком мал
|
|
188
|
+
- Проверьте `minSavingsThreshold` - экономия может быть < 30%
|
|
189
|
+
- Проверьте `skipToolPatterns` - инструмент может быть в списке пропуска
|
|
190
|
+
- Убедитесь, что контент является валидным JSON/CSV/YAML
|
|
191
|
+
|
|
192
|
+
### Проблемы с производительностью
|
|
193
|
+
|
|
194
|
+
- Уменьшите `minTokensThreshold` для более агрессивной оптимизации
|
|
195
|
+
- Увеличьте `minSavingsThreshold` для пропуска незначительных оптимизаций
|
|
196
|
+
- Добавьте больше инструментов в `skipToolPatterns` при необходимости
|
|
197
|
+
|
|
198
|
+
## Сравнение: Плагин vs MCP-сервер
|
|
199
|
+
|
|
200
|
+
| Функция | Режим плагина | Режим MCP-сервера |
|
|
201
|
+
|---------|--------------|-------------------|
|
|
202
|
+
| **Активация** | Автоматическая (PostToolUse) | Ручная (вызов инструмента) |
|
|
203
|
+
| **Совместимость** | Только Claude Code | Любой MCP-клиент |
|
|
204
|
+
| **Конфигурация** | Файл конфигурации плагина | Инструменты MCP |
|
|
205
|
+
| **Производительность** | Нулевые накладные расходы | Накладные расходы на вызов |
|
|
206
|
+
| **Сценарий использования** | Ежедневный рабочий процесс | Явный контроль |
|
|
207
|
+
|
|
208
|
+
**Рекомендация**: Используйте режим плагина для автоматической оптимизации. Используйте режим MCP-сервера для явного контроля или других MCP-клиентов.
|
|
209
|
+
|
|
210
|
+
## Удаление
|
|
211
|
+
|
|
212
|
+
### Режим плагина
|
|
213
|
+
```bash
|
|
214
|
+
claude plugin remove toonify-mcp
|
|
215
|
+
rm ~/.claude/toonify-config.json
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Режим MCP-сервера
|
|
219
|
+
```bash
|
|
220
|
+
claude mcp remove toonify
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Пакет
|
|
224
|
+
```bash
|
|
225
|
+
npm uninstall -g toonify-mcp
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Ссылки
|
|
229
|
+
|
|
230
|
+
- **GitHub**: https://github.com/kevintseng/toonify-mcp
|
|
231
|
+
- **Issues**: https://github.com/kevintseng/toonify-mcp/issues
|
|
232
|
+
- **NPM**: https://www.npmjs.com/package/toonify-mcp
|
|
233
|
+
- **MCP Docs**: https://code.claude.com/docs/mcp
|
|
234
|
+
- **TOON Format**: https://github.com/toon-format/toon
|
|
235
|
+
|
|
236
|
+
## Вклад в проект
|
|
237
|
+
|
|
238
|
+
Вклады приветствуются! Пожалуйста, ознакомьтесь с [CONTRIBUTING.md](CONTRIBUTING.md) для получения рекомендаций.
|
|
239
|
+
|
|
240
|
+
## Лицензия
|
|
241
|
+
|
|
242
|
+
Лицензия MIT - см. [LICENSE](LICENSE)
|
|
243
|
+
|
|
244
|
+
## Changelog
|
|
245
|
+
|
|
246
|
+
### v0.3.0 (2025-12-26)
|
|
247
|
+
- ✨ **Многоязычная оптимизация токенов** - точный подсчет для 15+ языков
|
|
248
|
+
- ✨ Языково-зависимые множители токенов (2x китайский, 2.5x японский, 3x арабский и др.)
|
|
249
|
+
- ✨ Определение и оптимизация смешанных языков
|
|
250
|
+
- ✨ Комплексное тестирование с реальной статистикой
|
|
251
|
+
- 📊 Подтвержденные данными заявления об экономии токенов (диапазон 30-65%, обычно 50-55%)
|
|
252
|
+
- ✅ 75+ пройденных тестов, включая многоязычные граничные случаи
|
|
253
|
+
- 📝 Многоязычные версии README
|
|
254
|
+
|
|
255
|
+
### v0.2.0 (2025-12-25)
|
|
256
|
+
- ✨ Добавлена поддержка плагина Claude Code с хуком PostToolUse
|
|
257
|
+
- ✨ Автоматическая оптимизация токенов (ручные вызовы не нужны)
|
|
258
|
+
- ✨ Система конфигурации плагина
|
|
259
|
+
- ✨ Двойной режим: Плагин (авто) + MCP-сервер (вручную)
|
|
260
|
+
- 📝 Комплексное обновление документации
|
|
261
|
+
|
|
262
|
+
### v0.1.1 (2024-12-24)
|
|
263
|
+
- 🐛 Исправления ошибок и улучшения
|
|
264
|
+
- 📝 Обновления документации
|
|
265
|
+
|
|
266
|
+
### v0.1.0 (2024-12-24)
|
|
267
|
+
- 🎉 Первый релиз
|
|
268
|
+
- ✨ Реализация MCP-сервера
|
|
269
|
+
- ✨ Оптимизация формата TOON
|
|
270
|
+
- ✨ Встроенное отслеживание метрик
|
package/README.vi.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 🎯 Toonify MCP
|
|
2
2
|
|
|
3
|
-
**[English](README.md) | [繁體中文](README.zh-TW.md) | [简体中文](README.zh-CN.md) | [日本語](README.ja.md) | [Español](README.es.md) | [Tiếng Việt](README.vi.md) | [Bahasa Indonesia](README.id.md)**
|
|
3
|
+
**[English](README.md) | [繁體中文](README.zh-TW.md) | [简体中文](README.zh-CN.md) | [日本語](README.ja.md) | [Español](README.es.md) | [Français](README.fr.md) | [Deutsch](README.de.md) | [한국어](README.ko.md) | [Русский](README.ru.md) | [Português](README.pt.md) | [Tiếng Việt](README.vi.md) | [Bahasa Indonesia](README.id.md)**
|
|
4
4
|
|
|
5
5
|
Máy chủ MCP + Plugin Claude Code cung cấp tối ưu hóa token tự động cho dữ liệu có cấu trúc.
|
|
6
6
|
Giảm 30-65% việc sử dụng token của Claude API **tùy thuộc vào cấu trúc dữ liệu** thông qua chuyển đổi định dạng TOON minh bạch, với mức tiết kiệm điển hình **50-55%** cho dữ liệu có cấu trúc.
|
package/README.zh-CN.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 🎯 Toonify MCP
|
|
2
2
|
|
|
3
|
-
**[English](README.md) | [繁體中文](README.zh-TW.md) | [简体中文](README.zh-CN.md) | [日本語](README.ja.md) | [Español](README.es.md) | [Tiếng Việt](README.vi.md) | [Bahasa Indonesia](README.id.md)**
|
|
3
|
+
**[English](README.md) | [繁體中文](README.zh-TW.md) | [简体中文](README.zh-CN.md) | [日本語](README.ja.md) | [Español](README.es.md) | [Français](README.fr.md) | [Deutsch](README.de.md) | [한국어](README.ko.md) | [Русский](README.ru.md) | [Português](README.pt.md) | [Tiếng Việt](README.vi.md) | [Bahasa Indonesia](README.id.md)**
|
|
4
4
|
|
|
5
5
|
MCP 服务器 + Claude Code 插件,提供结构化数据的自动 token 优化。
|
|
6
6
|
通过透明的 TOON 格式转换,**根据数据结构减少 30-65% 的 Claude API token 使用量**,结构化数据的典型节省率为 **50-55%**。
|
package/README.zh-TW.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 🎯 Toonify MCP
|
|
2
2
|
|
|
3
|
-
**[English](README.md) | [繁體中文](README.zh-TW.md) | [简体中文](README.zh-CN.md) | [日本語](README.ja.md) | [Español](README.es.md) | [Tiếng Việt](README.vi.md) | [Bahasa Indonesia](README.id.md)**
|
|
3
|
+
**[English](README.md) | [繁體中文](README.zh-TW.md) | [简体中文](README.zh-CN.md) | [日本語](README.ja.md) | [Español](README.es.md) | [Français](README.fr.md) | [Deutsch](README.de.md) | [한국어](README.ko.md) | [Русский](README.ru.md) | [Português](README.pt.md) | [Tiếng Việt](README.vi.md) | [Bahasa Indonesia](README.id.md)**
|
|
4
4
|
|
|
5
5
|
MCP 伺服器 + Claude Code 外掛程式,提供結構化數據的自動 token 優化。
|
|
6
6
|
透過透明的 TOON 格式轉換,**根據數據結構減少 30-65% 的 Claude API token 使用量**,結構化數據的典型節省率為 **50-55%**。
|
|
@@ -33,4 +33,38 @@ export interface CacheStrategy {
|
|
|
33
33
|
shouldCache: (content: string, tokens: number) => boolean;
|
|
34
34
|
formatCacheStructure: (content: CachedContent) => any;
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* LRU Cache Configuration
|
|
38
|
+
*/
|
|
39
|
+
export interface LRUCacheConfig {
|
|
40
|
+
enabled: boolean;
|
|
41
|
+
maxSize: number;
|
|
42
|
+
ttl: number;
|
|
43
|
+
persistent: boolean;
|
|
44
|
+
persistPath?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* LRU Cache Entry
|
|
48
|
+
*/
|
|
49
|
+
export interface LRUCacheEntry<T> {
|
|
50
|
+
key: string;
|
|
51
|
+
value: T;
|
|
52
|
+
timestamp: number;
|
|
53
|
+
lastAccessed: number;
|
|
54
|
+
accessCount: number;
|
|
55
|
+
expiresAt: number;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* LRU Cache Statistics
|
|
59
|
+
*/
|
|
60
|
+
export interface LRUCacheStats {
|
|
61
|
+
hits: number;
|
|
62
|
+
misses: number;
|
|
63
|
+
evictions: number;
|
|
64
|
+
expirations: number;
|
|
65
|
+
currentSize: number;
|
|
66
|
+
maxSize: number;
|
|
67
|
+
hitRate: number;
|
|
68
|
+
averageAccessCount: number;
|
|
69
|
+
}
|
|
36
70
|
//# sourceMappingURL=cache-types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache-types.d.ts","sourceRoot":"","sources":["../../../src/optimizer/caching/cache-types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,WAAW,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC1C,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,WAAW,GAAG,QAAQ,CAAC;IACjC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IAC1D,oBAAoB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,GAAG,CAAC;CACvD"}
|
|
1
|
+
{"version":3,"file":"cache-types.d.ts","sourceRoot":"","sources":["../../../src/optimizer/caching/cache-types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,WAAW,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC1C,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,WAAW,GAAG,QAAQ,CAAC;IACjC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IAC1D,oBAAoB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,GAAG,CAAC;CACvD;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,CAAC,CAAC;IACT,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,kBAAkB,EAAE,MAAM,CAAC;CAC5B"}
|
|
@@ -4,4 +4,6 @@
|
|
|
4
4
|
export * from './cache-types.js';
|
|
5
5
|
export * from './cache-strategies.js';
|
|
6
6
|
export { CacheOptimizer } from './cache-optimizer.js';
|
|
7
|
+
export { LRUCache } from './lru-cache.js';
|
|
8
|
+
export { PersistentCache } from './persistent-cache.js';
|
|
7
9
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/optimizer/caching/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/optimizer/caching/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -4,4 +4,6 @@
|
|
|
4
4
|
export * from './cache-types.js';
|
|
5
5
|
export * from './cache-strategies.js';
|
|
6
6
|
export { CacheOptimizer } from './cache-optimizer.js';
|
|
7
|
+
export { LRUCache } from './lru-cache.js';
|
|
8
|
+
export { PersistentCache } from './persistent-cache.js';
|
|
7
9
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/optimizer/caching/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/optimizer/caching/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LRU Cache with TTL (Time-to-Live) support
|
|
3
|
+
* Features:
|
|
4
|
+
* - Least Recently Used eviction strategy
|
|
5
|
+
* - TTL-based expiration
|
|
6
|
+
* - Optional disk persistence
|
|
7
|
+
*/
|
|
8
|
+
import type { LRUCacheConfig, LRUCacheStats } from './cache-types.js';
|
|
9
|
+
export declare class LRUCache<T = any> {
|
|
10
|
+
private cache;
|
|
11
|
+
private config;
|
|
12
|
+
private stats;
|
|
13
|
+
private persistentCache?;
|
|
14
|
+
private totalAccessCount;
|
|
15
|
+
constructor(config?: Partial<LRUCacheConfig>);
|
|
16
|
+
/**
|
|
17
|
+
* Generate cache key from content using SHA-256
|
|
18
|
+
*/
|
|
19
|
+
static generateKey(content: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Get value from cache
|
|
22
|
+
*/
|
|
23
|
+
get(key: string): T | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Set value in cache
|
|
26
|
+
*/
|
|
27
|
+
set(key: string, value: T): void;
|
|
28
|
+
/**
|
|
29
|
+
* Check if entry has expired
|
|
30
|
+
*/
|
|
31
|
+
private isExpired;
|
|
32
|
+
/**
|
|
33
|
+
* Evict least recently used entry
|
|
34
|
+
* Since Map maintains insertion order, the first entry is the least recently used
|
|
35
|
+
*/
|
|
36
|
+
private evictLRU;
|
|
37
|
+
/**
|
|
38
|
+
* Clear all cache entries
|
|
39
|
+
*/
|
|
40
|
+
clear(): void;
|
|
41
|
+
/**
|
|
42
|
+
* Clean up expired entries
|
|
43
|
+
*/
|
|
44
|
+
cleanup(): number;
|
|
45
|
+
/**
|
|
46
|
+
* Get cache statistics
|
|
47
|
+
*/
|
|
48
|
+
getStats(): LRUCacheStats;
|
|
49
|
+
/**
|
|
50
|
+
* Update hit rate and average access count (O(1) complexity)
|
|
51
|
+
*/
|
|
52
|
+
private updateStats;
|
|
53
|
+
/**
|
|
54
|
+
* Load cache from disk
|
|
55
|
+
*/
|
|
56
|
+
private loadFromDisk;
|
|
57
|
+
/**
|
|
58
|
+
* Force save all entries to disk and wait for completion
|
|
59
|
+
*/
|
|
60
|
+
saveToDisk(): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Get cache size
|
|
63
|
+
*/
|
|
64
|
+
size(): number;
|
|
65
|
+
/**
|
|
66
|
+
* Check if key exists (without updating access time)
|
|
67
|
+
*/
|
|
68
|
+
has(key: string): boolean;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=lru-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lru-cache.d.ts","sourceRoot":"","sources":["../../../src/optimizer/caching/lru-cache.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAiB,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGrF,qBAAa,QAAQ,CAAC,CAAC,GAAG,GAAG;IAC3B,OAAO,CAAC,KAAK,CAAgC;IAC7C,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,eAAe,CAAC,CAAqB;IAC7C,OAAO,CAAC,gBAAgB,CAAa;gBAEzB,MAAM,GAAE,OAAO,CAAC,cAAc,CAAM;IA6BhD;;OAEG;IACH,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAI3C;;OAEG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAsC/B;;OAEG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IA8BhC;;OAEG;IACH,OAAO,CAAC,SAAS;IAIjB;;;OAGG;IACH,OAAO,CAAC,QAAQ;IAqBhB;;OAEG;IACH,KAAK,IAAI,IAAI;IAUb;;OAEG;IACH,OAAO,IAAI,MAAM;IAqBjB;;OAEG;IACH,QAAQ,IAAI,aAAa;IAIzB;;OAEG;IACH,OAAO,CAAC,WAAW;IAQnB;;OAEG;IACH,OAAO,CAAC,YAAY;IAsBpB;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAWjC;;OAEG;IACH,IAAI,IAAI,MAAM;IAId;;OAEG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;CAO1B"}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LRU Cache with TTL (Time-to-Live) support
|
|
3
|
+
* Features:
|
|
4
|
+
* - Least Recently Used eviction strategy
|
|
5
|
+
* - TTL-based expiration
|
|
6
|
+
* - Optional disk persistence
|
|
7
|
+
*/
|
|
8
|
+
import crypto from 'crypto';
|
|
9
|
+
import { PersistentCache } from './persistent-cache.js';
|
|
10
|
+
export class LRUCache {
|
|
11
|
+
cache;
|
|
12
|
+
config;
|
|
13
|
+
stats;
|
|
14
|
+
persistentCache;
|
|
15
|
+
totalAccessCount = 0; // Running total for O(1) average calculation
|
|
16
|
+
constructor(config = {}) {
|
|
17
|
+
this.config = {
|
|
18
|
+
enabled: true,
|
|
19
|
+
maxSize: 500,
|
|
20
|
+
ttl: 3600000, // 1 hour in milliseconds
|
|
21
|
+
persistent: false,
|
|
22
|
+
persistPath: '~/.toonify-mcp/cache/optimization-cache.json',
|
|
23
|
+
...config
|
|
24
|
+
};
|
|
25
|
+
this.cache = new Map();
|
|
26
|
+
this.stats = {
|
|
27
|
+
hits: 0,
|
|
28
|
+
misses: 0,
|
|
29
|
+
evictions: 0,
|
|
30
|
+
expirations: 0,
|
|
31
|
+
currentSize: 0,
|
|
32
|
+
maxSize: this.config.maxSize,
|
|
33
|
+
hitRate: 0,
|
|
34
|
+
averageAccessCount: 0
|
|
35
|
+
};
|
|
36
|
+
// Initialize persistent cache if enabled
|
|
37
|
+
if (this.config.persistent && this.config.persistPath) {
|
|
38
|
+
this.persistentCache = new PersistentCache(this.config.persistPath);
|
|
39
|
+
this.loadFromDisk();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Generate cache key from content using SHA-256
|
|
44
|
+
*/
|
|
45
|
+
static generateKey(content) {
|
|
46
|
+
return crypto.createHash('sha256').update(content).digest('hex');
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Get value from cache
|
|
50
|
+
*/
|
|
51
|
+
get(key) {
|
|
52
|
+
if (!this.config.enabled) {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
const entry = this.cache.get(key);
|
|
56
|
+
if (!entry) {
|
|
57
|
+
this.stats.misses++;
|
|
58
|
+
this.updateStats();
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
// Check if expired
|
|
62
|
+
if (this.isExpired(entry)) {
|
|
63
|
+
this.totalAccessCount -= entry.accessCount; // Update running total
|
|
64
|
+
this.cache.delete(key);
|
|
65
|
+
this.stats.expirations++;
|
|
66
|
+
this.stats.misses++;
|
|
67
|
+
this.updateStats();
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
// Update access metadata (LRU)
|
|
71
|
+
entry.lastAccessed = Date.now();
|
|
72
|
+
entry.accessCount++;
|
|
73
|
+
this.totalAccessCount++; // Update running total
|
|
74
|
+
// Delete and re-insert to move to end (most recently used in Map)
|
|
75
|
+
this.cache.delete(key);
|
|
76
|
+
this.cache.set(key, entry);
|
|
77
|
+
this.stats.hits++;
|
|
78
|
+
this.updateStats();
|
|
79
|
+
return entry.value;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Set value in cache
|
|
83
|
+
*/
|
|
84
|
+
set(key, value) {
|
|
85
|
+
if (!this.config.enabled) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const now = Date.now();
|
|
89
|
+
// Check if we need to evict (LRU)
|
|
90
|
+
if (this.cache.size >= this.config.maxSize && !this.cache.has(key)) {
|
|
91
|
+
this.evictLRU();
|
|
92
|
+
}
|
|
93
|
+
const entry = {
|
|
94
|
+
key,
|
|
95
|
+
value,
|
|
96
|
+
timestamp: now,
|
|
97
|
+
lastAccessed: now,
|
|
98
|
+
accessCount: 0,
|
|
99
|
+
expiresAt: now + this.config.ttl
|
|
100
|
+
};
|
|
101
|
+
this.cache.set(key, entry);
|
|
102
|
+
this.stats.currentSize = this.cache.size;
|
|
103
|
+
// Persist to disk if enabled
|
|
104
|
+
if (this.persistentCache) {
|
|
105
|
+
this.persistentCache.save(key, entry);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Check if entry has expired
|
|
110
|
+
*/
|
|
111
|
+
isExpired(entry) {
|
|
112
|
+
return Date.now() > entry.expiresAt;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Evict least recently used entry
|
|
116
|
+
* Since Map maintains insertion order, the first entry is the least recently used
|
|
117
|
+
*/
|
|
118
|
+
evictLRU() {
|
|
119
|
+
// Get first entry (least recently used)
|
|
120
|
+
const firstKey = this.cache.keys().next().value;
|
|
121
|
+
if (firstKey) {
|
|
122
|
+
const entry = this.cache.get(firstKey);
|
|
123
|
+
if (entry) {
|
|
124
|
+
this.totalAccessCount -= entry.accessCount; // Update running total
|
|
125
|
+
}
|
|
126
|
+
this.cache.delete(firstKey);
|
|
127
|
+
this.stats.evictions++;
|
|
128
|
+
this.stats.currentSize = this.cache.size;
|
|
129
|
+
// Remove from disk
|
|
130
|
+
if (this.persistentCache) {
|
|
131
|
+
this.persistentCache.delete(firstKey);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Clear all cache entries
|
|
137
|
+
*/
|
|
138
|
+
clear() {
|
|
139
|
+
this.cache.clear();
|
|
140
|
+
this.stats.currentSize = 0;
|
|
141
|
+
this.totalAccessCount = 0; // Reset running total
|
|
142
|
+
if (this.persistentCache) {
|
|
143
|
+
this.persistentCache.clear();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Clean up expired entries
|
|
148
|
+
*/
|
|
149
|
+
cleanup() {
|
|
150
|
+
let removed = 0;
|
|
151
|
+
const now = Date.now();
|
|
152
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
153
|
+
if (now > entry.expiresAt) {
|
|
154
|
+
this.totalAccessCount -= entry.accessCount; // Update running total
|
|
155
|
+
this.cache.delete(key);
|
|
156
|
+
this.stats.expirations++;
|
|
157
|
+
removed++;
|
|
158
|
+
if (this.persistentCache) {
|
|
159
|
+
this.persistentCache.delete(key);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
this.stats.currentSize = this.cache.size;
|
|
164
|
+
return removed;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Get cache statistics
|
|
168
|
+
*/
|
|
169
|
+
getStats() {
|
|
170
|
+
return { ...this.stats };
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Update hit rate and average access count (O(1) complexity)
|
|
174
|
+
*/
|
|
175
|
+
updateStats() {
|
|
176
|
+
const total = this.stats.hits + this.stats.misses;
|
|
177
|
+
this.stats.hitRate = total > 0 ? this.stats.hits / total : 0;
|
|
178
|
+
// Calculate average access count using running total (O(1))
|
|
179
|
+
this.stats.averageAccessCount = this.cache.size > 0 ? this.totalAccessCount / this.cache.size : 0;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Load cache from disk
|
|
183
|
+
*/
|
|
184
|
+
loadFromDisk() {
|
|
185
|
+
if (!this.persistentCache) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const entries = this.persistentCache.loadAll();
|
|
189
|
+
const now = Date.now();
|
|
190
|
+
for (const entry of entries) {
|
|
191
|
+
// Skip expired entries
|
|
192
|
+
if (now > entry.expiresAt) {
|
|
193
|
+
this.stats.expirations++;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
this.cache.set(entry.key, entry);
|
|
197
|
+
this.totalAccessCount += entry.accessCount; // Initialize running total from disk
|
|
198
|
+
}
|
|
199
|
+
this.stats.currentSize = this.cache.size;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Force save all entries to disk and wait for completion
|
|
203
|
+
*/
|
|
204
|
+
async saveToDisk() {
|
|
205
|
+
if (!this.persistentCache) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
// Flush all pending writes and save current state
|
|
209
|
+
await this.persistentCache.flush();
|
|
210
|
+
await this.persistentCache.saveAll(Array.from(this.cache.values()));
|
|
211
|
+
await this.persistentCache.flush(); // Ensure final save completes
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Get cache size
|
|
215
|
+
*/
|
|
216
|
+
size() {
|
|
217
|
+
return this.cache.size;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Check if key exists (without updating access time)
|
|
221
|
+
*/
|
|
222
|
+
has(key) {
|
|
223
|
+
const entry = this.cache.get(key);
|
|
224
|
+
if (!entry) {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
return !this.isExpired(entry);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
//# sourceMappingURL=lru-cache.js.map
|