File size: 6,523 Bytes
0703e71
68c11f0
 
 
d232ed1
5b6755d
68c11f0
 
d232ed1
0703e71
5b6755d
60ffe71
a1f7b63
 
 
 
7be224f
 
 
 
5b6755d
210b40c
 
 
 
 
090dd00
210b40c
 
 
090dd00
210b40c
 
 
090dd00
210b40c
 
 
 
 
 
f21f2ed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a73aede
f21f2ed
 
a73aede
f21f2ed
 
 
 
 
 
0a402b2
f21f2ed
0a402b2
0108e87
f21f2ed
 
1418604
ec09f15
1418604
a1f7b63
1418604
a1f7b63
37528ea
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e899803
1418604
 
 
 
 
6b786ce
eb7b56f
1aa6e35
090dd00
bc3ac0f
5b6755d
 
7f45e1c
 
76c4bfe
8672bbc
2cdd4cb
 
1861169
87d4d46
d232ed1
6b786ce
 
 
 
 
 
 
 
 
bd26917
f21f2ed
f2c7470
f21f2ed
6b786ce
f2c7470
c43c3ad
 
 
 
 
d5478e2
 
 
c43c3ad
 
2cdd4cb
1418604
bd26917
1418604
 
 
 
d232ed1
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
import os
import re
from typing import List, Tuple, Union
from pathlib import Path
import gradio as gr
import openai



HF_TOKEN = os.environ.get("HF_TOKEN", None)
LEPTON_API_TOKEN = os.environ.get("LEPTON_API_TOKEN", None)

# client=openai.OpenAI(
#     base_url="https://yb15a7dy-patronus-lynx-8b-v1-1.tin.lepton.run/api/v1/",
#     api_key=LEPTON_API_TOKEN
# )
# client=openai.OpenAI(
#     base_url="https://yb15a7dy-lynx-70b.tin.lepton.run/api/v1/",
#     api_key=LEPTON_API_TOKEN
# )

PROMPT = """
Given the following QUESTION, DOCUMENT and ANSWER you must analyze the provided answer and determine whether it is faithful to the contents of the DOCUMENT. The ANSWER must not offer new information beyond the context provided in the DOCUMENT. The ANSWER also must not contradict information provided in the DOCUMENT. Output your final verdict by strictly following this format: "PASS" if the answer is faithful to the DOCUMENT and "FAIL" if the answer is not faithful to the DOCUMENT. Show your reasoning.

--
QUESTION (THIS DOES NOT COUNT AS BACKGROUND INFORMATION):
{question}

--
DOCUMENT:
{document}

--
ANSWER:
{answer}

--

Your output should be in JSON FORMAT with the keys "REASONING" and "SCORE":
{{"REASONING": <your reasoning as bullet points>, "SCORE": <your final score>}}
"""

HEADER = """
# Patronus Lynx Demo
<table bgcolor="#1E2432" cellspacing="0" cellpadding="0"  width="450">
<tr style="height:50px;">
<td style="text-align: center;">
<a href="https://www.patronus.ai">
<img src="https://cdn.prod.website-files.com/64e655d42d3be60f582d0472/64ede352897bcddbe2d41207_patronusai_final_logo.svg" width="200" height="40" />
</a>
</td>
</tr>
</table>
<table bgcolor="#1E2432" cellspacing="0" cellpadding="0"  width="450">
<tr style="height:30px;">
<td style="text-align: center;">
<a href="https://huggingface.co/PatronusAI/Llama-3-Patronus-Lynx-8B-Instruct"><img src="https://img.shields.io/badge/%F0%9F%A4%97%20Model_Card-Huggingface-orange" height="20"></a>
</td>
<td style="text-align: center;">
<a href="https://github.com/patronus-ai/Lynx-hallucination-detection"><img src="https://postimage.me/images/2024/03/04/GitHub_Logo_White.png" width="100" height="20"></a>
</td>
<td style="text-align: center; color: white;">
<a href="https://arxiv.org/abs/2407.08488"><img src="https://img.shields.io/badge/arXiv-2407.08488-b31b1b.svg" height="20"></a>
</td>
</tr>
</table>

**Patronus Lynx** is a state-of-the-art open-source model for hallucination detection.

**Getting Started**: Provide a question and document or context given to your model in addition to the answer given by the model and then click submit. The output panel will indicate whether the reponse is a hallucination (Fail) or if it is faithful to the given document or context (Pass) through the score Pass or Fail and provide reasoning behind the score.
"""

