--- name: find_consent_controls_llm kind: function lang: py domain: browser version: "1.0.0" purity: impure signature: "def find_consent_controls_llm(*, port: int = 9222, max_candidates: int = 40, model: str = 'claude-haiku-4-5-20251001') -> dict" description: "Identifica los botones de un banner de cookies/consentimiento usando un LLM en vez de selectores hardcodeados por CMP. Recolecta los controles clicables visibles de la pagina via CDP, los marca con un atributo estable data-fnllm='N' en el DOM, y pregunta a haiku (ask_llm) cual es ACEPTAR TODO, cual RECHAZAR y cual el enlace VER SOCIOS/configurar/mas opciones/finalidades. Resuelve los CMP cuyos botones no encajan con selectores fijos (casos no-button del scanner de databrokers)." tags: [consent, llm, cdp, browser, navegator, claude-direct, cookies, cmp, tcf, python, automation] uses_functions: [cdp_eval_py_browser, ask_llm_py_core] uses_types: [] returns: [] returns_optional: false error_type: "error_go_core" imports: ["json", "os", "re", "sys"] params_schema: params: - name: port desc: "Puerto de remote debugging del Chrome donde esta el banner. Default 9222." - name: max_candidates desc: "Maximo de controles clicables a recolectar y enviar al LLM. Default 40." - name: model desc: "Modelo Anthropic a usar via ask_llm para clasificar los controles. Default claude-haiku-4-5-20251001." output: "dict {status: 'ok'|'error', candidates: [{idx, tag, text, aria, id, cls}], accept_idx/reject_idx/vendors_idx: int|None, accept_selector/reject_selector/vendors_selector: '[data-fnllm=\"N\"]'|None, reason: str, error?: str}. Los selectores se construyen a partir del idx elegido por el LLM y sirven para clicar el control con cdp_eval. Nunca lanza: errores de CDP/eval/LLM se devuelven en el dict." tested: false tests: [] test_file_path: "" file_path: "python/functions/browser/find_consent_controls_llm.py" --- ## Ejemplo ```python import sys, os, time sys.path.insert(0, os.path.join("python", "functions")) from browser.cdp_eval import cdp_eval from browser.find_consent_controls_llm import find_consent_controls_llm # Requiere un Chrome con --remote-debugging-port=9335 y una pestana abierta. # Navega primero al sitio con banner y espera a que cargue el CMP. cdp_eval("location.href='https://www.elpuntavui.cat'", port=9335) time.sleep(6) res = find_consent_controls_llm(port=9335) print(res["accept_idx"], res["accept_selector"], res["reason"]) # Clicar el boton de aceptar elegido por el LLM: sel = res["accept_selector"] if sel: cdp_eval(f"document.querySelector('{sel}').click()", port=9335) ``` O directo por CLI: `python3 python/functions/browser/find_consent_controls_llm.py 9335`. ## Cuando usarla Cuando un banner de cookies/consentimiento NO se resuelve con selectores fijos por CMP (los casos "no-button" del scanner de databrokers): textos en otro idioma, marcas TCF poco comunes, botones renderizados con clases dinamicas. La funcion deja que un LLM lea los controles visibles y decida cual es aceptar/rechazar/ver-socios, devolviendo selectores `[data-fnllm="N"]` estables que persisten en el DOM para que el caller clique con `cdp_eval`. Usala como fallback despues de que los selectores hardcodeados fallen, no como primer intento (cuesta una llamada al LLM). ## Gotchas - **El banner debe estar YA en el DOM**: navega al sitio y espera unos segundos (`time.sleep(~6)`) ANTES de llamar. Si el CMP aun no se ha renderizado, la lista de candidatos no lo incluira y el LLM no podra elegir. - **El LLM puede equivocarse**: haiku es rapido pero falible. Verifica el `text` del candidato en `accept_idx` antes de clicar acciones irreversibles. Sube de modelo (`model="claude-opus-4-8"`) si la precision importa mas que el coste. - **Rate limits de ask_llm**: cada llamada consume cuota de la API directa de Anthropic. No la invoques en bucle cerrado sobre muchas pestanas sin throttling. - **Marca el DOM**: pone `data-fnllm="N"` en hasta `max_candidates` elementos. Si re-llamas tras cambiar la pagina, los atributos viejos pueden quedar; los selectores solo son fiables sobre el mismo render donde se recolectaron. - **Requiere remote debugging**: sin un Chrome con `--remote-debugging-port`, `cdp_eval` falla y devuelve `{status: "error", error: "cdp_eval: ..."}`. - Solo recolecta controles **visibles** (`getClientRects().length>0`) y con texto corto (<=60 chars). Controles dentro de shadow DOM o iframes cross-origin no se ven.