File size: 10,891 Bytes
8d959a7
 
1d5ab84
8d959a7
5e37144
8d959a7
 
 
 
1d5ab84
 
 
 
e8aacfb
ce24f5e
1d5ab84
 
 
 
 
 
 
 
 
 
ce34d64
 
 
 
 
 
 
 
1d5ab84
 
ce34d64
 
 
 
 
 
1d5ab84
8d959a7
 
 
 
 
 
5e37144
8d959a7
 
 
a6028d3
8d959a7
a6028d3
8d959a7
 
5e37144
8d959a7
 
 
 
 
e8aacfb
 
 
 
ce34d64
a12fb0a
 
 
 
b46bc02
ce34d64
 
 
b46bc02
 
1365073
 
 
 
 
ce34d64
 
 
1365073
 
cf68153
ce34d64
 
 
5e37144
cf68153
 
 
 
 
8d959a7
 
 
 
6045345
 
 
 
81de0ef
1d5ab84
 
 
ce34d64
 
 
1d5ab84
 
81de0ef
 
1d5ab84
 
 
 
 
 
ce34d64
 
 
 
 
 
 
 
1d5ab84
 
 
ce34d64
 
 
 
 
 
 
 
 
1d5ab84
 
81de0ef
 
 
 
 
 
 
5e37144
81de0ef
 
 
 
 
 
 
2bc1a5b
 
 
81de0ef
5e37144
81de0ef
 
 
 
 
8d959a7
 
a6028d3
8d959a7
 
 
 
 
 
 
 
 
a6028d3
8d959a7
 
 
 
 
 
 
 
5e37144
8d959a7
5e37144
1d5ab84
8d959a7
 
1d5ab84
8d959a7
1d5ab84
ce34d64
8d959a7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a6028d3
8d959a7
 
 
 
 
5e37144
8d959a7
ce24f5e
 
 
1d5ab84
 
ce34d64
 
 
1d5ab84
 
 
 
 
 
 
 
8d43785
 
 
 
8d959a7
 
 
 
 
 
 
 
 
 
a6028d3
 
 
 
8d959a7
 
 
 
 
 
 
 
 
 
 
5e37144
 
 
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
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
import copy
import dataclasses
import logging
from enum import auto, Enum
from typing import List, Tuple, Any, Union, Generator

IGNORE_TOKEN_ID = -100


class PromptStyle(Enum):
    instruct = "instruct"
    chat = "chat"


class AlpacaPrompter:
    system_prompt = "Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request.\n\n"
    system_no_input_prompt = "Below is an instruction that describes a task. Write a response that appropriately completes the request.\n\n"
    prompt_style = None

    def __init__(self, prompt_style="instruct"):
        self.prompt_style = prompt_style
        self.match_prompt_style()

    def match_prompt_style(self):
        if self.prompt_style == PromptStyle.instruct.value:
            self.prompt_input = (
                self.system_prompt
                + "### Instruction:\n{instruction}\n\n### Input:\n{input}\n\n### Response:\n"
            )
            self.prompt_no_input = (
                self.system_no_input_prompt
                + "### Instruction:\n{instruction}\n\n### Response:\n"
            )
            self.response_split = "### Response:"
        if self.prompt_style == PromptStyle.chat.value:
            self.prompt_input = (
                self.system_prompt + "USER: {instruction}\n{input}\nASSISTANT:"
            )
            self.prompt_no_input = (
                self.system_no_input_prompt + "USER: {instruction}\nASSISTANT:"
            )
            self.response_split = "ASSISTANT:"

    def build_prompt(
        self,
        instruction: str,
        input: Union[None, str] = None,
        output: Union[None, str] = None,
    ) -> Generator[str, None, None]:
        # returns the full prompt from instruction and optional input
        # if a label (=response, =output) is provided, it's also appended.
        if input:
            res = self.prompt_input.format(instruction=instruction, input=input)
        else:
            res = self.prompt_no_input.format(instruction=instruction)
        if output:
            res = f"{res}{output}"
        yield res

    def get_response(self, output: str) -> str:
        return output.split(self.response_split)[1].strip()


