-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathparaphraser.py
More file actions
143 lines (124 loc) · 6.09 KB
/
paraphraser.py
File metadata and controls
143 lines (124 loc) · 6.09 KB
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
import os
import threading
from concurrent.futures import ThreadPoolExecutor, as_completed
from typing import List
import backoff
import openai
from openai import AzureOpenAI
from tqdm import tqdm
from paraboth.utils import diskcache_cache
from paraboth.paraphraser import BaseParaphraser
class Paraphraser(BaseParaphraser):
def __init__(self):
self.client = AzureOpenAI(
api_key=os.getenv("AZURE_OPENAI_KEY"),
api_version=os.getenv("AZURE_OPENAI_API_VERSION"),
azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),
)
self.paraphrasing_not_possible = "paraphrasing_not_possible"
self.verbose = True
self.max_retries = 5 # Maximum number of retries on rate limit error
self.lock = threading.Lock() # For thread-safe printing
@diskcache_cache(cache_dir="paraphrase_cache", cache_size=10**9) # 1GB
def paraphrase(self, sentence: str, n_sentences: int = 6):
sentence = sentence.strip()
if len(sentence) == 0:
print(f"Error! Received empty sentence for paraphrasing.")
return f"{self.paraphrasing_not_possible}"
return self._paraphrase_with_backoff(sentence, n_sentences)
@backoff.on_exception(
backoff.expo,
openai.RateLimitError,
max_time=60, # Maximum total retry time in seconds
)
def _paraphrase_with_backoff(self, sentence: str, n_sentences: int):
prompt = f"""
Paraphrasiere den folgenden Satz <{sentence}> {n_sentences} mal und gib sie folgendermassen zurück: <paraphrased_sentence_1>|<paraphrased_sentence_2>|...|<paraphrased_sentence_n>
Anweisungen:
- Behalte die Wortreihenfolge im Satz so weit wie möglich bei.
- Behalte die ursprüngliche Bedeutung so weit wie möglich bei, während du Variationen einführst.
- Wenn der Satz im Perfekt steht (z.B. "Er ist schwimmen gegangen"), überlege, ihn umzuformulieren, um der Schweizerdeutschen Syntax zu entsprechen. Beispiel: "er ist schwimmen gegangen" -> "er ging schwimmen".
- Die Daten, die du erhältst, sind normalisiert, enthalten also keine Sonderzeichen und sind komplett kleingeschrieben.
Folgende Regeln zählen für den Output:
- Antworte im normalisierten Format ohne jegliche Satzzeichen.
- Wenn nichts paraphrasiert werden kann, gibt "{self.paraphrasing_not_possible}" zurück.
- Falls weniger als {n_sentences} paraphrasierte Sätze generiert werden können, gib nur die generierten Sätze zurück.
- Es sollen strings in der liste zurückgegeben werden, also <paraphrased_sentence_1>|<paraphrased_sentence_2>|...|<paraphrased_sentence_n>. Sehr wichtig!
- Es soll IMMER entweder {self.paraphrasing_not_possible} ODER eine Liste mit strings zurückgegeben werden, nichts anderes.
Deine Ausgabe soll folgendermassen aussehen:
<paraphrased_sentence_1>|<paraphrased_sentence_2>|...|<paraphrased_sentence_n>
ODER
{self.paraphrasing_not_possible}
"""
try:
response = self.client.chat.completions.create(
model="gpt-4o-mini", # Ensure this matches your deployment name
messages=[
{
"role": "system",
"content": "Du bist ein Experte im Paraphrasieren von Sätzen.",
},
{"role": "user", "content": prompt},
],
seed=42,
)
except openai.BadRequestError as e:
print(f"Error: {e}\n")
return f"{self.paraphrasing_not_possible}"
# Check for content filtering
choice = response.choices[0]
if choice.finish_reason == "content_filter":
if self.verbose:
with self.lock:
print(f"Content filtered for sentence: '{sentence}'")
return f"{self.paraphrasing_not_possible}"
if choice.message.content is None:
if self.verbose:
with self.lock:
print(f"No content returned for sentence: '{sentence}'")
print(f"Response: {response}")
return f"{self.paraphrasing_not_possible}"
answer = response.choices[0].message.content.strip()
if len(answer) == 0:
print(f"Error! Paraphrasing '{sentence}' returned an empty string.")
return f"{self.paraphrasing_not_possible}"
if self.paraphrasing_not_possible in answer:
if self.verbose:
with self.lock:
print(f"Paraphrasing not possible for sentence: '{sentence}'")
return f"{self.paraphrasing_not_possible}"
return answer
def paraphrase_list(self, sentences: List[str], n_sentences=6, min_words=3):
results = [None] * len(sentences)
def process_sentence(sentence):
if len(sentence.split()) >= min_words:
paraphrase = self.paraphrase(sentence, n_sentences)
if paraphrase != self.paraphrasing_not_possible:
paraphrased = [sentence] + paraphrase.split("|")
else:
paraphrased = [sentence]
else:
paraphrased = [sentence]
return [x for x in paraphrased if x.strip() != ""]
results = [None] * len(sentences)
with ThreadPoolExecutor(max_workers=30) as executor:
futures = {
executor.submit(process_sentence, sentence): i
for i, sentence in enumerate(sentences)
}
for future in tqdm(as_completed(futures), total=len(futures)):
index = futures[future]
results[index] = future.result()
return results
# Usage example
if __name__ == "__main__":
paraphraser = Paraphraser()
original_sentences = [
"Ich habe es versucht",
"Ich bin müde",
"Ein Kaffee mit Creme ohne Zucker",
"Das Velo ist meins.",
]
paraphrased_sentence = paraphraser.paraphrase_list(original_sentences)
print(f"Original: {original_sentences}")
print(f"Paraphrased: {paraphrased_sentence}")