Spaces:
Sleeping
Sleeping
File size: 26,915 Bytes
08141f1 5413358 9edfd4c 52b8580 8907d55 9edfd4c 37f68ba eb7cbda 866e087 eb7cbda 9edfd4c 5413358 52b8580 8907d55 52b8580 95064ac 52b8580 5413358 52b8580 9edfd4c 52b8580 9edfd4c 6977462 37f68ba 8907d55 9edfd4c 6977462 c67c91c 6977462 c67c91c 5413358 6977462 5413358 6977462 52b8580 6977462 52b8580 6977462 52b8580 6977462 eb7cbda 4ec8a50 52b8580 4ec8a50 eb7cbda 4ec8a50 eb7cbda 4ec8a50 eb7cbda 4ec8a50 eb7cbda 866e087 eb7cbda 866e087 eb7cbda 866e087 eb7cbda 866e087 eb7cbda 52b8580 eb7cbda 52b8580 eb7cbda 52b8580 eb7cbda 6977462 37f68ba 8907d55 37f68ba fa68783 37f68ba fa68783 37f68ba fa68783 37f68ba 8907d55 fa68783 8907d55 fa68783 8907d55 fa68783 8907d55 9edfd4c 37f68ba 8907d55 9edfd4c 8907d55 37f68ba fa68783 37f68ba 9edfd4c 37f68ba 9edfd4c 37f68ba 8907d55 37f68ba 8907d55 9edfd4c 37f68ba 9edfd4c 37f68ba | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 | import logging
import re
import streamlit as st
from modules.config import client_anthropic
from modules.utils import sanitizar, extrair_texto_arquivo, aprimorar_prompt
from modules.docgen import criar_docx, _gerar_com_continuacao
from modules.memoria import ler_arquivo_memoria
from modules.video_presentation import gerar_video_apresentacao
from modules.pptx_generator import (
gerar_pptx,
salvar_apresentacao_drive, listar_apresentacoes_drive, carregar_apresentacao_drive,
)
# Detecta URLs do Drive/Docs/Sheets/Slides em texto livre. Captura o ID
# em qualquer um dos 5 formatos comuns. Usado pra permitir que o usuario
# cole links no campo de instrucoes e o agente busque o conteudo automatico.
_RE_DRIVE_URLS = re.compile(
r'https?://(?:'
r'docs\.google\.com/document/d/([a-zA-Z0-9_-]+)'
r'|docs\.google\.com/spreadsheets/d/([a-zA-Z0-9_-]+)'
r'|docs\.google\.com/presentation/d/([a-zA-Z0-9_-]+)'
r'|drive\.google\.com/file/d/([a-zA-Z0-9_-]+)'
r'|drive\.google\.com/open\?id=([a-zA-Z0-9_-]+)'
r')'
)
def _carregar_arquivos_drive_de_urls(texto: str) -> tuple[str, list[str]]:
"""Detecta URLs de Drive/Docs/Sheets/Slides em texto livre, busca o
conteudo de cada arquivo via Drive API, e retorna (conteudo_concatenado,
lista_de_nomes). Limita cada arquivo a 15k chars pra nao estourar contexto.
"""
if not texto:
return "", []
ids_vistos: set[str] = set()
ids: list[str] = []
for match in _RE_DRIVE_URLS.finditer(texto):
for g in match.groups():
if g and g not in ids_vistos:
ids_vistos.add(g)
ids.append(g)
if not ids:
return "", []
from modules.drive_utils import conectar_drive, _ler_conteudo_drive_arquivo
svc = conectar_drive()
if not svc:
return "", []
partes: list[str] = []
nomes: list[str] = []
for file_id in ids:
try:
meta = svc.files().get(fileId=file_id, fields="id,name,mimeType").execute()
nome = meta.get("name", file_id)
mime = meta.get("mimeType", "")
conteudo = _ler_conteudo_drive_arquivo(svc, file_id, mime, nome)
if conteudo and conteudo.strip():
if len(conteudo) > 15000:
conteudo = conteudo[:15000] + "\n[...TRUNCADO...]"
partes.append(f"### Arquivo do Drive: {nome}\n{conteudo}")
nomes.append(nome)
else:
nomes.append(f"{nome} (sem conteudo extraivel)")
except Exception as e:
logging.warning(f"[Docs] Erro ao ler arquivo Drive {file_id}: {e}")
nomes.append(f"{file_id} (erro de acesso)")
return "\n\n".join(partes), nomes
# --------------- Enhancer UI helper ---------------
def _enhancer_ui(key_prefix: str) -> str | None:
"""Show enhancer approval UI. Returns final prompt or None if still waiting."""
estado_key = f"_enh_{key_prefix}_state"
original_key = f"_enh_{key_prefix}_orig"
aprimorado_key = f"_enh_{key_prefix}_enh"
estado = st.session_state.get(estado_key)
if estado != "awaiting":
return None
original = st.session_state.get(original_key, "")
aprimorado = st.session_state.get(aprimorado_key, "")
st.markdown("---")
with st.expander("Seu prompt original", expanded=False):
st.markdown(original)
st.markdown("**Prompt aprimorado:**")
st.info(aprimorado)
col1, col2, col3 = st.columns(3)
with col1:
if st.button("Usar aprimorado", type="primary", key=f"btn_enh_usar_{key_prefix}"):
st.session_state[estado_key] = None
st.session_state[f"_enh_{key_prefix}_run"] = aprimorado
st.rerun()
with col2:
if st.button("Regenerar", key=f"btn_enh_regen_{key_prefix}"):
with st.spinner("Regenerando..."):
st.session_state[aprimorado_key] = aprimorar_prompt(original)
st.rerun()
with col3:
if st.button("Usar original", key=f"btn_enh_orig_{key_prefix}"):
st.session_state[estado_key] = None
st.session_state[f"_enh_{key_prefix}_run"] = original
st.rerun()
return None
def _trigger_enhancer(texto: str, key_prefix: str):
"""Start the enhancer flow: enhance + set awaiting state."""
st.session_state[f"_enh_{key_prefix}_orig"] = texto
with st.spinner("Aprimorando seu prompt..."):
st.session_state[f"_enh_{key_prefix}_enh"] = aprimorar_prompt(texto)
st.session_state[f"_enh_{key_prefix}_state"] = "awaiting"
st.rerun()
def _pop_enhancer_result(key_prefix: str) -> str | None:
"""Pop and return the enhancer result if available."""
return st.session_state.pop(f"_enh_{key_prefix}_run", None)
# --------------- Gamma prompt ---------------
_GAMMA_PROMPT = """Voce e um especialista em criar apresentacoes executivas interativas para a plataforma Gamma.app.
Recebeu o conteudo extraido de uma apresentacao PowerPoint. Sua tarefa e transformar esse conteudo em um ROTEIRO COMPLETO para uma apresentacao Gamma.app animada e narrada.
CONTEUDO DO POWERPOINT:
{conteudo_ppt}
INSTRUCOES ADICIONAIS DO USUARIO:
{instrucoes}
CONTEXTO ADICIONAL:
{contexto}
Gere o roteiro seguindo EXATAMENTE este formato para cada slide/card do Gamma:
---
## Card [N]: [Titulo do Card]
**Visual:** [Descreva o layout ideal: hero image, split layout, grid, quote, etc.]
**Icones/Ilustracoes:** [Sugira icones ou ilustracoes no estilo sketch/hand-drawn que combinam com o tema]
**Texto principal:**
[Texto que aparece no card — curto, impactante, com palavras-chave em **destaque**]
**Narração (texto que o apresentador fala):**
[Texto mais longo e explicativo que complementa o que esta no card. Deve soar natural, como alguem apresentando para a diretoria. Use linguagem executiva mas acessivel.]
**Animacao sugerida:** [fade-in, slide-left, zoom, typewriter, etc.]
**Duracao estimada:** [X segundos]
---
REGRAS:
1. Cada slide do PPT pode virar 1 ou mais cards no Gamma (quebre slides densos em multiplos cards)
2. O primeiro card deve ser uma capa impactante com titulo e subtitulo
3. O ultimo card deve ser um encerramento com call-to-action ou agradecimento
4. Use frases curtas e impactantes nos cards — a narração complementa
5. Sugira destaques visuais (palavras com background colorido, como no exemplo)
6. Para tabelas/dados, sugira visualizacoes (graficos, progress bars, KPIs side-by-side)
7. Mantenha o tom profissional mas envolvente
8. Toda narração em portugues brasileiro
9. Inclua transicoes suaves entre cards
10. Para dados numericos, destaque os numeros grandes e as variacoes (%, +/-)
Ao final, inclua uma secao:
## Resumo para Gamma.app
- Total de cards: [N]
- Duracao estimada: [X minutos]
- Estilo visual sugerido: [ex: Sketch/Hand-drawn, Minimalista, Corporativo]
- Paleta de cores sugerida: [baseada no conteudo]
- Instrucoes para importar no Gamma: [passo a passo rapido]
Gere o roteiro COMPLETO agora."""
# --------------- PPTX generation logic ---------------
def _executar_geracao_pptx(instrucao_pptx: str, auto_ctx: bool):
"""Executa a geracao do PPTX (separado da UI para reuso)."""
contexto_auto = ""
if auto_ctx:
with st.spinner("Carregando contexto completo (OKRs, roadmap, metricas, iniciativas, PM OS)..."):
partes = []
try:
from modules.contexto import carregar_contexto_sob_demanda
ctx_base, _ctx_log = carregar_contexto_sob_demanda(
"okrs roadmap metricas iniciativas squad cora estrategia"
)
if ctx_base:
partes.append(f"## Contexto Base (Roadmap, Squad, Empresa)\n{ctx_base}")
except Exception as e:
logging.warning(f"[Docs] Erro ao carregar contexto base: {e}")
try:
from modules.pm_os import pm_os_carregar_contexto_outputs
ctx_pm_os = pm_os_carregar_contexto_outputs()
if ctx_pm_os:
partes.append(f"## Contexto PM OS (OKRs, PRDs, Outputs)\n{ctx_pm_os}")
except Exception as e:
logging.warning(f"[Docs] Erro ao carregar PM OS: {e}")
try:
from modules.pm_os import PM_OS_DRIVE, _pm_os_listar_com_svc
from modules.drive_utils import conectar_drive, _ler_conteudo_drive_arquivo
svc = conectar_drive()
if svc:
prds = _pm_os_listar_com_svc(svc, PM_OS_DRIVE.get("outputs_prds", ""))
prd_parts = []
for arq in prds:
if arq.get("mimeType") == "application/vnd.google-apps.folder":
continue
conteudo = _ler_conteudo_drive_arquivo(svc, arq["id"], arq["mimeType"], arq["name"])
if conteudo.strip():
prd_parts.append(f"### PRD: {arq['name']}\n{conteudo[:5000]}")
if prd_parts:
partes.append(f"## PRDs das Iniciativas\n" + "\n\n".join(prd_parts))
except Exception as e:
logging.warning(f"[Docs] Erro ao carregar PRDs: {e}")
try:
from modules.contexto_fraude import carregar_inteligencia_fraude
ctx_fraude = carregar_inteligencia_fraude()
if ctx_fraude:
partes.append(f"## Inteligencia de Fraude\n{ctx_fraude[:15000]}")
except Exception as e:
logging.warning(f"[Docs] Erro ao carregar contexto fraude: {e}")
contexto_auto = "\n\n---\n\n".join(partes)
# Contexto de documentos selecionados
mem_docs = st.session_state.get("memoria_arquivos") or []
docs_selecionados = st.session_state.get("pptx_docs_ctx", [])
for doc in mem_docs:
if doc["name"] in docs_selecionados:
contexto_auto += f"\n--- {doc['name']} ---\n{ler_arquivo_memoria(doc['id'])}\n"
# Carrega arquivos linkados no campo de instrucoes (Docs, Sheets, Slides,
# Drive files). Permite o usuario colar URL e o agente puxa o conteudo.
with st.spinner("Verificando links do Drive na instrucao..."):
drive_conteudo, drive_nomes = _carregar_arquivos_drive_de_urls(instrucao_pptx)
if drive_conteudo:
contexto_auto += "\n\n---\n\n## Arquivos do Drive linkados na instrucao\n\n" + drive_conteudo
if drive_nomes:
st.info("Arquivos do Drive carregados: " + ", ".join(drive_nomes))
progress_bar = st.progress(0)
status_text = st.empty()
def update_progress(step, total, msg):
progress_bar.progress(step / total)
status_text.info(f"**Passo {step}/{total}:** {msg}")
try:
pptx_bytes, n_slides, slides_html = gerar_pptx(
tema=instrucao_pptx,
contexto=contexto_auto,
progress_callback=update_progress,
)
progress_bar.empty()
status_text.empty()
st.session_state["_pptx_slides"] = slides_html
st.session_state["_pptx_bytes"] = pptx_bytes
nome_apres = instrucao_pptx.strip()[:50] or "apresentacao"
fid = salvar_apresentacao_drive(slides_html, nome_apres)
if fid:
st.session_state["_pptx_drive_id"] = fid
st.success(f"Apresentacao gerada com sucesso! {n_slides} slides."
+ (" Salva no Drive." if fid else ""))
st.download_button(
f"Baixar PPTX ({n_slides} slides)",
data=pptx_bytes,
file_name="apresentacao_cora.pptx",
mime="application/vnd.openxmlformats-officedocument.presentationml.presentation",
key="dl_pptx",
)
except Exception as e:
progress_bar.empty()
status_text.empty()
st.error(f"Erro ao gerar apresentacao: {e}")
def _executar_edicao_slides(edit_instrucao: str, saved_slides: list):
"""Executa a edicao dos slides."""
from modules.pptx_generator import editar_slides_lote
progress_bar = st.progress(0)
status_text = st.empty()
def update_edit_progress(step, total, msg):
progress_bar.progress(step / total)
status_text.info(f"**Passo {step}/{total}:** {msg}")
try:
pptx_bytes, n_slides, slides_atualizados = editar_slides_lote(
slides=saved_slides,
instrucao=edit_instrucao,
progress_callback=update_edit_progress,
)
progress_bar.empty()
status_text.empty()
st.session_state["_pptx_slides"] = slides_atualizados
st.session_state["_pptx_bytes"] = pptx_bytes
salvar_apresentacao_drive(slides_atualizados,
st.session_state.get("pptx_instrucao", "apresentacao")[:50])
st.success(f"Edicao aplicada com sucesso! {n_slides} slides.")
st.download_button(
f"Baixar PPTX editado ({n_slides} slides)",
data=pptx_bytes,
file_name="apresentacao_cora.pptx",
mime="application/vnd.openxmlformats-officedocument.presentationml.presentation",
key="dl_pptx_edited",
)
except Exception as e:
progress_bar.empty()
status_text.empty()
st.error(f"Erro ao editar slides: {e}")
# --------------- Main render ---------------
def render_docs_tab():
"""Renderiza a aba Documentos."""
st.header("Gerador de Documentos")
enhancer_on = st.session_state.get("prompt_enhancer_ativo", False)
tipo_doc = st.selectbox("Tipo de documento:", [
"Apresentacao Executiva (PPTX)",
"Video Executivo (MP4)",
"Apresentacao Interativa (Gamma.app)",
"Roteiro de Apresentacao",
"Analise de Risco",
"Email Executivo",
"Documento Livre",
])
# --- Apresentacao Executiva PPTX ---
if tipo_doc == "Apresentacao Executiva (PPTX)":
st.markdown("""
Gera uma **apresentacao PowerPoint com visual rico** — igual ao video executivo.
**Como funciona:**
1. Descreva o tema (ex: "Racional de priorizacao das iniciativas por KR")
2. **Cole links do Drive/Docs/Sheets/Slides** direto no campo abaixo — o agente extrai o conteudo automaticamente
3. O agente carrega contexto automaticamente (OKRs, metricas, iniciativas) se a opcao estiver marcada
4. Claude Opus cria slides HTML com design visual rico (gradientes, cards, icones)
5. Playwright renderiza cada slide em imagem Full HD (1920x1080)
6. Monta o PPTX com imagens — visual identico ao video executivo
""")
auto_ctx = st.checkbox("Carregar contexto automaticamente (OKRs, roadmap, metricas)", value=True, key="pptx_auto_ctx")
instrucao_pptx = st.text_area("Tema e instrucoes da apresentacao:", height=150,
placeholder=(
"Ex: Racional de atingimento dos OKRs — como as iniciativas do roadmap impactam cada KR. "
"Tom executivo para o CPO.\n\n"
"Voce pode colar links do Drive aqui (Docs, Sheets, Slides, PDFs, DOCX...) que o agente "
"le o conteudo automaticamente. Ex: https://docs.google.com/document/d/ABC123/edit"
),
key="pptx_instrucao")
# Check if enhancer returned a result
enh_result = _pop_enhancer_result("pptx_gen")
if enh_result:
_executar_geracao_pptx(enh_result, auto_ctx)
# Show enhancer UI if awaiting
elif st.session_state.get("_enh_pptx_gen_state") == "awaiting":
_enhancer_ui("pptx_gen")
# Button
elif st.button("Gerar Apresentacao PPTX", type="primary", key="btn_pptx"):
if not instrucao_pptx.strip():
st.warning("Descreva o tema da apresentacao.")
elif enhancer_on:
_trigger_enhancer(instrucao_pptx, "pptx_gen")
else:
_executar_geracao_pptx(instrucao_pptx, auto_ctx)
# --- Carregar apresentacao salva do Drive ---
st.divider()
with st.expander("Carregar apresentacao salva"):
if st.button("Listar apresentacoes", key="btn_list_pptx"):
with st.spinner("Buscando no Drive..."):
st.session_state["_pptx_lista_drive"] = listar_apresentacoes_drive()
lista_drive = st.session_state.get("_pptx_lista_drive", [])
if lista_drive:
opcoes_drive = {}
opcoes_meta = {}
for a in lista_drive:
name = a["name"]
display = name.replace("pptx_", "").replace(".json", "")
is_json = name.endswith(".json")
tipo = "editavel" if is_json else "importavel"
label = f"{display} ({a.get('modifiedTime', '')[:10]}) [{tipo}]"
opcoes_drive[label] = a["id"]
opcoes_meta[label] = name
selecionada = st.selectbox("Apresentacao:", list(opcoes_drive.keys()), key="pptx_load_select")
is_pptx_import = not opcoes_meta.get(selecionada, "").endswith(".json")
if is_pptx_import:
st.caption("Este arquivo sera importado: Claude vai recriar os slides como HTML editavel. Pode levar ~1 min por slide.")
if st.button("Carregar", key="btn_load_pptx"):
spinner_msg = "Importando PPTX (Claude recriando slides)..." if is_pptx_import else "Carregando apresentacao..."
with st.spinner(spinner_msg):
slides_carregados = carregar_apresentacao_drive(
opcoes_drive[selecionada],
file_name=opcoes_meta.get(selecionada, ""),
)
if slides_carregados:
st.session_state["_pptx_slides"] = slides_carregados
if is_pptx_import:
nome_save = opcoes_meta.get(selecionada, "import").rsplit(".", 1)[0]
fid = salvar_apresentacao_drive(slides_carregados, nome_save)
if fid:
st.success(f"Importado e salvo! {len(slides_carregados)} slides. Edite abaixo.")
else:
st.success(f"Importado: {len(slides_carregados)} slides. Edite abaixo.")
else:
st.success(f"Apresentacao carregada: {len(slides_carregados)} slides. Edite abaixo.")
st.rerun()
else:
st.error("Erro ao carregar apresentacao.")
elif st.session_state.get("_pptx_lista_drive") is not None:
st.caption("Nenhuma apresentacao salva encontrada.")
# --- Edicao de slides ---
saved_slides = st.session_state.get("_pptx_slides")
if saved_slides:
st.divider()
st.subheader("Editar slides")
with st.expander(f"Ver slides atuais ({len(saved_slides)})"):
for i, s in enumerate(saved_slides):
st.caption(f"**Slide {i+1}:** {s.get('titulo', 'Sem titulo')}")
st.caption("Descreva as alteracoes referenciando os slides por numero. "
"Slides nao mencionados serao mantidos identicos.")
edit_instrucao = st.text_area("Alteracoes:", height=150,
placeholder="Ex:\nSlide 1: Trocar titulo para 'Novo Titulo'\n"
"Slide 3: Remover a parte sobre migração\n"
"Slide 5: Adicionar bullet sobre modelo de ML",
key="pptx_edit_instrucao")
# Check if enhancer returned a result for edit
enh_edit_result = _pop_enhancer_result("pptx_edit")
if enh_edit_result:
_executar_edicao_slides(enh_edit_result, saved_slides)
elif st.session_state.get("_enh_pptx_edit_state") == "awaiting":
_enhancer_ui("pptx_edit")
elif st.button("Aplicar edicoes", type="primary", key="btn_pptx_edit"):
if not edit_instrucao.strip():
st.warning("Descreva o que deseja alterar.")
elif enhancer_on:
_trigger_enhancer(edit_instrucao, "pptx_edit")
else:
_executar_edicao_slides(edit_instrucao, saved_slides)
# Documentos adicionais de contexto
mem_docs = st.session_state.get("memoria_arquivos") or []
if mem_docs:
st.multiselect("Documentos adicionais de contexto (opcional):",
[d["name"] for d in mem_docs], key="pptx_docs_ctx")
return # Nao renderizar o resto da aba
# --- Upload de PPT (para Video ou Gamma) ---
ppt_conteudo = ""
ppt_file_ref = None
needs_ppt = tipo_doc in ("Video Executivo (MP4)", "Apresentacao Interativa (Gamma.app)")
if tipo_doc == "Video Executivo (MP4)":
st.markdown("""
Transforme sua apresentacao (PowerPoint ou PDF) em um **video profissional narrado** —
com slides redesenhados na identidade visual Cora, narracao feminina em portugues,
animacoes e transicoes suaves.
**Como funciona:**
1. Faca upload do seu arquivo abaixo (PPTX ou PDF)
2. Adicione instrucoes especificas (opcional)
3. O agente redesenha os slides, gera narracao e monta o video MP4
""")
ppt_file = st.file_uploader("Upload da apresentação (.pptx ou .pdf):", type=["pptx", "pdf"], key="ppt_video")
if ppt_file:
ppt_file_ref = ppt_file
with st.spinner("Extraindo conteudo do PowerPoint..."):
ppt_conteudo = extrair_texto_arquivo(ppt_file)
if ppt_conteudo:
n_slides = ppt_conteudo.count("--- SLIDE")
st.success(f"PowerPoint carregado: {n_slides} slides extraidos")
with st.expander("Ver conteudo extraido"):
st.text(ppt_conteudo[:3000] + ("..." if len(ppt_conteudo) > 3000 else ""))
else:
st.warning("Nao foi possivel extrair conteudo do PPT.")
elif tipo_doc == "Apresentacao Interativa (Gamma.app)":
st.markdown("""
Transforme sua apresentacao (PowerPoint ou PDF) em um **roteiro completo para o Gamma.app** —
com narração, animações, e sugestões visuais para criar uma apresentação interativa e animada.
**Como funciona:**
1. Faca upload do seu arquivo abaixo (PPTX ou PDF)
2. O agente gera o roteiro completo com narração para cada card
3. Copie o conteudo no [Gamma.app](https://gamma.app) e ele cria a apresentação animada
""")
ppt_file = st.file_uploader("Upload da apresentação (.pptx ou .pdf):", type=["pptx", "pdf"], key="ppt_gamma")
if ppt_file:
with st.spinner("Extraindo conteudo do PowerPoint..."):
ppt_conteudo = extrair_texto_arquivo(ppt_file)
if ppt_conteudo:
n_slides = ppt_conteudo.count("--- SLIDE")
st.success(f"PowerPoint carregado: {n_slides} slides extraidos")
with st.expander("Ver conteudo extraido"):
st.text(ppt_conteudo[:3000] + ("..." if len(ppt_conteudo) > 3000 else ""))
else:
st.warning("Nao foi possivel extrair conteudo do PPT.")
mem_docs_tab = st.session_state.memoria_arquivos or []
docs_ctx = st.multiselect("Documentos de contexto (memoria):", [d["name"] for d in mem_docs_tab], key="docs_ctx")
instrucao = st.text_area("Descreva o que precisa:", height=150,
placeholder="Ex: Foco nos resultados, tom executivo para diretoria, destacar KPIs..."
if needs_ppt
else "Ex: Roteiro de apresentacao sobre os KPIs do trimestre...")
if st.button("Gerar Documento", type="primary"):
if needs_ppt and not ppt_conteudo:
st.warning("Faca upload do arquivo (PPTX ou PDF) primeiro.")
elif not instrucao.strip() and not needs_ppt:
st.warning("Descreva o documento que precisa.")
else:
contexto_docs = ""
for doc in mem_docs_tab:
if doc["name"] in docs_ctx:
contexto_docs += f"\n--- {doc['name']} ---\n{ler_arquivo_memoria(doc['id'])}\n"
if tipo_doc == "Video Executivo (MP4)":
placeholder = st.empty()
ppt_file_ref.seek(0)
video_bytes = gerar_video_apresentacao(
ppt_file_ref, instrucao, contexto_docs, placeholder
)
if video_bytes:
video_bytes.seek(0)
st.video(video_bytes, format="video/mp4")
video_bytes.seek(0)
st.download_button(
"Baixar Video (MP4)",
data=video_bytes,
file_name="apresentacao_executiva.mp4",
mime="video/mp4",
key="dl_video",
)
elif tipo_doc == "Apresentacao Interativa (Gamma.app)":
with st.spinner("Gerando apresentacao interativa..."):
prompt_doc = _GAMMA_PROMPT.format(
conteudo_ppt=ppt_conteudo,
instrucoes=instrucao or "Nenhuma instrucao adicional.",
contexto=contexto_docs or "Nenhum contexto adicional.",
)
placeholder = st.empty()
resposta = _gerar_com_continuacao(client_anthropic, st.session_state.modelo, prompt_doc, placeholder)
st.download_button(
"Baixar em Word (.docx)", data=criar_docx(resposta),
file_name=f"{sanitizar(tipo_doc)}.docx",
mime="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
key="dl_doc"
)
else:
with st.spinner("Gerando..."):
prompt_doc = f"""Voce e um PM Senior. Gere um {tipo_doc} completo e profissional em portugues.
SOLICITACAO: {instrucao}
CONTEXTO ADICIONAL:
{contexto_docs or "Nenhum contexto adicional fornecido."}
Use formatacao Markdown com titulos, subtitulos e listas bem estruturadas."""
placeholder = st.empty()
resposta = _gerar_com_continuacao(client_anthropic, st.session_state.modelo, prompt_doc, placeholder)
st.download_button(
"Baixar em Word (.docx)", data=criar_docx(resposta),
file_name=f"{sanitizar(tipo_doc)}.docx",
mime="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
key="dl_doc_other"
)
|