class UnpromptedPrompter(AlpacaPrompter):
    system_prompt = ""
    system_no_input_prompt = ""


class JeopardyPrompter(AlpacaPrompter):
    prompt_input = "Below is a Jeopardy clue paired with input providing the category of the clue. Write a concise response that best answers tbe clue given the category.\n\n### Instruction:\n{instruction}\n\n### Input:\n{input}\n\n### Response:\n"


class MultipleChoiceExplainPrompter(AlpacaPrompter):
    system_prompt = (
        "Choose the answer that best answers the question. Explain your reasoning."
    )


class MultipleChoiceConcisePrompter(AlpacaPrompter):
    prompt_input = "Choose the answer that best answers the question. Be concise in your response.\n\nUSER: {instruction}\n{input}\nASSISTANT:\n"


class SummarizeTLDRPrompter(AlpacaPrompter):
    prompt_no_input = (
        "USER: Summarize the following article as a TL;DR.\n{instruction}\nASSISTANT:"
    )


class CompletionPrompter(AlpacaPrompter):
    def build_prompt(
        self, instruction: str, input=None, output=None
    ) -> Generator[str, None, None]:
        yield instruction

    def get_response(self, output: str) -> str:
        return output.strip()


class GPTeacherPrompter(AlpacaPrompter):
    ...


class NomicGPT4AllPrompter(AlpacaPrompter):
    ...


class ReflectAlpacaPrompter:
    system_prompt = "Below is an instruction that describes a task, paired with an input that provides further context. You, the Assistant, should generate a response as if it were an abstract for an academic or technical paper on the query along with a methodology. Then generate an Agent Reflection where you create a long form response as if from subject matter expert, be verbose, diligent, and creative in your application of knowledge, apply it through the lens of the response generated by the assistant. Look for flawed reasoning, faulty logic, or other mistakes in the method. Finally, generate a final response and method for the user with the Assistant abstract and Reflection analysis as augmentations to the generation\n\n"
    system_no_input_prompt = "Below is an instruction that describes a task. You, the Assistant, should generate a response as if it were an abstract for an academic or technical paper on the query along with a methodology. Then generate an Agent Reflection where you create a long form response as if from subject matter expert, be verbose, diligent, and creative in your application of knowledge, apply it through the lens of the response generated by the assistant. Look for flawed reasoning, faulty logic, or other mistakes in the method. Finally, generate a final response and method for the user with the Assistant abstract and Reflection analysis as augmentations to the generation\n\n"

    prompt_input = (
        "### Instruction:\n{instruction}\n\n### Input:\n{input}\n\n### Response:\n"
    )
    prompt_no_input = "### Instruction:\n{instruction}\n\n### Response:\n"
    agent_label = "### Thought:\n{output}\n\n### Agent Reflection:\n{reflection}\n\n### Final Response:\n{corrected}"
    response_split = "### Response:"

    def __init__(self, prompt_style="instruct"):
        self.prompt_style = prompt_style
        self.match_prompt_style()

    def match_prompt_style(self):
        if self.prompt_style == PromptStyle.instruct.value:
            self.prompt_input = (
                self.system_prompt
                + "### Instruction:\n{instruction}\n\n### Input:\n{input}\n\n### Response:\n"
            )
            self.prompt_no_input = (
                self.system_no_input_prompt
                + "### Instruction:\n{instruction}\n\n### Response:\n"
            )
            self.agent_label = "### Thought:\n{output}\n\n### Agent Reflection:\n{reflection}\n\n### Final Response:\n{corrected}"
            self.response_split = "### Final Response:"
        if self.prompt_style == PromptStyle.chat.value:
            self.prompt_input = (
                self.system_prompt + "USER: {instruction}\n{input}\nASSISTANT:"
            )
            self.prompt_no_input = (
                self.system_no_input_prompt + "USER: {instruction}\nASSISTANT:"
            )
            self.agent_label = (
                "\nTHOUGHT: {output}\nASSISTANT REFLECTION: {reflection}\nASSISTANT:"
            )
            self.response_split = "ASSISTANT:"

    def build_prompt(
        self,
        instruction: str,
        input: Union[None, str] = None,
        output: Union[None, str] = None,
        reflection: Union[None, str] = None,
        corrected: Union[None, str] = None,
    ) -> Generator[str, None, None]:
        # returns the full prompt from instruction and optional input
        # if a label (=response, =output) is provided, it's also appended.
        if input:
            res = self.prompt_input.format(instruction=instruction, input=input)
        else:
            res = self.prompt_no_input.format(instruction=instruction)
        if output and reflection and corrected:
            label = self.agent_label.format(
                output=output, reflection=reflection, corrected=corrected
            )
            res = f"{res}{label}"
        yield res

    def get_response(self, output: str) -> str:
        return output.split(self.response_split)[1].strip()


