openTARS / app /tars_assistente.py
ProjectOpenTARS4's picture
Mirror of openTARS 5.2 Ranger (revision 2026110101)
2695555 verified
Raw History Blame Contribute Delete
4.55 kB
"""5.2: o assistente residente (decisões do Enzo, 20/10; tudo ligado por padrão desde 24/10). Sem Qt.
- Ligado: as ações de texto usam o Lite; depois de cada uso o modelo fica na memória só por KEEP_ALIVE_OCIOSO e o
Ollama o descarrega sozinho (25/10, pedido do Enzo: parado, a IA sai da memória). Desligar descarrega na hora (0).
- Iniciar com o sistema: sobe o openTARS escondido (`opentars-gui --residente`), com o ícone na bandeja.
- O atalho das ações de texto é à parte (`tars_atalho.registrar_acoes`).
- aplicar_padroes(): liga tudo isso (e a sugestão pelo programa em uso) uma vez, na primeira abertura."""
import os
import sys
import threading
from pathlib import Path
import requests
import tars_i18n as i18n
import tars_plataforma as plataforma
NOME_AUTOSTART = "opentars-assistente"
def ligado():
return bool(i18n.ler_config().get("assistente"))
KEEP_ALIVE_OCIOSO = "3m" # quanto o modelo do assistente fica na memória depois do último uso
def manter_carregado(host, modelo, carregar, sessao=None):
"""Um pedido sem prompt carrega o modelo por KEEP_ALIVE_OCIOSO (e o Ollama descarrega sozinho) ou descarrega já (0)."""
r = (sessao or requests).post(f"{host}/api/generate", json={"model": modelo, "keep_alive": KEEP_ALIVE_OCIOSO if carregar else 0},
timeout=(5, 180))
if r.status_code != 200:
try:
motivo = r.json().get("error")
except ValueError:
motivo = None
raise RuntimeError(motivo or f"HTTP {r.status_code}")
def ligar(ativo, host, modelo, sessao=None):
"""Guarda a escolha. Ligar não carrega nada (a IA só entra na memória quando é usada); desligar descarrega o modelo
na hora. Erro do Ollama sobe (a tela mostra); a escolha fica guardada."""
i18n.salvar_config(assistente=bool(ativo))
if not ativo:
manter_carregado(host, modelo, False, sessao)
def comando_residente():
if plataforma.sistema() == "windows": # o atalho do Windows roda na pasta do app; pythonw não abre console
python = Path(sys.executable)
sem_console = python.with_name("pythonw.exe")
return [str(sem_console if sem_console.exists() else python), "-m", "tars_qt", "--residente"]
# shortcut: rodando da fonte (sem o pacote instalado) o início com o sistema não acha o opentars-gui; só vale instalado
return [plataforma.lancador("opentars-gui") or "opentars-gui", "--residente"]
def inicia_com_sistema():
try:
return plataforma.arquivo_autostart(NOME_AUTOSTART).exists()
except OSError: # NaoSuportado fora do Linux, até a F5/F6
return False
def iniciar_com_sistema(ativo):
return plataforma.autostart(NOME_AUTOSTART, "openTARS", comando_residente(), bool(ativo))
def aplicar_padroes(em_segundo_plano=True):
"""Decisão do Enzo (24/10): o assistente vem todo ligado: o modelo das ações na memória, o atalho das ações de texto,
o início com o sistema e a sugestão pelo programa em uso. Uma vez só (na primeira abertura, ou na primeira depois de
atualizar), e só o que a pessoa ainda não escolheu: o que ela desligar depois continua desligado. Os testes e a prova
(TARS_SEM_ATALHO=1) não mexem no sistema. O início com o sistema leva segundos no Windows (PowerShell): vai numa
thread. Devolve o que ligou."""
config = i18n.ler_config()
if config.get("assistente_padroes") or os.environ.get("TARS_SEM_ATALHO") == "1":
return []
novos = {"assistente_padroes": True}
if "assistente" not in config:
novos["assistente"] = True
if "sugestoes_programa" not in config:
novos["sugestoes_programa"] = True
i18n.salvar_config(**novos)
ligou = [k for k in ("assistente", "sugestoes_programa") if k in novos]
if "atalho_acoes_registrado" not in config:
import tars_atalho
try:
if tars_atalho.registrar_acoes()[0]:
ligou.append("atalho")
except Exception: # noqa: BLE001 (sem GNOME, sem gsettings: fica o botão Cadastrar na página Assistente)
pass
if not inicia_com_sistema():
def inicio():
try:
iniciar_com_sistema(True)
except Exception: # noqa: BLE001 (sem permissão na pasta: fica a caixa na página Assistente)
pass
if em_segundo_plano:
threading.Thread(target=inicio, daemon=True).start()
else:
inicio()
ligou.append("inicio")
return ligou