File size: 7,472 Bytes
416f715
d35fa36
e4329d1
d35fa36
48dfdaf
e4329d1
 
48dfdaf
d35fa36
 
e4329d1
 
a231b39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48dfdaf
 
fb1d2b9
ec2affb
a231b39
ec2affb
 
 
 
a231b39
ec2affb
fb1d2b9
 
 
ec2affb
a231b39
 
 
 
 
 
 
 
 
 
 
 
 
48dfdaf
 
 
a231b39
 
 
 
48dfdaf
e4329d1
 
 
 
 
 
 
2b2c2cb
e4329d1
48dfdaf
ce59f44
e4329d1
48dfdaf
e4329d1
 
 
 
 
d35fa36
48dfdaf
e4329d1
 
 
 
 
48dfdaf
 
e4329d1
ec2affb
 
d35fa36
e4329d1
 
 
48dfdaf
 
a231b39
 
ec2affb
 
 
 
 
 
 
 
 
 
a231b39
e4329d1
ec2affb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ce59f44
ec2affb
 
 
 
 
a231b39
ec2affb
48dfdaf
a231b39
e4329d1
ec2affb
 
 
 
 
 
48dfdaf
a231b39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e4329d1
d35fa36
a231b39
e4329d1
 
48dfdaf
ec2affb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a231b39
d35fa36
e4329d1
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
import spaces
import os
from typing import List
import gradio as gr

os.environ["TOKENIZERS_PARALLELISM"] = "false"
from kalm_reranker import KaLMReranker

MODEL_ID = "KaLM-Embedding/KaLM-Reranker-V1-Nano"
DEFAULT_INSTRUCTION = "Given a query, retrieve documents that answer the query."
MAX_DOCS = 20
MAX_DOC_CHARS = 4000
INIT_DOCS = 4

EXAMPLES = [
    {
        "label": "Capital of China",
        "query": "What is the capital of China?",
        "docs": [
            "The capital of China is Beijing.",
            "Gravity attracts bodies toward one another.",
            "Paris is the capital of France.",
        ],
    },
    {
        "label": "Efficient Reranking",
        "query": "Which model is suitable for efficient reranking?",
        "docs": [
            "KaLM-Reranker-V1-Nano is designed for efficient reranking.",
            "Large language models are often expensive for reranking.",
            "Image classifiers are used for visual recognition.",
        ],
    },
    {
        "label": "KaLM-Reranker Design",
        "query": "What is KaLM-Reranker-V1 designed for?",
        "docs": [
            "KaLM-Reranker-V1 is a reranker for compressed document reranking.",
            "KaLM-Embedding is a general-purpose embedding model.",
            "Weather forecasting predicts future weather conditions.",
        ],
    },
]


def _make_vis_updates(count: int):
    return [gr.update(visible=(i < count)) for i in range(MAX_DOCS)]


def add_doc(count: int):
    count = min(count + 1, MAX_DOCS)
    return _make_vis_updates(count) + [count]


def remove_doc(count: int):
    count = max(count - 1, 1)
    return _make_vis_updates(count) + [count]


def load_example(example_label):
    if not example_label:
        query_upd = gr.update(value="")
        doc_upds = [
            gr.update(value="", visible=(i < INIT_DOCS))
            for i in range(MAX_DOCS)
        ]
        return [query_upd] + doc_upds + [INIT_DOCS]

    ex = next((e for e in EXAMPLES if e["label"] == example_label), None)
    if ex is None:
        return [gr.update()] + [gr.update() for _ in range(MAX_DOCS)] + [INIT_DOCS]

    docs = ex["docs"]
    count = len(docs)
    query_upd = gr.update(value=ex["query"])
    doc_upds = [
        gr.update(value=docs[i] if i < count else "", visible=(i < count))
        for i in range(MAX_DOCS)
    ]
    return [query_upd] + doc_upds + [count]


