File size: 5,190 Bytes
2d161de
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# -*- coding: utf-8 -*-
"""app.ipynb

Automatically generated by Colab.

Original file is located at
    https://colab.research.google.com/drive/1e3y3OTjQnNFZ7FLgs2elviYcG7ptj2fI
"""

from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
from sentence_transformers import SentenceTransformer, util
import numpy as np
import joblib
import torch
import gradio as gr
from transformers import TextGenerationPipeline

# === Cargar modelos entrenados ===
modelo_riesgo = joblib.load("modelo_riesgo.pkl")
modelo_violencia = joblib.load("modelo_tipo_violencia.pkl")
modelo_medida = joblib.load("modelo_tipo_medida.pkl")
codificadores = joblib.load("codificadores_label_encoder.pkl")
modelo_vector = SentenceTransformer("Snowflake/snowflake-arctic-embed-xs")

# === Cargar modelo de lenguaje OpenHermes-2.5 ===
model_id = "teknium/OpenHermes-2.5-Mistral-7B"

tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
    model_id,
    trust_remote_code=True,
    torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
    device_map="auto"
)

modelo_llm = TextGenerationPipeline(
    model=model,
    tokenizer=tokenizer,
    max_new_tokens=300,
    temperature=0.5,
    do_sample=True,
)

# === Frases prototipo para verificador semántico ===
frases_fisica = [
    "Me golpeó con el puño cerrado", "Me pateó", "Me lanzó contra la pared",
    "Me estranguló", "Me fracturó una costilla", "Me tiró al piso violentamente"
]
frases_sexual = [
    "Me obligó a tener relaciones sexuales", "Me tocó sin consentimiento",
    "Me violó", "Me forzó a tener sexo", "Me agredió sexualmente"
]
embeds_fisica = modelo_vector.encode(frases_fisica)
embeds_sexual = modelo_vector.encode(frases_sexual)

# === FUNCIÓN PRINCIPAL ===
def predecir_con_recomendacion(edad, genero, hijos, convivencia_agresor, consumo_sustancias, apoyo_familiar, descripcion):
    # Codificar variables
    vector_tabular = np.array([
        int(edad),
        int(hijos),
        codificadores["genero"].transform([genero])[0],
        0, 0, 0,
        codificadores["convivencia_agresor"].transform([convivencia_agresor])[0],
        codificadores["consumo_sustancias"].transform([consumo_sustancias])[0],
        codificadores["apoyo_familiar"].transform([apoyo_familiar])[0]
    ])

    # Vectorizar descripción
    vector_desc = modelo_vector.encode([descripcion])[0]
    entrada = np.concatenate([vector_tabular, vector_desc])

    # Predicciones
    riesgo_cod = modelo_riesgo.predict([entrada])[0]
    tipo_violencia_cod = modelo_violencia.predict([entrada])[0]
    tipo_medida_cod = modelo_medida.predict([entrada])[0]

    # Decodificar
    riesgo = codificadores["riesgo"].inverse_transform([riesgo_cod])[0]
    tipo_violencia = codificadores["tipo_violencia"].inverse_transform([tipo_violencia_cod])[0]
    tipo_medida = codificadores["tipo_medida"].inverse_transform([tipo_medida_cod])[0]

    # Verificador semántico
    emb_desc = modelo_vector.encode(descripcion)
    sim_fisica = max(util.cos_sim(emb_desc, embeds_fisica)[0])
    sim_sexual = max(util.cos_sim(emb_desc, embeds_sexual)[0])
    if sim_fisica > 0.7:
        tipo_violencia = "física"
    elif sim_sexual > 0.7:
        tipo_violencia = "sexual"

    # PROMPT legal
    prompt = f"""[INSTRUCCIÓN]
Como asistente legal, tu tarea es determinar qué medida de protección debe aplicarse en un caso de violencia intrafamiliar, de acuerdo al Artículo 5 de la Ley 575 de 2000 (modificado por la Ley 1257 de 2008 y la Ley 2126 de 2021).

[CASO]
Tipo de violencia: {tipo_violencia}
Nivel de riesgo: {riesgo}
Tipo de medida cautelar: {tipo_medida}
Descripción del caso: {descripcion}

[OBJETIVO]
Con base en la descripción del caso y la ley, indica:
- Qué literal(es) del Artículo 5 (a-n) deben aplicarse.
- Justifica legalmente tu elección con argumentos claros y precisos.

[RESPUESTA]
"""

    respuesta = modelo_llm(prompt, max_new_tokens=350, temperature=0.5)[0]["generated_text"]
    razonamiento = respuesta.split("[RESPUESTA]")[-1].strip()

    return tipo_violencia, riesgo, tipo_medida, razonamiento

# === Gradio UI ===
gr.Interface(
    fn=predecir_con_recomendacion,
    inputs=[
        gr.Slider(18, 65, value=30, label="Edad"),
        gr.Radio(["F", "M"], label="Género"),
        gr.Slider(0, 5, step=1, value=1, label="Número de hijos"),
        gr.Radio(["sí", "no"], label="¿Convive con el agresor?"),
        gr.Radio(["sí", "no"], label="¿Hay consumo de sustancias?"),
        gr.Radio(["sí", "no"], label="¿Tiene apoyo familiar?"),
        gr.Textbox(lines=4, label="Descripción del caso")
    ],
    outputs=[
        gr.Textbox(label="🛑 Tipo de violencia predicho"),
        gr.Textbox(label="⚠️ Nivel de riesgo"),
        gr.Textbox(label="🧾 Tipo de medida cautelar"),
        gr.Textbox(label="📜 Recomendación legal razonada")
    ],
    title="LEGALFAMI – Asistente Legal con Razonamiento Jurídico",
    description="Predice tipo de violencia, nivel de riesgo y medida cautelar según Ley 575 Artículo 5, y recomienda la acción legal correspondiente.",
    theme="default"
).launch()