feat: criado repositório jhonny-editor no Gitea

criado repositório jhonny-editor no Gitea
adicionado script admin/deploy.command com commit automático
atualizado admin/DEV-NOTES.md com template limpo

Resumo:
- 32 arquivos alterados
- 16 novos
- 15 modificados
- 0 removidos

 16 files changed, 638 insertions(+), 61 deletions(-)

Arquivos:
  - code/engine/skills/boas-praticas-oo.md -> .agents/skills/boas-praticas-oo/SKILL.md
  - AGENTS.md
  - code/cep-plugin/index.html
  - code/cep-plugin/main.js
  - code/cep-plugin/styles.css
  - code/engine/integracoes/visual/apple_vision.py
  - code/engine/integracoes/visual/apple_vision_runner.swift
  - code/engine/integracoes/visual/extrator_ffmpeg.py
  - code/engine/scanner/__init__.py
  - code/engine/scanner/configuracao_visual.py
  - code/engine/scanner/coordenacao/__init__.py
  - code/engine/scanner/retakes/adaptador_de_falas.py
  - code/engine/scanner/retakes/agrupador_de_takes.py
  - code/engine/scanner/retakes/detector_de_reinicios.py
  - code/engine/scanner/retakes/modelos_de_retakes.py
  - code/engine/scanner/visual.py
  - .jhonny/
  - .retakes/
  - CODING_STANDARDS.md
  - code/engine/executar_retakes.py
  - code/engine/persistencia/
  - code/engine/scanner/retakes/__init__.py
  - code/engine/scanner/retakes/carregador_de_artefatos.py
  - code/engine/scanner/retakes/classificador_de_retakes.py
  - code/engine/scanner/retakes/coordenador_de_retakes.py
  - code/engine/scanner/retakes/deteccao_de_retakes.py
  - code/engine/scanner/retakes/gerador_de_evidencias.py
  - code/engine/scanner/retakes/repositorio_de_retakes.py
  - code/engine/testes/test_consultas_de_persistencia.py
  - code/engine/testes/test_deteccao_de_retakes.py
  - code/engine/testes/test_persistencia_sqlite.py
  - code/relatorios/audio/arquivos/92f97877259a86a8095b1eecafdefe357212a12247d04b78df60e0c0ae38b2ea/
This commit is contained in:
João Henrique
2026-09-08 17:13:19 -04:00
parent b9bf3b2863
commit ee181360c8
40 changed files with 3674 additions and 61 deletions
+87 -10
View File
@@ -1,10 +1,13 @@
"""Adapter Python para o runner Swift que concentra Vision e Apple Intelligence."""
import atexit
import json
import os
from pathlib import Path
import queue
import subprocess
import tempfile
import threading
from typing import Any, Callable
from ...scanner.modelos import EvidenciaVisual
@@ -18,27 +21,101 @@ RECURSOS_PADRAO = (
)
class _TrabalhadorPersistente:
"""Um processo do runner Swift vivo entre chamadas, evitando recarregar Vision/Foundation Models a cada frame."""
def __init__(self, executavel: Path) -> None:
self.processo = subprocess.Popen([str(executavel)], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, text=True, bufsize=1)
def executar(self, carga: dict[str, Any], timeout: float) -> dict[str, Any]:
if self.processo.poll() is not None or not self.processo.stdin or not self.processo.stdout:
raise RuntimeError("Runner Apple Vision não está mais em execução.")
try:
self.processo.stdin.write(json.dumps(carga) + "\n")
self.processo.stdin.flush()
except (BrokenPipeError, ValueError) as exc:
raise RuntimeError("Runner Apple Vision não aceitou o pedido.") from exc
resposta: dict[str, str] = {}
falha: list[BaseException] = []
def ler() -> None:
try:
resposta["linha"] = self.processo.stdout.readline() # type: ignore[union-attr]
except BaseException as exc: # noqa: BLE001 - repassado à thread principal
falha.append(exc)
leitor = threading.Thread(target=ler, daemon=True)
leitor.start()
leitor.join(timeout)
if leitor.is_alive():
self.encerrar()
raise TimeoutError(f"Runner Apple Vision excedeu {timeout}s.")
if falha:
raise falha[0]
linha = resposta.get("linha", "")
if not linha:
detalhe = (self.processo.stderr.read() if self.processo.stderr else "") or "runner encerrou sem resposta"
raise RuntimeError(f"Runner Apple Vision falhou: {detalhe.strip()}")
return json.loads(linha)
def encerrar(self) -> None:
if self.processo.poll() is None:
self.processo.kill()
try:
self.processo.wait(timeout=5)
except subprocess.TimeoutExpired:
pass
class ExecutorDoRunnerApple:
"""Compila o runner Swift quando necessário e o executa em processo isolado."""
"""Compila o runner Swift quando necessário e mantém um pool de processos persistentes."""
def __init__(self, fonte: str | Path | None = None, compilador: str = "swiftc",
diretorio_de_build: str | Path | None = None) -> None:
diretorio_de_build: str | Path | None = None, trabalhadores: int = 4) -> None:
self.fonte = Path(fonte) if fonte else Path(__file__).with_name("apple_vision_runner.swift")
self.compilador = compilador
self.diretorio_de_build = Path(diretorio_de_build) if diretorio_de_build else (
Path(tempfile.gettempdir()) / "jhonny-apple-vision")
self.trabalhadores = max(1, trabalhadores)
self._pool: "queue.Queue[_TrabalhadorPersistente]" | None = None
self._trava = threading.Lock()
self._todos: list[_TrabalhadorPersistente] = []
atexit.register(self.encerrar)
def executar(self, carga: dict[str, Any], timeout: float) -> dict[str, Any]:
executavel = self._garantir_compilado()
processo = subprocess.run([str(executavel)], input=json.dumps(carga), capture_output=True,
text=True, timeout=timeout)
if processo.returncode != 0:
detalhe = processo.stderr.strip() or "runner Apple terminou sem diagnóstico"
raise RuntimeError(f"Runner Apple Vision falhou: {detalhe}")
pool = self._garantir_pool(executavel)
trabalhador = pool.get()
try:
return json.loads(processo.stdout)
except json.JSONDecodeError as exc:
raise RuntimeError(f"Runner Apple Vision devolveu JSON inválido: {processo.stdout!r}") from exc
return trabalhador.executar(carga, timeout)
except Exception:
trabalhador.encerrar()
with self._trava:
self._todos.remove(trabalhador)
trabalhador = _TrabalhadorPersistente(executavel)
self._todos.append(trabalhador)
raise
finally:
pool.put(trabalhador)
def encerrar(self) -> None:
with self._trava:
for trabalhador in self._todos:
trabalhador.encerrar()
self._todos.clear()
self._pool = None
def _garantir_pool(self, executavel: Path) -> "queue.Queue[_TrabalhadorPersistente]":
with self._trava:
if self._pool is None:
self._pool = queue.Queue()
for _ in range(self.trabalhadores):
trabalhador = _TrabalhadorPersistente(executavel)
self._todos.append(trabalhador)
self._pool.put(trabalhador)
return self._pool
def _garantir_compilado(self) -> Path:
if not self.fonte.is_file():