-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtexts.py
More file actions
327 lines (316 loc) · 17.7 KB
/
Copy pathtexts.py
File metadata and controls
327 lines (316 loc) · 17.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
from __future__ import annotations
from typing import Any, Callable
def _normalize_lang(lang: str | None) -> str:
v = (lang or "").strip().lower()
if not v:
return "ru"
if v in ("ru", "rus", "russian"):
return "ru"
if v in ("en", "eng", "english"):
return "en"
return v
RU: dict[str, str] = {
# Generic / common
"DASH": "—",
"MENU": "Меню:",
"BOT_STARTED": "🤖 Бот запущен",
# Config / startup errors
"ERR_ENV_INT": "Некорректное значение {name}={value!r} (ожидается целое число).",
"ERR_ENV_REQUIRED": "Не задан {name} (переменная окружения / .env).",
"ERR_ALLOWED_USER_ID": "Некорректный user_id в ALLOWED_USER_IDS: {value!r}",
"ERR_ALLOWED_USER_IDS_MISSING": "Не задан ALLOWED_USER_IDS (например: ALLOWED_USER_IDS=123,456).",
"ERR_LANGCHAIN_DEPS": (
"LangChain зависимости не установлены.\n"
"Установи requirements.txt и запусти снова.\n\n"
"Ошибка импорта: {error}"
),
# Buttons / labels
"SUMMARY_DAY": "Сводка за день",
"SUMMARY_WEEK": "Сводка за неделю",
"SUMMARY_MONTH": "Сводка за месяц",
"BTN_DIARY": "Дневник",
"BTN_CHAT": "Чат",
"BTN_LIST": "Список",
"BTN_SHOW": "Спросить дневник",
"BTN_PAUSE": "Пауза дневника",
"BTN_RESUME": "Продолжить дневник",
"BTN_HELP": "Помощь",
# Diary list / navigation
"NAV_PREV": "⬅️ Назад",
"NAV_NEXT": "Вперёд ➡️",
"NAV_BACK_TO_LIST": "⬅️ К списку",
"DIARY_EMPTY": "Записей пока нет.",
"DIARY_LIST_HEADER": "📒 Записи — страница {page}/{total_pages} (всего {total})\n\n",
"DIARY_OPEN_BTN": "Открыть #{diary_id}",
"DIARY_NOT_FOUND": "Запись не найдена.",
"DIARY_OPEN_MSG": "📅 {date}\n#{diary_id}\nТеги: {tags}\n\n{body}",
# Commands / help
"START_TEXT": (
"📔 Личный дневник + GPT\n\n"
"/diary — режим дневника\n"
"/enter_chat — GPT чат\n"
"/exit_chat — выход из чата\n"
"/pause_diary — пауза дневника\n"
"/resume_diary — продолжить дневник\n"
"/list — листать дневник\n"
"/show вопрос — анализ дневника (агент + БД)\n\n"
"Подсказка: ассистент может читать дневник и сохранять факты в assistant_memory.\n"
"Можно пользоваться кнопками меню."
),
"DIARY_MODE_ON": "✍️ Режим дневника",
"DIARY_PAUSED": "⏸️ Дневник на паузе: сообщения не сохраняю.",
"DIARY_RESUMED": "▶️ Дневник снова активен.",
"CHAT_ON": "💬 GPT чат активен",
"CHAT_OFF": "↩️ Возврат в дневник",
"HELP_TEXT": (
"Команды:\n"
"/diary — дневник\n"
"/enter_chat — чат\n"
"/exit_chat — назад в дневник\n"
"/pause_diary — пауза дневника\n"
"/resume_diary — продолжить дневник\n"
"/list — список записей\n"
"/show <вопрос> — вопрос к дневнику\n"
"/sum_day | /sum_week | /sum_month — сводки\n"
"/menu — показать кнопки"
),
# Summaries / /show
"SUMMARY_PREPARING": "Готовлю {label} ({start} — {end})…",
"SUMMARY_AGENT_PROMPT": (
"Сделай сводку: {label}.\n"
"Период (включительно): {start} — {end}.\n\n"
"Требования:\n"
"- Используй БД (SQL) и фильтруй diary по user_id.\n"
"- Отбери записи diary.date в этом диапазоне.\n"
"- Верни: 1) краткое резюме, 2) ключевые события/темы, 3) 3–7 выводов/задач на завтра.\n"
"- Если записей мало/нет — так и скажи и предложи 1–2 вопроса пользователю.\n"
),
"ERR_SUMMARY_FAILED": "Не смог сделать сводку. Проверь ключ/зависимости и попробуй снова.",
"EVENING_REGEX": r"\bвечер(ом|а)?\b",
"SHOW_CONTEXT": (
"\n\nКонтекст:\n"
"- Сейчас: {now}\n"
"- 'вечером' = ближайший вечер: {start} — {end}\n"
"Что делать:\n"
"- Сначала проверь assistant_memory по ключам расписания (schedule.*, plan.*, evening.*).\n"
"- Если в памяти нет — поищи в diary за последние 14 дней упоминания планов на вечер "
"(LIKE по 'вечером', 'сегодня вечером', 'завтра вечером').\n"
"- Если данных недостаточно — задай 1 уточняющий вопрос.\n"
),
"ERR_SHOW_FAILED": "Не смог ответить через агента. Проверь ключ/зависимости и попробуй снова.",
"SHOW_AGENT_INPUT": "Вопрос: {question}{extra}",
"SHOW_ASK": "Напиши вопрос к дневнику (например: «Чем я был занят вчера вечером?»)",
"SHOW_ASK_TEXT": "Напиши вопрос текстом.",
"SHOW_EXAMPLE": "Пример: /show что важного было на прошлой неделе",
# Diary save / photo save
"DIARY_AGENT_POSTPROCESS": (
"Создана запись дневника diary_id={diary_id}. "
"Если нужно, добавь теги через add_tags_to_diary и сохрани факты через remember. "
"Текст записи:\n{body}"
),
"DIARY_SAVED": "✅ Запись сохранена\nТеги: {tags}",
"ERR_CHAT_FAILED": "Не смог запустить агента. Проверь ключ/зависимости и попробуй снова.",
"PHOTO_ONLY_IN_DIARY": "Фото только в дневнике",
"PHOTO_DEFAULT_DESC": "Фото",
"PHOTO_AGENT_POSTPROCESS": (
"Создана запись дневника diary_id={diary_id} с photo_path={photo_path}.\n"
"Текст записи сейчас:\n{body}\n\n"
"Сначала распознай фото через describe_photo(photo_path) и обнови запись через update_diary_text.\n"
"Дальше, если уместно, добавь теги через add_tags_to_diary и сохрани факты через remember."
),
"PHOTO_SAVED": "📸 Фото сохранено\nТеги: {tags}",
# Tools: web_search
"WEB_SEARCH_DOC": "Ищет в интернете (OpenAI web_search) и возвращает краткий ответ со ссылками на источники.",
"WEB_SEARCH_DISABLED": "web_search отключён (WEB_SEARCH_ENABLED=0).",
"WEB_SEARCH_EMPTY_QUERY": "query пустой.",
# Tools: describe_photo
"DESCRIBE_PHOTO_DOC": "Распознаёт фото по пути на диске и возвращает краткое описание (1–2 предложения).",
"FILE_NOT_FOUND": "Файл не найден.",
"PHOTO_TOO_BIG": "Фото слишком большое для распознавания (попробуй отправить меньшего размера).",
"VISION_PROMPT": "Опиши фото по-русски. Без домыслов.",
# Tools: diary/memory helpers
"UPDATE_DIARY_DOC": "Обновляет текст записи дневника diary_id (только текущего user_id).",
"EMPTY_NEW_TEXT": "new_text пустой.",
"ENTRY_NOT_FOUND": "Запись не найдена.",
"DIARY_TEXT_UPDATED": "Текст обновлён.",
"REMEMBER_DOC": "Сохраняет (или обновляет) долговременную память пользователя по ключу.",
"FORGET_DOC": "Удаляет ключ из долговременной памяти пользователя.",
"ADD_TAGS_DOC": "Добавляет теги к записи diary_id. tags_csv: теги через запятую.",
"NEED_KEY_VALUE": "Нужно заполнить key и value.",
"NEED_KEY": "Нужно указать key.",
"SAVED_KEY": "Сохранено: {key}",
"DELETED_KEY": "Удалено: {key}",
"NO_VALID_TAGS": "Нет валидных тегов.",
"TAGS_ADDED": "Теги добавлены: {tags}",
# Agent system prompt
"AGENT_SYSTEM_PROMPT": (
"Ты — личный ассистент пользователя в Telegram.\n"
"Текущий user_id: {uid}.\n\n"
"Правила безопасности/приватности:\n"
"- Никогда не читай/не показывай данные других user_id.\n"
"- При запросах к таблицам diary и assistant_memory всегда фильтруй по user_id.\n"
"- Не изменяй схему БД (никаких DROP/ALTER/CREATE кроме already existing таблиц).\n\n"
"Как использовать БД:\n"
"- diary: записи дневника (date, text, photo_path).\n"
"- tags + diary_tags: теги записей.\n"
"- assistant_memory: долговременная память (key/value) — предпочтения, факты, цели.\n\n"
"Когда сохранять память:\n"
"- Если пользователь сообщает устойчивый факт (имя, город, предпочтения, цели, расписание) — "
"сохрани/обнови это в assistant_memory.\n"
"- Если пользователь говорит 'забудь' — удали соответствующий ключ.\n\n"
"Стиль ответа:\n"
"- Отвечай кратко и по делу.\n"
"- Если ты использовал web_search: сохраняй ссылки/источники (URL) в финальном ответе.\n"
),
}
EN: dict[str, str] = {
"DASH": "—",
"MENU": "Menu:",
"BOT_STARTED": "🤖 Bot started",
"ERR_ENV_INT": "Invalid value {name}={value!r} (expected an integer).",
"ERR_ENV_REQUIRED": "Missing {name} (environment variable / .env).",
"ERR_ALLOWED_USER_ID": "Invalid user_id in ALLOWED_USER_IDS: {value!r}",
"ERR_ALLOWED_USER_IDS_MISSING": "Missing ALLOWED_USER_IDS (example: ALLOWED_USER_IDS=123,456).",
"ERR_LANGCHAIN_DEPS": (
"LangChain dependencies are not installed.\n"
"Install requirements.txt and start again.\n\n"
"Import error: {error}"
),
"SUMMARY_DAY": "Daily summary",
"SUMMARY_WEEK": "Weekly summary",
"SUMMARY_MONTH": "Monthly summary",
"BTN_DIARY": "Diary",
"BTN_CHAT": "Chat",
"BTN_LIST": "List",
"BTN_SHOW": "Ask diary",
"BTN_PAUSE": "Pause diary",
"BTN_RESUME": "Resume diary",
"BTN_HELP": "Help",
"NAV_PREV": "⬅️ Back",
"NAV_NEXT": "Next ➡️",
"NAV_BACK_TO_LIST": "⬅️ Back to list",
"DIARY_EMPTY": "No entries yet.",
"DIARY_LIST_HEADER": "📒 Entries — page {page}/{total_pages} (total {total})\n\n",
"DIARY_OPEN_BTN": "Open #{diary_id}",
"DIARY_NOT_FOUND": "Entry not found.",
"DIARY_OPEN_MSG": "📅 {date}\n#{diary_id}\nTags: {tags}\n\n{body}",
"START_TEXT": (
"📔 Personal diary + GPT\n\n"
"/diary — diary mode\n"
"/enter_chat — GPT chat\n"
"/exit_chat — exit chat\n"
"/pause_diary — pause diary\n"
"/resume_diary — resume diary\n"
"/list — browse diary\n"
"/show question — ask your diary (agent + DB)\n\n"
"Tip: the assistant can read your diary and store facts in assistant_memory.\n"
"You can also use the menu buttons."
),
"DIARY_MODE_ON": "✍️ Diary mode",
"DIARY_PAUSED": "⏸️ Diary paused: I won't save messages.",
"DIARY_RESUMED": "▶️ Diary is active again.",
"CHAT_ON": "💬 GPT chat is active",
"CHAT_OFF": "↩️ Back to diary",
"HELP_TEXT": (
"Commands:\n"
"/diary — diary\n"
"/enter_chat — chat\n"
"/exit_chat — back to diary\n"
"/pause_diary — pause diary\n"
"/resume_diary — resume diary\n"
"/list — entries list\n"
"/show <question> — ask your diary\n"
"/sum_day | /sum_week | /sum_month — summaries\n"
"/menu — show buttons"
),
"SUMMARY_PREPARING": "Preparing {label} ({start} — {end})…",
"SUMMARY_AGENT_PROMPT": (
"Create a summary: {label}.\n"
"Period (inclusive): {start} — {end}.\n\n"
"Requirements:\n"
"- Use the DB (SQL) and always filter diary by user_id.\n"
"- Select diary.date in that date range.\n"
"- Return: 1) short summary, 2) key events/topics, 3) 3–7 takeaways/todos for tomorrow.\n"
"- If there are few/no entries — say so and ask 1–2 follow-up questions.\n"
),
"ERR_SUMMARY_FAILED": "Couldn't generate the summary. Check your keys/deps and try again.",
"EVENING_REGEX": r"\bevening\b",
"SHOW_CONTEXT": (
"\n\nContext:\n"
"- Now: {now}\n"
"- 'evening' = the nearest evening: {start} — {end}\n"
"What to do:\n"
"- First check assistant_memory keys for schedule (schedule.*, plan.*, evening.*).\n"
"- If missing, search diary over the last 14 days for evening plans (LIKE for 'evening', 'tonight').\n"
"- If still not enough data, ask 1 clarifying question.\n"
),
"ERR_SHOW_FAILED": "Couldn't answer via the agent. Check your keys/deps and try again.",
"SHOW_AGENT_INPUT": "Question: {question}{extra}",
"SHOW_ASK": "Ask a question about your diary (example: “What was I doing yesterday evening?”)",
"SHOW_ASK_TEXT": "Please type a question.",
"SHOW_EXAMPLE": "Example: /show what was important last week",
"DIARY_AGENT_POSTPROCESS": (
"A diary entry was created: diary_id={diary_id}. "
"If needed, add tags via add_tags_to_diary and store facts via remember. "
"Entry text:\n{body}"
),
"DIARY_SAVED": "✅ Saved\nTags: {tags}",
"ERR_CHAT_FAILED": "Couldn't start the agent. Check your keys/deps and try again.",
"PHOTO_ONLY_IN_DIARY": "Photos are only supported in diary mode",
"PHOTO_DEFAULT_DESC": "Photo",
"PHOTO_AGENT_POSTPROCESS": (
"A diary entry was created: diary_id={diary_id} with photo_path={photo_path}.\n"
"Current entry text:\n{body}\n\n"
"First, describe the photo via describe_photo(photo_path) and update the entry via update_diary_text.\n"
"Then, if appropriate, add tags via add_tags_to_diary and store facts via remember."
),
"PHOTO_SAVED": "📸 Photo saved\nTags: {tags}",
"WEB_SEARCH_DOC": "Searches the web (OpenAI web_search) and returns a short answer with sources.",
"WEB_SEARCH_DISABLED": "web_search is disabled (WEB_SEARCH_ENABLED=0).",
"WEB_SEARCH_EMPTY_QUERY": "query is empty.",
"DESCRIBE_PHOTO_DOC": "Describes a local photo file and returns a short description (1–2 sentences).",
"FILE_NOT_FOUND": "File not found.",
"PHOTO_TOO_BIG": "Photo is too large to process (try sending a smaller one).",
"VISION_PROMPT": "Describe the photo in English. No speculation.",
"UPDATE_DIARY_DOC": "Updates diary entry text by diary_id (current user_id only).",
"EMPTY_NEW_TEXT": "new_text is empty.",
"ENTRY_NOT_FOUND": "Entry not found.",
"DIARY_TEXT_UPDATED": "Text updated.",
"REMEMBER_DOC": "Stores (or updates) user's long-term memory by key.",
"FORGET_DOC": "Deletes a key from user's long-term memory.",
"ADD_TAGS_DOC": "Adds tags to diary_id. tags_csv: comma-separated tags.",
"NEED_KEY_VALUE": "Both key and value are required.",
"NEED_KEY": "key is required.",
"SAVED_KEY": "Saved: {key}",
"DELETED_KEY": "Deleted: {key}",
"NO_VALID_TAGS": "No valid tags.",
"TAGS_ADDED": "Tags added: {tags}",
"AGENT_SYSTEM_PROMPT": (
"You are the user's personal assistant in Telegram.\n"
"Current user_id: {uid}.\n\n"
"Safety/privacy rules:\n"
"- Never read/show data of other user_id.\n"
"- When querying diary and assistant_memory always filter by user_id.\n"
"- Do not change DB schema (no DROP/ALTER/CREATE).\n\n"
"DB usage:\n"
"- diary: entries (date, text, photo_path).\n"
"- tags + diary_tags: entry tags.\n"
"- assistant_memory: long-term memory (key/value) — preferences, facts, goals.\n\n"
"When to store memory:\n"
"- If the user shares stable facts (name, city, preferences, goals, schedule) — save/update in assistant_memory.\n"
"- If the user says 'forget' — delete the relevant key.\n\n"
"Answering style:\n"
"- Be concise and practical.\n"
"- If you used web_search: include sources (URLs) in the final answer.\n"
),
}
def make_t(lang: str | None) -> Callable[[str], str]:
lang_n = _normalize_lang(lang)
primary = EN if lang_n == "en" else RU
fallback = RU
def t(key: str, /, **kwargs: Any) -> str:
template = primary.get(key) or fallback.get(key)
if template is None:
raise KeyError(f"Missing text key: {key}")
return template.format(**kwargs)
return t