feat: criada a aba Análises no painel CEP para ler do banco (anali
- criada a aba Análises no painel CEP para ler do banco (analises.db) e copiar/abrir o J-SOM de som (transcrição por cena) e o de imagem (evidências visuais), com novo módulo engine/gerar_relatorio_de_analises.py e testes - criado o módulo engine/editor (leitura do plano de edição em JSON, tradução de tempos de origem para a timeline ativa e aplicação de cortes/zooms/marcadores no Premiere via MCP) e o ponto de entrada engine/aplicar_plano_de_edicao.py, com testes; corrigido engine/integracoes/premiere/cliente_mcp.py para levantar os erros específicos já declarados em erros_mcp.py em vez de RuntimeError genérico Resumo: - 15 arquivos alterados - 8 novos - 5 modificados - 2 removidos 7 files changed, 172 insertions(+), 8 deletions(-) Arquivos: - .jhonny/analises.db - .jhonny/analises.db-shm - .jhonny/analises.db-wal - code/cep-plugin/index.html - code/cep-plugin/main.js - code/cep-plugin/styles.css - code/engine/integracoes/premiere/cliente_mcp.py - code/engine/aplicar_plano_de_edicao.py - code/engine/editor/ - code/engine/gerar_relatorio_de_analises.py - code/engine/testes/duplos_de_premiere.py - code/engine/testes/test_aplicador_de_plano_de_edicao.py - code/engine/testes/test_gerar_relatorio_de_analises.py - code/engine/testes/test_leitor_de_plano_de_edicao.py - code/engine/testes/test_mapeador_de_tempo.py
This commit is contained in:
@@ -1,23 +1,43 @@
|
||||
"""Cliente MCP (Model Context Protocol) para o servidor local do Premiere.
|
||||
|
||||
Fala o protocolo JSON-RPC do MCP por stdio com o processo Node do bridge
|
||||
(``code/dist/index.js``). É a única camada do sistema que conhece o
|
||||
transporte (subprocesso, linhas JSON, ids de requisição); o resto do código
|
||||
usa só o método :meth:`ClienteMCP.chamar` com nome de ferramenta e
|
||||
argumentos.
|
||||
"""
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
import json
|
||||
import subprocess
|
||||
from typing import Any
|
||||
|
||||
from .erros_mcp import ErroDeConexaoMCP, ErroDeFerramentaMCP
|
||||
|
||||
|
||||
class ClienteMCP(ABC):
|
||||
"""Define a comunicação técnica com o servidor MCP."""
|
||||
|
||||
@abstractmethod
|
||||
def conectar(self) -> None: ...
|
||||
def conectar(self) -> None:
|
||||
"""Inicia a conexão com o servidor MCP. Não faz nada se já estiver conectado."""
|
||||
|
||||
@abstractmethod
|
||||
def desconectar(self) -> None: ...
|
||||
def desconectar(self) -> None:
|
||||
"""Encerra a conexão com o servidor MCP, se houver uma ativa."""
|
||||
|
||||
@abstractmethod
|
||||
def esta_conectado(self) -> bool: ...
|
||||
def esta_conectado(self) -> bool:
|
||||
"""Indica se há uma conexão ativa com o servidor MCP."""
|
||||
|
||||
@abstractmethod
|
||||
def chamar(self, nome_da_ferramenta: str, argumentos: dict[str, Any] | None = None) -> dict[str, Any]: ...
|
||||
def chamar(self, nome_da_ferramenta: str, argumentos: dict[str, Any] | None = None) -> dict[str, Any]:
|
||||
"""Executa uma ferramenta do servidor MCP e devolve o resultado.
|
||||
|
||||
Levanta :class:`~.erros_mcp.ErroDeConexaoMCP` se não houver conexão
|
||||
ativa e :class:`~.erros_mcp.ErroDeFerramentaMCP` se o servidor
|
||||
responder com erro.
|
||||
"""
|
||||
|
||||
|
||||
class ClienteMCPPorStdio(ClienteMCP):
|
||||
@@ -30,6 +50,11 @@ class ClienteMCPPorStdio(ClienteMCP):
|
||||
self._proximo_id = 1
|
||||
|
||||
def conectar(self) -> None:
|
||||
"""Sobe o processo do servidor MCP e envia o handshake ``initialize``.
|
||||
|
||||
Não faz nada se um processo já estiver em execução — não é possível
|
||||
reconectar sem antes chamar :meth:`desconectar`.
|
||||
"""
|
||||
if self._processo is not None:
|
||||
return
|
||||
self._processo = subprocess.Popen(
|
||||
@@ -43,29 +68,42 @@ class ClienteMCPPorStdio(ClienteMCP):
|
||||
self._enviar("initialize", {"protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": {"name": "engine", "version": "0.1.0"}})
|
||||
|
||||
def desconectar(self) -> None:
|
||||
"""Encerra o processo do servidor MCP, se houver um em execução."""
|
||||
if self._processo is not None:
|
||||
self._processo.terminate()
|
||||
self._processo = None
|
||||
|
||||
def esta_conectado(self) -> bool:
|
||||
"""``True`` enquanto o processo do servidor MCP estiver vivo."""
|
||||
return self._processo is not None and self._processo.poll() is None
|
||||
|
||||
def chamar(self, nome_da_ferramenta: str, argumentos: dict[str, Any] | None = None) -> dict[str, Any]:
|
||||
"""Executa ``nome_da_ferramenta`` no servidor MCP com ``argumentos``.
|
||||
|
||||
Levanta :class:`~.erros_mcp.ErroDeConexaoMCP` se não houver conexão
|
||||
ativa e :class:`~.erros_mcp.ErroDeFerramentaMCP` se o servidor
|
||||
responder com um erro para essa ferramenta.
|
||||
"""
|
||||
if not self.esta_conectado():
|
||||
raise RuntimeError("Cliente MCP não está conectado.")
|
||||
raise ErroDeConexaoMCP("Cliente MCP não está conectado.")
|
||||
resposta = self._enviar("tools/call", {"name": nome_da_ferramenta, "arguments": argumentos or {}})
|
||||
if resposta.get("error"):
|
||||
raise RuntimeError(f"Erro MCP: {resposta['error']}")
|
||||
raise ErroDeFerramentaMCP(f"Erro ao executar {nome_da_ferramenta!r} no MCP: {resposta['error']}")
|
||||
return resposta.get("result", {})
|
||||
|
||||
def _enviar(self, metodo: str, parametros: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Envia uma requisição JSON-RPC pela stdin do processo e lê a resposta da stdout.
|
||||
|
||||
Levanta :class:`~.erros_mcp.ErroDeConexaoMCP` se o processo não
|
||||
estiver disponível ou encerrar sem responder.
|
||||
"""
|
||||
if self._processo is None or self._processo.stdin is None or self._processo.stdout is None:
|
||||
raise RuntimeError("Processo MCP indisponível.")
|
||||
raise ErroDeConexaoMCP("Processo MCP indisponível.")
|
||||
identificador = self._proximo_id
|
||||
self._proximo_id += 1
|
||||
self._processo.stdin.write(json.dumps({"jsonrpc": "2.0", "id": identificador, "method": metodo, "params": parametros}) + "\n")
|
||||
self._processo.stdin.flush()
|
||||
linha = self._processo.stdout.readline()
|
||||
if not linha:
|
||||
raise RuntimeError("O MCP encerrou sem retornar resposta.")
|
||||
raise ErroDeConexaoMCP("O MCP encerrou sem retornar resposta.")
|
||||
return json.loads(linha)
|
||||
|
||||
Reference in New Issue
Block a user