def update_client_base_url(model_name):
    if model_name == "Patronus Lynx 8B v1.1":
        return "https://yb15a7dy-patronus-lynx-8b-v1-1.tin.lepton.run/api/v1/"
    elif model_name == "Patronus Lynx 70B":
        return "https://yb15a7dy-lynx-70b.tin.lepton.run/api/v1/"

def parse_patronus_lynx_response(
    response: str,
) -> Tuple[bool, Union[List[str], None]]:
    """
    Parses the response from the Patronus Lynx LLM and returns a tuple of:
    - Whether the response is hallucinated or not.
    - A reasoning trace explaining the decision.
    """
    # Default to hallucinated
    hallucination, reasoning = True, None
    reasoning_pattern = r'"REASONING":\s*\[(.*?)\]'
    score_pattern = r'"SCORE":\s*"?\b(PASS|FAIL)\b"?'

    reasoning_match = re.search(reasoning_pattern, response, re.DOTALL)
    score_match = re.search(score_pattern, response)

    if score_match:
        score = score_match.group(1)
        if score == "PASS":
            hallucination = False
    if reasoning_match:
        reasoning_content = reasoning_match.group(1)
        reasoning = re.split(r"['\"],\s*['\"]", reasoning_content)

    return hallucination, reasoning

def model_call(question, document, answer, client_base_url):
    client = openai.OpenAI(
        base_url=client_base_url,
        api_key=LEPTON_API_TOKEN
    )
    print("CLIENT AND CLIENT BASE URL", client, client_base_url)
    if question == "" or document == "" or answer == "":
        return "", ""
    NEW_FORMAT = PROMPT.format(question=question, document=document, answer=answer)
    print("ENTIRE NEW_FORMAT", NEW_FORMAT)
    response = client.completions.create(
        model="gpt-3.5-turbo-instruct",
        prompt=NEW_FORMAT,
        temperature=0.0
    )
    print("RESPONSE FROM CLIENT:", response)
    hallucination, reasoning = parse_patronus_lynx_response(response.choices[0].text)
    score = "FAIL" if hallucination else "PASS"
    combined_reasoning = " ".join(reasoning)[1:-1]
    return combined_reasoning, score

# inputs = [
#     gr.Textbox(label="Question"),
#     gr.Textbox(label="Document"),
#     gr.Textbox(label="Answer")
# ]
# outputs = [
#     gr.Textbox(label="Reasoning"),
#     gr.Textbox(label="Score")
# ]

with gr.Blocks() as demo:
    base_url_state = gr.State(update_client_base_url("Patronus Lynx 8B v1.1"))
    gr.Markdown(HEADER)
    # gr.Interface(fn=model_call, inputs=inputs, outputs=outputs)
    model_dropdown = gr.Dropdown(choices=["Patronus Lynx 8B v1.1", "Patronus Lynx 70B"], value="Patronus Lynx 8B v1.1", label="Model", interactive=True)
    with gr.Row():
        with gr.Column(scale=1):
            question = gr.Textbox(label="Question")
            document = gr.Textbox(label="Document")
            answer = gr.Textbox(label="Answer")
            with gr.Row():
                clear_btn = gr.ClearButton([question, document, answer])
                submit_button = gr.Button("Submit")
        with gr.Column(scale=1):
            reasoning = gr.Textbox(label="Reasoning")
            score = gr.Textbox(label="Score (FAIL if Hallucinated, PASS if not)")
    model_dropdown.change(fn=update_client_base_url, inputs=[model_dropdown], outputs=[base_url_state])

    submit_button.click(fn=model_call, inputs=[question, document, answer, base_url_state], outputs=[reasoning, score])
    question.submit(fn=model_call, inputs=[question, document, answer, base_url_state], outputs=[reasoning, score])
    document.submit(fn=model_call, inputs=[question, document, answer, base_url_state], outputs=[reasoning, score])
    answer.submit(fn=model_call, inputs=[question, document, answer, base_url_state], outputs=[reasoning, score])
demo.launch()