class SeparatorStyle(Enum):
    """Different separator style."""

    SINGLE = auto()
    TWO = auto()
    DOLLY = auto()


# TODO clean this 💩 up
@dataclasses.dataclass
class Conversation:
    """A class that keeps all conversation history."""

    system: str
    roles: List[str]
    messages: List[List[str]]
    offset: int
    sep_style: SeparatorStyle = SeparatorStyle.SINGLE
    sep: str = "###"
    sep2: str = None

    def get_prompt(self) -> Generator[str, None, None]:
        seps = [self.sep, self.sep2]
        preamble = self.system + seps[0]
        yield preamble
        for i, (role, message) in enumerate(self.messages):
            if message:
                yield (role + ":", " " + message)
            else:
                logging.warning("role with empty message: " + role)
                yield (role + ":",)

    def copy(self):
        return Conversation(
            system=self.system,
            roles=self.roles,
            messages=[[x, y] for x, y in self.messages],
            offset=self.offset,
            sep_style=self.sep_style,
            sep=self.sep,
            sep2=self.sep2,
        )

    def append_message(self, role, message):
        self.messages.append([role, message])


conv_vicuna_v1_1 = Conversation(
    system="A chat between a curious user and an artificial intelligence assistant. "
    "The assistant gives helpful, detailed, and polite answers to the user's questions.",
    roles=["USER", "ASSISTANT"],
    messages=[],
    offset=0,
    sep_style=SeparatorStyle.TWO,
    sep=" ",
    sep2=" ",
)


class ShareGPTPrompter:
    def __init__(self, prompt_style=None):
        if prompt_style != PromptStyle.chat.value:
            raise Exception(
                f"unsupported prompt_style for ShareGPTPrompter({prompt_style})"
            )

    # def match_prompt_style(self):
    #     if self.prompt_style == PromptStyle.chat.value:
    #         self.prompt_input = self.system_prompt + "USER: {instruction}\n{input}\nASSISTANT:"
    #         self.prompt_no_input = self.system_no_input_prompt + "USER: {instruction}\nASSISTANT:"
    #         self.response_split = "ASSISTANT:"

    def build_prompt(self, source, *args, **kwargs) -> Generator[str, None, None]:
        # ignore the system prompt if provided
        if source[0]["from"] == "system":
            source.pop(0)

        if len(source) < 2:
            # If there isn't a back and forth conversation, ignore it
            # also happens on the data splitting leaving empty conversations
            raise IndexError

        conv = conv_vicuna_v1_1.copy()
        roles = {"human": conv.roles[0], "gpt": conv.roles[1]}

        try:
            # Apply prompt templates
            if (
                source[0]["from"] not in roles
                or roles[source[0]["from"]] != conv.roles[0]
            ):
                # Skip the first one if it is not from human
                source = source[1:]
        except IndexError as e:
            # sometimes there is a bing or system chat
            raise e

        conv.messages = []
        for j, sentence in enumerate(source):
            role = roles[sentence["from"]]
            assert role == conv.roles[j % 2]
            conv.append_message(role, sentence["value"])

        for part in conv.get_prompt():
            yield part