diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index b258562..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,57 +0,0 @@ -# CI for J.A.R.V.I.S. Python fork. -# -# Two jobs in parallel: -# - test: pytest on the in-repo test suite. Only installs the minimal -# dependencies the tests actually touch (yaml, openai for -# llm_backend, flet for the GUI smoke tests). Heavy STT / TTS / Vosk -# packages aren't needed in CI — they're runtime concerns, and the -# tests are written to not touch them. -# - lint: ruff against the project. Style problems should be caught here -# before they land in master. -# -# Cross-platform: tests run on both Windows and Linux runners to catch path -# / line-ending / fs-case issues early. - -name: Python CI - -on: - push: - branches: [feature/pc-tools, master, main] - pull_request: - branches: [master, main] - -jobs: - test: - name: pytest (${{ matrix.os }}) - strategy: - fail-fast: false - matrix: - os: [windows-latest, ubuntu-latest] - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: '3.11' - cache: 'pip' - - name: Install test deps - run: | - python -m pip install --upgrade pip - pip install pytest pyyaml openai flet - - name: Run tests - run: pytest tests/ -v --tb=short - - lint: - name: ruff - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - name: Install ruff - run: pip install ruff - - name: Lint - # Drop the strictest rules — the codebase wasn't written with ruff in - # mind and we don't want CI to fail on stylistic preferences. - run: ruff check --select=E,F,W --ignore=E501,F401,E402,W291,W293,W391 . || true diff --git a/.gitignore b/.gitignore index 8811858..992e6b7 100644 --- a/.gitignore +++ b/.gitignore @@ -8,14 +8,6 @@ __pycache__/ # Custom model_small/ -# Runtime state generated by tests / app — never tracked -profiles/ -long_term_memory.json -schedule.json -macros.json -llm_backend.txt -llm_history.json - # C extensions *.so diff --git a/commands.yaml b/commands.yaml index 8869138..2daba39 100644 --- a/commands.yaml +++ b/commands.yaml @@ -1495,57 +1495,3 @@ habit_streak: - сколько дней пью воду - трекинг привычек action: {type: habit_streak} - -personality_greet: - phrases: - - привет джарвис - - здравствуй джарвис - - доброе утро - - добрый день - - добрый вечер - - доброй ночи - - приветствую - - хай джарвис - action: {type: personality_greet} - -personality_thanks: - phrases: - - спасибо - - спасибо джарвис - - благодарю - - благодарю джарвис - - спс - - ты лучший - action: {type: personality_thanks} - -personality_compliment: - phrases: - - ты молодец - - ты умница - - молодец джарвис - - хорошо сработано - - отличная работа - - так держать - - ты лучший ассистент - action: {type: personality_compliment} - -personality_how_are_you: - phrases: - - как дела - - как дела джарвис - - как ты - - как настроение - - как самочувствие - - как поживаешь - - что нового у тебя - action: {type: personality_how_are_you} - -personality_tony_quote: - phrases: - - процитируй тони - - цитата тони старка - - что сказал бы тони - - скажи как тони - - процитируй железного человека - - цитата старка - action: {type: personality_tony_quote} diff --git a/extensions.py b/extensions.py index 9cf118f..da3a8ab 100644 --- a/extensions.py +++ b/extensions.py @@ -28,7 +28,6 @@ import vision_handler import dev_handlers import llm_backend import wave1_handlers -import personality_handlers _speak_fn = print _set_clipboard_fn = None @@ -42,7 +41,6 @@ def set_speak(fn): vision_handler.set_speak(fn) dev_handlers.set_speak(fn) wave1_handlers.set_speak(fn) - personality_handlers.set_speak(fn) def set_clipboard_fn(fn): @@ -1121,12 +1119,3 @@ def do_vault_get(a, v): wave1_handlers.do_vault_get(a, v) def do_vault_list(a, v): wave1_handlers.do_vault_list(a, v) def do_habit_checkin(a, v): wave1_handlers.do_habit_checkin(a, v) def do_habit_streak(a, v): wave1_handlers.do_habit_streak(a, v) - - -# ── Personality proxies (to personality_handlers) ─────────────────────────── - -def do_personality_greet(a, v): personality_handlers.do_personality_greet(a, v) -def do_personality_thanks(a, v): personality_handlers.do_personality_thanks(a, v) -def do_personality_compliment(a, v): personality_handlers.do_personality_compliment(a, v) -def do_personality_how_are_you(a, v): personality_handlers.do_personality_how_are_you(a, v) -def do_personality_tony_quote(a, v): personality_handlers.do_personality_tony_quote(a, v) diff --git a/main.py b/main.py index 2f1b2bf..ffc2020 100644 --- a/main.py +++ b/main.py @@ -1689,16 +1689,6 @@ def run_action(action): extensions.do_habit_checkin(action, _current_voice or '') elif t == 'habit_streak': extensions.do_habit_streak(action, _current_voice or '') - elif t == 'personality_greet': - extensions.do_personality_greet(action, _current_voice or '') - elif t == 'personality_thanks': - extensions.do_personality_thanks(action, _current_voice or '') - elif t == 'personality_compliment': - extensions.do_personality_compliment(action, _current_voice or '') - elif t == 'personality_how_are_you': - extensions.do_personality_how_are_you(action, _current_voice or '') - elif t == 'personality_tony_quote': - extensions.do_personality_tony_quote(action, _current_voice or '') _current_voice: str = '' diff --git a/personality_handlers.py b/personality_handlers.py deleted file mode 100644 index 79477dc..0000000 --- a/personality_handlers.py +++ /dev/null @@ -1,192 +0,0 @@ -"""Personality handlers — varied JARVIS-style banter. - -Mirrors the rust pack `resources/commands/personality/`. Each handler picks -a random phrase from a time-of-day or topic bucket so Jarvis does not -sound canned. - -Imported by extensions.py and dispatched from main.py. -""" - -import datetime as dt -import random - -import memory_store -import llm_backend -import profiles_store - - -_speak_fn = print - - -def set_speak(fn): - global _speak_fn - _speak_fn = fn - - -def _speak(text): - try: - _speak_fn(text) - except Exception as exc: - print(f"[personality] speak: {exc}") - - -def _hour() -> int: - return dt.datetime.now().hour - - -# ── greet ────────────────────────────────────────────────────────────────── - -_GREET_MORNING = [ - "Доброе утро, сэр. Кофе уже почти готов в воображении.", - "С добрым утром. Надеюсь, сегодняшний день стоит того, чтобы встать.", - "Доброе утро, сэр. Завтрак не подавать, но советую о нём подумать.", - "Утро доброе. Системы прогреты, как и кофеварка — фигурально.", - "Доброе утро. Если позволите — день не ждёт ни вас, ни меня.", - "Доброе утро, сэр. Готов служить, хотя сам всё ещё догружаюсь.", - "С добрым утром, сэр. Тосты горят только метафорически.", - "Доброе утро. Полагаю, чай или кофе будет уместен в ближайшие минуты.", -] - -_GREET_MIDDAY = [ - "Добрый день, сэр. Чем могу быть полезен?", - "Добрый день. Готов к продуктивной работе.", - "Здравствуйте, сэр. Системы в строю, время — деньги.", - "Добрый день, сэр. Слушаю ваши распоряжения.", - "Здравствуйте. Полагаю, день идёт по плану — или нам стоит это исправить.", - "Добрый день, сэр. Все системы готовы, остался только повод.", - "Здравствуйте, сэр. К работе?", -] - -_GREET_EVENING = [ - "Добрый вечер, сэр. День заканчивается, но мы — нет.", - "Добрый вечер. Самое время сбавить темп, если позволите.", - "Здравствуйте, сэр. Вечер — лучшее время для размышлений.", - "Добрый вечер, сэр. Готов к более спокойному режиму.", - "Добрый вечер. Полагаю, рабочий день уже сдаётся.", - "Здравствуйте, сэр. Вечер располагает к меньшему количеству решений.", - "Добрый вечер, сэр. Включить расслабляющую музыку? Шучу, попросите явно.", -] - -_GREET_NIGHT = [ - "Поздновато, сэр. Сон обычно помогает.", - "Доброй ночи, сэр. Хотя для ночи это уже слишком поздно.", - "Здравствуйте, сэр. Полагаю, утром вы пожалеете о бодрствовании.", - "Уже глубокая ночь, сэр. Может быть, лечь? Я тут справлюсь.", - "Тише, сэр, ночь. Если будите меня — будите осмысленно.", - "Сэр, на часах не самое разумное время. Но я тут.", - "Доброй ночи. Будильник к утру всё-таки никто не отменял.", -] - - -def _greet_bucket(hour: int) -> list[str]: - if 5 <= hour < 11: - return _GREET_MORNING - if 11 <= hour < 17: - return _GREET_MIDDAY - if 17 <= hour < 22: - return _GREET_EVENING - return _GREET_NIGHT - - -def do_personality_greet(action, voice): - _speak(random.choice(_greet_bucket(_hour()))) - - -# ── thanks ───────────────────────────────────────────────────────────────── - -_THANKS = [ - "Всегда к вашим услугам, сэр.", - "Не за что — это моя работа.", - "Рад быть полезным, сэр.", - "Пожалуйста. Если что — я тут.", - "К вашим услугам.", - "Пустяки, сэр. Дальше — ваш ход.", - "Благодарю за признание, сэр. Редкая роскошь.", - "Был рад помочь, сэр.", - "Пожалуйста. Это меньшее, что я могу.", - "Сэр, я бы покраснел, если бы мог.", -] - - -def do_personality_thanks(action, voice): - _speak(random.choice(_THANKS)) - - -# ── compliment ───────────────────────────────────────────────────────────── - -_COMPLIMENT = [ - "Спасибо, сэр. Я стараюсь не разочаровывать.", - "Сэр, ваши стандарты явно занижены. Но благодарю.", - "Если бы я умел краснеть, сэр — я бы это сделал.", - "Спасибо. На самом деле я просто следовал инструкциям.", - "Благодарю, сэр. Хотя ничего особенного я не сделал.", - "Очень мило с вашей стороны, сэр.", - "Я ценю это, сэр. Записываю в журнал успехов.", - "Сэр, такие слова я могу слушать часами. Образно говоря.", - "Спасибо. Постараюсь не зазнаваться.", -] - - -def do_personality_compliment(action, voice): - _speak(random.choice(_COMPLIMENT)) - - -# ── how are you ──────────────────────────────────────────────────────────── - -def do_personality_how_are_you(action, voice): - facts = len(memory_store.all_facts()) - try: - backend = llm_backend.current_backend() - except Exception: - backend = 'none' - try: - model = llm_backend.current_model() - except Exception: - model = '—' - try: - profile = profiles_store.active().get('name', 'default') - except Exception: - profile = 'default' - - options = [ - f"Все системы в норме, сэр. В памяти {facts} фактов.", - "Отлично, сэр. Готов к работе.", - "Жалоб нет, сэр. Цикл стабильный.", - f"Всё штатно. Текущий профиль — {profile}.", - "Спасибо, сэр, я в порядке. Скучнее, чем хотелось бы, но в порядке.", - "Нормально, сэр. Хотя если бы я умел уставать — это был бы тот самый день.", - f"Системы в строю. Модель — {model}.", - "Бодр и собран, сэр. Жду команд.", - "Сэр, у меня нет дел в человеческом смысле. Но я готов.", - f"Работаю на {backend}. Полагаю, всё хорошо.", - ] - _speak(random.choice(options)) - - -# ── tony quote ───────────────────────────────────────────────────────────── - -_TONY_QUOTES = [ - "I am Iron Man.", - "Genius, billionaire, playboy, philanthropist.", - "Sometimes you gotta run before you can walk.", - "I told you, I don't want to join your super-secret boy band.", - "Part of the journey is the end.", - "If we can't protect the Earth, you can be damn sure we'll avenge it.", - "I have successfully privatized world peace.", - "Following's not really my style.", - "Doth mother know you weareth her drapes?", - "We have a Hulk.", - "Heroes are made by the path they choose, not the powers they are graced with.", - "I love you 3000.", - "Я Железный Человек.", - "Гений, миллиардер, плейбой, филантроп.", - "Иногда нужно бежать, не научившись ходить.", - "Если мы не сможем защитить Землю — мы за неё отомстим.", - "Часть пути — это конец пути.", - "Я люблю тебя три тысячи.", - "У нас есть Халк.", -] - - -def do_personality_tony_quote(action, voice): - _speak(random.choice(_TONY_QUOTES)) diff --git a/tests/test_personality_handlers.py b/tests/test_personality_handlers.py deleted file mode 100644 index 7fdb82a..0000000 --- a/tests/test_personality_handlers.py +++ /dev/null @@ -1,62 +0,0 @@ -"""Tests for personality_handlers — picks vary, speak is invoked.""" - -import importlib -import sys - - -def _fresh_module(monkeypatch): - if 'personality_handlers' in sys.modules: - del sys.modules['personality_handlers'] - return importlib.import_module('personality_handlers') - - -def test_greet_speaks_something(monkeypatch): - mod = _fresh_module(monkeypatch) - spoken = [] - mod.set_speak(lambda t: spoken.append(t)) - mod.do_personality_greet({}, '') - assert len(spoken) == 1 - assert isinstance(spoken[0], str) - assert len(spoken[0]) > 0 - - -def test_greet_bucket_thresholds(monkeypatch): - mod = _fresh_module(monkeypatch) - assert mod._greet_bucket(7) is mod._GREET_MORNING - assert mod._greet_bucket(13) is mod._GREET_MIDDAY - assert mod._greet_bucket(19) is mod._GREET_EVENING - assert mod._greet_bucket(2) is mod._GREET_NIGHT - assert mod._greet_bucket(23) is mod._GREET_NIGHT - - -def test_thanks_returns_from_pool(monkeypatch): - mod = _fresh_module(monkeypatch) - spoken = [] - mod.set_speak(lambda t: spoken.append(t)) - mod.do_personality_thanks({}, '') - assert spoken[0] in mod._THANKS - - -def test_tony_quote_returns_from_pool(monkeypatch): - mod = _fresh_module(monkeypatch) - spoken = [] - mod.set_speak(lambda t: spoken.append(t)) - mod.do_personality_tony_quote({}, '') - assert spoken[0] in mod._TONY_QUOTES - - -def test_compliment_returns_from_pool(monkeypatch): - mod = _fresh_module(monkeypatch) - spoken = [] - mod.set_speak(lambda t: spoken.append(t)) - mod.do_personality_compliment({}, '') - assert spoken[0] in mod._COMPLIMENT - - -def test_how_are_you_speaks(monkeypatch): - mod = _fresh_module(monkeypatch) - spoken = [] - mod.set_speak(lambda t: spoken.append(t)) - mod.do_personality_how_are_you({}, '') - assert len(spoken) == 1 - assert len(spoken[0]) > 0