Compare commits
No commits in common. "45d9425ed71d8f9c0ee5f5a34624ad6d08ef0e97" and "59a1258dcc5223b5b283c0763b9adbed581cbe4c" have entirely different histories.
45d9425ed7
...
59a1258dcc
7 changed files with 0 additions and 394 deletions
57
.github/workflows/ci.yml
vendored
57
.github/workflows/ci.yml
vendored
|
|
@ -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
|
|
||||||
8
.gitignore
vendored
8
.gitignore
vendored
|
|
@ -8,14 +8,6 @@ __pycache__/
|
||||||
# Custom
|
# Custom
|
||||||
model_small/
|
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
|
# C extensions
|
||||||
*.so
|
*.so
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1495,57 +1495,3 @@ habit_streak:
|
||||||
- сколько дней пью воду
|
- сколько дней пью воду
|
||||||
- трекинг привычек
|
- трекинг привычек
|
||||||
action: {type: 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}
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ import vision_handler
|
||||||
import dev_handlers
|
import dev_handlers
|
||||||
import llm_backend
|
import llm_backend
|
||||||
import wave1_handlers
|
import wave1_handlers
|
||||||
import personality_handlers
|
|
||||||
|
|
||||||
_speak_fn = print
|
_speak_fn = print
|
||||||
_set_clipboard_fn = None
|
_set_clipboard_fn = None
|
||||||
|
|
@ -42,7 +41,6 @@ def set_speak(fn):
|
||||||
vision_handler.set_speak(fn)
|
vision_handler.set_speak(fn)
|
||||||
dev_handlers.set_speak(fn)
|
dev_handlers.set_speak(fn)
|
||||||
wave1_handlers.set_speak(fn)
|
wave1_handlers.set_speak(fn)
|
||||||
personality_handlers.set_speak(fn)
|
|
||||||
|
|
||||||
|
|
||||||
def set_clipboard_fn(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_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_checkin(a, v): wave1_handlers.do_habit_checkin(a, v)
|
||||||
def do_habit_streak(a, v): wave1_handlers.do_habit_streak(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)
|
|
||||||
|
|
|
||||||
10
main.py
10
main.py
|
|
@ -1689,16 +1689,6 @@ def run_action(action):
|
||||||
extensions.do_habit_checkin(action, _current_voice or '')
|
extensions.do_habit_checkin(action, _current_voice or '')
|
||||||
elif t == 'habit_streak':
|
elif t == 'habit_streak':
|
||||||
extensions.do_habit_streak(action, _current_voice or '')
|
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 = ''
|
_current_voice: str = ''
|
||||||
|
|
|
||||||
|
|
@ -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))
|
|
||||||
|
|
@ -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
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue