File size: 3,972 Bytes
1f74418
a9a8ff2
b817834
5ad7eb7
961c645
b123e5c
961c645
f74b98c
62cf6c0
e6b58e1
 
 
f74b98c
5ad7eb7
961c645
862915a
961c645
62cf6c0
 
 
 
 
d39067f
62cf6c0
d39067f
62cf6c0
 
 
 
 
d380ffa
62cf6c0
d380ffa
961c645
 
 
f74b98c
 
ee79e54
62cf6c0
 
5ad7eb7
62cf6c0
 
d380ffa
b123e5c
 
 
 
a8608f2
961c645
b123e5c
961c645
b123e5c
 
 
 
 
 
 
 
 
961c645
 
b123e5c
961c645
b123e5c
 
 
 
 
 
 
 
 
 
 
 
 
a8608f2
62cf6c0
862915a
 
62cf6c0
 
b123e5c
 
 
 
 
62cf6c0
ee79e54
862915a
 
 
e6b58e1
f74b98c
b123e5c
862915a
 
 
 
f74b98c
ee79e54
b123e5c
1f74418
961c645
862915a
961c645
b123e5c
 
 
 
862915a
b123e5c
f74b98c
62cf6c0
862915a
 
 
 
 
 
 
f74b98c
b123e5c
f74b98c
b123e5c
 
f74b98c
62cf6c0
d380ffa
f74b98c
62cf6c0
f74b98c
 
d380ffa
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
import gradio as gr
import pandas as pd
from transformers import pipeline

# ------------------------------------------------
# Load Qwen 3B (instruction-following, concise)
# ------------------------------------------------
generator = pipeline(
    "text-generation",
    model="Qwen/Qwen2.5-3B-Instruct",
    device_map="auto",
    trust_remote_code=True
)

# ------------------------------------------------
# Generic fact quantization (NO domain logic)
# ------------------------------------------------
def magnitude_bucket(x):
    if x < 0.05:
        return "low"
    elif x < 0.2:
        return "medium"
    else:
        return "high"

def direction_bucket(diff):
    if diff > 0:
        return "increase"
    elif diff < 0:
        return "decrease"
    else:
        return "no_change"

# ------------------------------------------------
# Core logic
# ------------------------------------------------
def analyze_kpi(csv_file, top_n):
    df = pd.read_csv(csv_file.name)

    dates = df.columns[1:]
    prev_date, curr_date = dates[-2], dates[-1]

    df["diff"] = df[curr_date] - df[prev_date]
    df["abs_diff"] = df["diff"].abs()

    ranked = df.sort_values("abs_diff", ascending=False)

    top_kpis = ranked.head(3)
    other_kpis = ranked.iloc[3:]

    # -------------------------------
    # Top 3 KPI FACT BLOCKS
    # -------------------------------
    top_facts = []
    for _, row in top_kpis.iterrows():
        top_facts.append({
            "KPI": row["Kpi"],
            "DIRECTION": direction_bucket(row["diff"]),
            "CHANGE_VALUE": round(row["abs_diff"], 2),
            "MAGNITUDE": magnitude_bucket(row["abs_diff"]),
            "UNIT": "percentage points" if "%" in row["Kpi"] else "units"
        })

    # -------------------------------
    # Remaining KPI AGGREGATED FACTS
    # -------------------------------
    if len(other_kpis) > 0:
        other_facts = {
            "KPI_COUNT": len(other_kpis),
            "AVG_CHANGE": round(other_kpis["abs_diff"].mean(), 2),
            "MAGNITUDE": magnitude_bucket(other_kpis["abs_diff"].mean()),
            "DIRECTION_BALANCE": (
                "mostly_increase" if (other_kpis["diff"] > 0).mean() > 0.7
                else "mostly_decrease" if (other_kpis["diff"] > 0).mean() < 0.3
                else "mixed"
            )
        }
    else:
        other_facts = None

    # -------------------------------
    # Model input = FACT BLOCK ONLY
    # (no narrative text embedded)
    # -------------------------------
    model_input = (
        "Write a short insight-style summary using only the facts below.\n\n"
        "TOP_KPI_FACTS:\n"
        f"{top_facts}\n\n"
        "OTHER_KPI_FACTS:\n"
        f"{other_facts}"
    )

    # -------------------------------
    # Controlled variation generation
    # -------------------------------
    output = generator(
        model_input,
        max_new_tokens=120,
        do_sample=True,
        temperature=0.6,
        top_p=0.85,
        repetition_penalty=1.1
    )[0]["generated_text"]

    return ranked.head(top_n)[["Kpi", "abs_diff"]], output

# ------------------------------------------------
# Gradio UI (Hugging Face Space)
# ------------------------------------------------
with gr.Blocks(title="Network Insight Summary") as demo:
    gr.Markdown("## 📊 Network Insight Summary")
    gr.Markdown(
        "Upload KPI CSV to generate a concise, insight-style summary "
        "highlighting the most significant KPI changes."
    )

    csv_input = gr.File(file_types=[".csv"])
    top_n_input = gr.Slider(
        minimum=3,
        maximum=6,
        value=5,
        step=1,
        label="KPIs to Display"
    )

    btn = gr.Button("Generate Insight")

    table = gr.Dataframe(label="Top KPI Changes")
    summary = gr.Textbox(label="Insight Summary", lines=4)

    btn.click(
        analyze_kpi,
        inputs=[csv_input, top_n_input],
        outputs=[table, summary]
    )

demo.launch()