CREATE OR REPLACE FUNCTION blob_to_clob(p_blob IN BLOB) RETURN CLOB IS l_clob CLOB; l_varchar VARCHAR2(32767); l_start PLS_INTEGER := 1; l_buffer PLS_INTEGER := 32767; BEGIN DBMS_LOB.CREATETEMPORARY(l_clob, TRUE); FOR i IN 1 .. CEIL(DBMS_LOB.GETLENGTH(p_blob) / l_buffer) LOOP l_varchar := UTL_RAW.CAST_TO_VARCHAR2( DBMS_LOB.SUBSTR(p_blob, l_buffer, l_start) ); DBMS_LOB.WRITEAPPEND(l_clob, LENGTH(l_varchar), l_varchar); l_start := l_start + l_buffer; END LOOP; RETURN l_clob; END blob_to_clob; / set serveroutput on set define off CREATE OR REPLACE PROCEDURE CONVERT_MDFILES_TO_JSON AS l_md_content CLOB; l_prompt CLOB; l_final_json CLOB; -- CHR(10) agrega saltos de linea al prompt enviado al modelo. BEGIN SELECT md_clob INTO l_md_content FROM markdown_files WHERE STATUS = 'INGESTED' FOR UPDATE; -- ===================================================================== -- Llamada unica: genera todo el JSON estructurado desde el markdown. -- ===================================================================== l_prompt := 'Abaixo está um documento em markdown de uma promoção do ueno bank (Paraguai), em espanhol.' || CHR(10) || 'Extraia todos os dados relevantes da promoção em formato JSON estruturado.' || CHR(10) || CHR(10) || 'Retorne APENAS o objeto JSON abaixo (sem markdown fences, sem texto antes ou depois):' || CHR(10) || '{' || CHR(10) || ' "promocao": {' || CHR(10) || ' "titulo": "...",' || CHR(10) || ' "vigencia": {"data_inicio": "YYYY-MM-DD", "data_fim": "YYYY-MM-DD"}' || CHR(10) || ' },' || CHR(10) || ' "medios_pago": {' || CHR(10) || ' "tarjetas_aceitas": ["lista completa"],' || CHR(10) || ' "emissor": "...",' || CHR(10) || ' "rede": "...",' || CHR(10) || ' "billeteras_electronicas": ["lista"],' || CHR(10) || ' "qr_permitido": true,' || CHR(10) || ' "ecommerce_permitido": true' || CHR(10) || ' },' || CHR(10) || ' "beneficios_por_nivel": [' || CHR(10) || ' {"nivel": "nivel N", "percentual_reintegro": 0, "monto_compra_minimo_gs": 0, "reintegro_maximo_gs": 0}' || CHR(10) || ' ],' || CHR(10) || ' "cuotas": {' || CHR(10) || ' "maximo_sin_intereses": 0,' || CHR(10) || ' "requer_solicitacao_cliente": true' || CHR(10) || ' },' || CHR(10) || ' "acreditacao_reintegro": {' || CHR(10) || ' "prazo_dias_habiles": 0,' || CHR(10) || ' "percentual_creditado": 0,' || CHR(10) || ' "tipo_conta": "..."' || CHR(10) || ' },' || CHR(10) || ' "exclusoes": ["frases curtas, 1 por item"],' || CHR(10) || ' "comercios": [' || CHR(10) || ' {"name": "...", "category": "...", "payment_processor": "upay"}' || CHR(10) || ' ]' || CHR(10) || '}' || CHR(10) || CHR(10) || 'REGRAS:' || CHR(10) || '- Datas no formato ISO YYYY-MM-DD.' || CHR(10) || '- Campos booleanos devem usar true ou false sem aspas.' || CHR(10) || '- Campos numéricos devem usar números, sem aspas.' || CHR(10) || '- Valores monetários em guaranies como integers (remover "Gs.", pontos e espaços).' || CHR(10) || '- Se um campo não aparecer no documento, use null.' || CHR(10) || '- exclusoes: liste apenas os principais pontos de exclusão em frases curtas.' || CHR(10) || '- comercios: extraia TODOS os comércios que aparecem literalmente no documento.' || CHR(10) || '- comercios.category: seção em negrito mais próxima (**Belleza...**, **Grupo Zavidoro...**, etc).' || CHR(10) || '- comercios.payment_processor: "upay" ou "bancard" conforme aparece ao lado do nome.' || CHR(10) || '- Preserve os nomes dos comércios exatamente como aparecem.' || CHR(10) || '- Não invente comércios nem complete nomes ausentes.' || CHR(10) || CHR(10) || 'DOCUMENTO:' || CHR(10) || l_md_content; l_final_json := DBMS_CLOUD_AI.GENERATE( prompt => l_prompt, profile_name => 'PROFILE_GPT-5.5', action => 'chat'); IF l_final_json IS NOT JSON THEN RAISE_APPLICATION_ERROR(-20210, 'Modelo retornou JSON inválido. Últimos 200 chars: ' || SUBSTR(l_final_json, -200)); END IF; -- Graba el JSON completo generado por el modelo. INSERT INTO parsed_markdown_files (data) VALUES (l_final_json); UPDATE markdown_files SET STATUS = 'LOADED'; COMMIT; DBMS_OUTPUT.PUT_LINE('JSON final: ' || LENGTH(l_final_json) || ' chars'); END; / set serveroutput on -- drop tables DECLARE l_count PLS_INTEGER := 0; BEGIN -- Recorre solo tablas del esquema actual con el prefijo indicado. FOR t IN ( SELECT table_name FROM user_tables WHERE table_name LIKE 'T\_%' ESCAPE '\' ORDER BY table_name ) LOOP -- Usa comillas dobles para soportar nombres creados con case-sensitive. EXECUTE IMMEDIATE 'DROP TABLE "' || REPLACE(t.table_name, '"', '""') || '" CASCADE CONSTRAINTS PURGE'; l_count := l_count + 1; DBMS_OUTPUT.PUT_LINE('Tabla eliminada: ' || t.table_name); END LOOP; DBMS_OUTPUT.PUT_LINE('Total de tablas eliminadas: ' || l_count); END; /