@spaces.GPU
def rerank(query: str, instruction: str, chunk_size: int, *doc_texts: str):
    docs = [doc.strip() for doc in doc_texts if doc and doc.strip()]
    docs = docs[:MAX_DOCS]
    docs = [doc[:MAX_DOC_CHARS] for doc in docs]

    if not query.strip():
        return [], "Please input a query."
    if not docs:
        return [], "Please input at least one candidate document."

    reranker = KaLMReranker(
        MODEL_ID,
        device=None,
        dtype=None,
        batch_size=4,
        query_max_length=512,
        max_length=1024,
        chunk_size=chunk_size,
    )

    query = query.strip()
    instruction = instruction.strip() or DEFAULT_INSTRUCTION

    try:
        rankings = reranker.rank(
            query=query,
            documents=docs,
            instruction=instruction,
        )

        table = []
        for rank_idx, item in enumerate(rankings, start=1):
            corpus_id = item["corpus_id"]
            score = float(item["score"])
            doc = docs[corpus_id]
            table.append([rank_idx, corpus_id, round(score, 6), doc])

        summary = (
            f"Reranked {len(docs)} documents with "
            f"`{MODEL_ID}` (chunk_size={chunk_size}). Higher score means more relevant."
        )
        return table, summary
    except Exception as error:
        return [], f"Error during reranking: {repr(error)}"


# ── UI ──────────────────────────────────────────────────────────────────────

with gr.Blocks(title="KaLM-Reranker-V1 Demo") as demo:
    gr.Markdown(
        """
# KaLM-Reranker-V1 Demo
**KaLM-Reranker-V1** is a fast but not late-interaction reranker for compressed document reranking.
Input a query and several candidate documents. The demo returns relevance scores and reranked results.
        """
    )

    with gr.Row():
        # ── Left column: inputs ──
        with gr.Column(scale=1):
            query_input = gr.Textbox(
                label="Query",
                value="",
                lines=2,
            )
            instruction_input = gr.Textbox(
                label="Instruction",
                value=DEFAULT_INSTRUCTION,
                lines=2,
            )
            chunk_size_input = gr.Dropdown(
                label="Chunk Size",
                choices=[2, 4, 8, 16],
                value=2,
                interactive=True,
            )

            example_selector = gr.Dropdown(
                label="Load Example",
                choices=[""] + [ex["label"] for ex in EXAMPLES],
                value="",
                interactive=True,
            )

            gr.Markdown("### Candidate Documents")
            doc_count = gr.State(value=INIT_DOCS)
            doc_textboxes: List[gr.Textbox] = []

            with gr.Column() as doc_list:
                for i in range(MAX_DOCS):
                    visible = i < INIT_DOCS
                    tb = gr.Textbox(
                        label=f"Document {i + 1}",
                        lines=2,
                        placeholder=f"Enter document {i + 1}...",
                        visible=visible,
                    )
                    doc_textboxes.append(tb)

            with gr.Row():
                add_btn = gr.Button("+ Add Document", size="sm")
                remove_btn = gr.Button("- Remove Last", size="sm")

            submit = gr.Button("Rerank", variant="primary")

        # ── Right column: outputs ──
        with gr.Column(scale=1):
            output_table = gr.Dataframe(
                headers=["Rank", "Corpus ID", "Score", "Document"],
                label="Reranking Results",
                wrap=True,
            )
            output_summary = gr.Markdown()

    # ── Events ──
    add_btn.click(
        fn=add_doc,
        inputs=[doc_count],
        outputs=doc_textboxes + [doc_count],
    )
    remove_btn.click(
        fn=remove_doc,
        inputs=[doc_count],
        outputs=doc_textboxes + [doc_count],
    )
    example_selector.change(
        fn=load_example,
        inputs=[example_selector],
        outputs=[query_input] + doc_textboxes + [doc_count],
    )
    submit.click(
        fn=rerank,
        inputs=[query_input, instruction_input, chunk_size_input] + doc_textboxes,
        outputs=[output_table, output_summary],
    )

    gr.Markdown(
        """
## Citation

If you find this demo useful, please cite:

```bibtex
@misc{zhao2026kalmrerankerv1,
      title={KaLM-Reranker-V1: Fast but Not Late Interaction for Compressed Document Reranking},
      author={Xinping Zhao and Jiaxin Xu and Ziqi Dai and Xin Zhang and Shouzheng Huang and Danyu Tang and Xinshuo Hu and Meishan Zhang and Baotian Hu and Min Zhang},
      year={2026},
      eprint={2606.22807},
      archivePrefix={arXiv},
      primaryClass={cs.CL},
      url={https://arxiv.org/abs/2606.22807},
}
```
        """
    )


if __name__ == "__main__":
    demo.launch()