pygpt-net 2.6.3__py3-none-any.whl → 2.6.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pygpt_net/CHANGELOG.txt +5 -0
- pygpt_net/__init__.py +1 -1
- pygpt_net/config.py +55 -65
- pygpt_net/controller/chat/chat.py +38 -35
- pygpt_net/controller/chat/render.py +144 -217
- pygpt_net/controller/chat/stream.py +51 -25
- pygpt_net/controller/config/config.py +39 -42
- pygpt_net/controller/config/field/checkbox.py +16 -12
- pygpt_net/controller/config/field/checkbox_list.py +36 -31
- pygpt_net/controller/config/field/cmd.py +51 -57
- pygpt_net/controller/config/field/combo.py +33 -16
- pygpt_net/controller/config/field/dictionary.py +48 -55
- pygpt_net/controller/config/field/input.py +50 -32
- pygpt_net/controller/config/field/slider.py +40 -45
- pygpt_net/controller/config/field/textarea.py +20 -6
- pygpt_net/controller/config/placeholder.py +110 -231
- pygpt_net/controller/lang/mapping.py +57 -95
- pygpt_net/controller/lang/plugins.py +64 -55
- pygpt_net/controller/lang/settings.py +39 -38
- pygpt_net/controller/layout/layout.py +11 -2
- pygpt_net/controller/plugins/plugins.py +19 -1
- pygpt_net/controller/ui/mode.py +107 -125
- pygpt_net/core/bridge/bridge.py +5 -5
- pygpt_net/core/command/command.py +149 -219
- pygpt_net/core/ctx/ctx.py +94 -146
- pygpt_net/core/debug/debug.py +48 -58
- pygpt_net/core/models/models.py +74 -112
- pygpt_net/core/modes/modes.py +13 -21
- pygpt_net/core/plugins/plugins.py +154 -177
- pygpt_net/core/presets/presets.py +103 -176
- pygpt_net/core/render/web/body.py +2 -3
- pygpt_net/core/render/web/renderer.py +109 -180
- pygpt_net/core/text/utils.py +28 -44
- pygpt_net/core/tokens/tokens.py +104 -203
- pygpt_net/data/config/config.json +2 -2
- pygpt_net/data/config/models.json +2 -2
- pygpt_net/item/ctx.py +141 -139
- pygpt_net/plugin/agent/plugin.py +2 -1
- pygpt_net/plugin/audio_output/plugin.py +5 -2
- pygpt_net/plugin/base/plugin.py +77 -93
- pygpt_net/plugin/bitbucket/plugin.py +3 -2
- pygpt_net/plugin/cmd_code_interpreter/plugin.py +3 -2
- pygpt_net/plugin/cmd_custom/plugin.py +3 -2
- pygpt_net/plugin/cmd_files/plugin.py +3 -2
- pygpt_net/plugin/cmd_history/plugin.py +3 -2
- pygpt_net/plugin/cmd_mouse_control/plugin.py +5 -2
- pygpt_net/plugin/cmd_serial/plugin.py +3 -2
- pygpt_net/plugin/cmd_system/plugin.py +3 -6
- pygpt_net/plugin/cmd_web/plugin.py +3 -2
- pygpt_net/plugin/experts/plugin.py +2 -2
- pygpt_net/plugin/facebook/plugin.py +3 -4
- pygpt_net/plugin/github/plugin.py +4 -2
- pygpt_net/plugin/google/plugin.py +3 -3
- pygpt_net/plugin/idx_llama_index/plugin.py +3 -2
- pygpt_net/plugin/mailer/plugin.py +3 -5
- pygpt_net/plugin/openai_vision/plugin.py +3 -2
- pygpt_net/plugin/real_time/plugin.py +52 -60
- pygpt_net/plugin/slack/plugin.py +3 -4
- pygpt_net/plugin/telegram/plugin.py +3 -4
- pygpt_net/plugin/twitter/plugin.py +3 -4
- pygpt_net/ui/widget/textarea/web.py +18 -14
- {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.4.dist-info}/METADATA +7 -2
- {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.4.dist-info}/RECORD +66 -66
- {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.4.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.4.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.4.dist-info}/entry_points.txt +0 -0
pygpt_net/core/text/utils.py
CHANGED
|
@@ -6,11 +6,9 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date: 2025.08.
|
|
9
|
+
# Updated Date: 2025.08.15 23:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
|
-
import re
|
|
13
|
-
|
|
14
12
|
|
|
15
13
|
def output_html2text(html: str) -> str:
|
|
16
14
|
"""
|
|
@@ -19,46 +17,31 @@ def output_html2text(html: str) -> str:
|
|
|
19
17
|
:param html: HTML content
|
|
20
18
|
:return: Plain text
|
|
21
19
|
"""
|
|
22
|
-
|
|
23
|
-
if html == "":
|
|
20
|
+
if not html:
|
|
24
21
|
return ""
|
|
25
22
|
try:
|
|
26
|
-
|
|
23
|
+
from bs4 import BeautifulSoup, NavigableString
|
|
24
|
+
try:
|
|
25
|
+
soup = BeautifulSoup(html, 'lxml')
|
|
26
|
+
except Exception:
|
|
27
|
+
soup = BeautifulSoup(html, 'html.parser')
|
|
27
28
|
# remove headers from code blocks
|
|
28
|
-
for tag in soup.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
tag.
|
|
32
|
-
for tag in soup.find_all('span', class_='ts'):
|
|
33
|
-
empty = soup.new_tag('span')
|
|
34
|
-
empty.string = ''
|
|
35
|
-
tag.replace_with(empty)
|
|
36
|
-
for tag in soup.find_all('div', class_='name-header'):
|
|
37
|
-
empty = soup.new_tag('span')
|
|
38
|
-
empty.string = ''
|
|
39
|
-
tag.replace_with(empty)
|
|
40
|
-
for tag in soup.find_all('span', class_='toggle-cmd-output'):
|
|
41
|
-
empty = soup.new_tag('span')
|
|
42
|
-
empty.string = ''
|
|
43
|
-
tag.replace_with(empty)
|
|
29
|
+
for tag in soup.select('p.code-header-wrapper'):
|
|
30
|
+
tag.replace_with(NavigableString('\n'))
|
|
31
|
+
for tag in soup.select('span.ts, span.toggle-cmd-output, div.name-header'):
|
|
32
|
+
tag.decompose()
|
|
44
33
|
# add separators
|
|
45
|
-
for tag in soup.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
for p in soup.select('.msg-user .msg p'):
|
|
50
|
-
for br in p.find_all('br'):
|
|
51
|
-
br.replace_with("\n")
|
|
52
|
-
for tag in soup.find_all('div', class_='msg-user'):
|
|
53
|
-
sep = soup.new_tag('p')
|
|
54
|
-
sep.string = '\n\n'
|
|
55
|
-
tag.insert_before(sep)
|
|
34
|
+
for tag in soup.select('div.msg-bot, div.msg-user'):
|
|
35
|
+
tag.insert_before(NavigableString('\n\n'))
|
|
36
|
+
for br in soup.select('.msg-user .msg p br'):
|
|
37
|
+
br.replace_with('\n')
|
|
56
38
|
text = soup.get_text(separator="", strip=False)
|
|
57
39
|
return text.replace('\t', ' ')
|
|
58
|
-
except Exception
|
|
40
|
+
except Exception:
|
|
59
41
|
pass
|
|
60
42
|
return ""
|
|
61
43
|
|
|
44
|
+
|
|
62
45
|
def output_clean_html(html: str) -> str:
|
|
63
46
|
"""
|
|
64
47
|
Clean output HTML content
|
|
@@ -66,20 +49,24 @@ def output_clean_html(html: str) -> str:
|
|
|
66
49
|
:param html: HTML content
|
|
67
50
|
:return: HTML content
|
|
68
51
|
"""
|
|
69
|
-
from bs4 import BeautifulSoup
|
|
70
52
|
try:
|
|
71
|
-
|
|
53
|
+
from bs4 import BeautifulSoup
|
|
54
|
+
try:
|
|
55
|
+
soup = BeautifulSoup(html, 'lxml')
|
|
56
|
+
except Exception:
|
|
57
|
+
soup = BeautifulSoup(html, 'html.parser')
|
|
72
58
|
# remove copy to clipboard from code blocks
|
|
73
|
-
for tag in soup.
|
|
59
|
+
for tag in soup.select('a.code-header-copy'):
|
|
74
60
|
tag.decompose()
|
|
75
61
|
# remove action icons
|
|
76
|
-
for tag in soup.
|
|
62
|
+
for tag in soup.select('div.action-icons'):
|
|
77
63
|
tag.decompose()
|
|
78
64
|
return str(soup)
|
|
79
|
-
except Exception
|
|
65
|
+
except Exception:
|
|
80
66
|
pass
|
|
81
67
|
return html
|
|
82
68
|
|
|
69
|
+
|
|
83
70
|
def has_unclosed_code_tag(text: str) -> bool:
|
|
84
71
|
"""
|
|
85
72
|
Check if HTML content has unclosed code block
|
|
@@ -87,9 +74,6 @@ def has_unclosed_code_tag(text: str) -> bool:
|
|
|
87
74
|
:param text: HTML content
|
|
88
75
|
:return: True if unclosed code block found
|
|
89
76
|
"""
|
|
90
|
-
if text
|
|
77
|
+
if not text:
|
|
91
78
|
return False
|
|
92
|
-
|
|
93
|
-
if len(code_blocks) % 2 != 0:
|
|
94
|
-
return True
|
|
95
|
-
return False
|
|
79
|
+
return (text.count('```') % 2) != 0
|
pygpt_net/core/tokens/tokens.py
CHANGED
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date: 2025.
|
|
9
|
+
# Updated Date: 2025.08.15 23:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from typing import Tuple, List
|
|
13
|
+
from functools import lru_cache
|
|
13
14
|
|
|
14
15
|
import tiktoken
|
|
15
16
|
|
|
@@ -49,41 +50,60 @@ CHAT_MODES = [
|
|
|
49
50
|
]
|
|
50
51
|
|
|
51
52
|
class Tokens:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
Tokens core
|
|
53
|
+
_default_encoding = "cl100k_base"
|
|
54
|
+
_const_cache = {}
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
"""
|
|
56
|
+
def __init__(self, window=None):
|
|
58
57
|
self.window = window
|
|
59
58
|
|
|
59
|
+
@staticmethod
|
|
60
|
+
@lru_cache(maxsize=128)
|
|
61
|
+
def _encoding_name_for_model(model: str | None) -> str:
|
|
62
|
+
if model:
|
|
63
|
+
try:
|
|
64
|
+
return tiktoken.encoding_for_model(model).name
|
|
65
|
+
except KeyError:
|
|
66
|
+
return Tokens._default_encoding
|
|
67
|
+
except ValueError:
|
|
68
|
+
return Tokens._default_encoding
|
|
69
|
+
return Tokens._default_encoding
|
|
70
|
+
|
|
71
|
+
@staticmethod
|
|
72
|
+
@lru_cache(maxsize=64)
|
|
73
|
+
def _get_encoding(encoding_name: str):
|
|
74
|
+
try:
|
|
75
|
+
return tiktoken.get_encoding(encoding_name)
|
|
76
|
+
except Exception:
|
|
77
|
+
return tiktoken.get_encoding(Tokens._default_encoding)
|
|
78
|
+
|
|
79
|
+
@classmethod
|
|
80
|
+
def _const_tokens(cls, text: str, model: str = "gpt-4") -> int:
|
|
81
|
+
enc_name = cls._encoding_name_for_model(model)
|
|
82
|
+
key = (enc_name, text)
|
|
83
|
+
cached = cls._const_cache.get(key)
|
|
84
|
+
if cached is not None:
|
|
85
|
+
return cached
|
|
86
|
+
try:
|
|
87
|
+
enc = cls._get_encoding(enc_name)
|
|
88
|
+
val = len(enc.encode(text))
|
|
89
|
+
except Exception:
|
|
90
|
+
val = 0
|
|
91
|
+
cls._const_cache[key] = val
|
|
92
|
+
return val
|
|
93
|
+
|
|
60
94
|
@staticmethod
|
|
61
95
|
def from_str(
|
|
62
96
|
string: str,
|
|
63
97
|
model: str = "gpt-4"
|
|
64
98
|
) -> int:
|
|
65
|
-
|
|
66
|
-
Return number of tokens from string
|
|
67
|
-
|
|
68
|
-
:param string: string
|
|
69
|
-
:param model: model name
|
|
70
|
-
:return: number of tokens
|
|
71
|
-
"""
|
|
72
|
-
if string is None or string == "":
|
|
99
|
+
if not string:
|
|
73
100
|
return 0
|
|
74
|
-
|
|
75
|
-
default = "cl100k_base"
|
|
76
101
|
try:
|
|
77
102
|
try:
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
else:
|
|
81
|
-
encoding = tiktoken.get_encoding(default)
|
|
82
|
-
except KeyError:
|
|
83
|
-
encoding = tiktoken.get_encoding(default)
|
|
103
|
+
enc_name = Tokens._encoding_name_for_model(model)
|
|
104
|
+
encoding = Tokens._get_encoding(enc_name)
|
|
84
105
|
except ValueError:
|
|
85
106
|
return 0
|
|
86
|
-
|
|
87
107
|
try:
|
|
88
108
|
return len(encoding.encode(str(string)))
|
|
89
109
|
except Exception as e:
|
|
@@ -95,12 +115,6 @@ class Tokens:
|
|
|
95
115
|
|
|
96
116
|
@staticmethod
|
|
97
117
|
def get_extra(model: str = "gpt-4") -> int:
|
|
98
|
-
"""
|
|
99
|
-
Return number of extra tokens
|
|
100
|
-
|
|
101
|
-
:param model: model name
|
|
102
|
-
:return: number of tokens
|
|
103
|
-
"""
|
|
104
118
|
return 3
|
|
105
119
|
|
|
106
120
|
@staticmethod
|
|
@@ -109,27 +123,16 @@ class Tokens:
|
|
|
109
123
|
name: str,
|
|
110
124
|
model: str = "gpt-4"
|
|
111
125
|
) -> int:
|
|
112
|
-
"""
|
|
113
|
-
Return number of tokens from prompt
|
|
114
|
-
|
|
115
|
-
:param text: prompt text
|
|
116
|
-
:param name: input name
|
|
117
|
-
:param model: model name
|
|
118
|
-
:return: number of tokens
|
|
119
|
-
"""
|
|
120
126
|
model, per_message, per_name = Tokens.get_config(model)
|
|
121
127
|
num = 0
|
|
122
|
-
|
|
123
128
|
try:
|
|
124
129
|
num += Tokens.from_str(text, model)
|
|
125
130
|
except Exception as e:
|
|
126
131
|
print("Tokens calc exception", e)
|
|
127
|
-
|
|
128
|
-
if name is not None and name != "":
|
|
132
|
+
if name:
|
|
129
133
|
num += per_message + per_name
|
|
130
134
|
else:
|
|
131
135
|
num += per_message
|
|
132
|
-
|
|
133
136
|
return num
|
|
134
137
|
|
|
135
138
|
@staticmethod
|
|
@@ -137,55 +140,37 @@ class Tokens:
|
|
|
137
140
|
text: str,
|
|
138
141
|
model: str = "gpt-4"
|
|
139
142
|
) -> int:
|
|
140
|
-
|
|
141
|
-
Return number of tokens from text, without any extra tokens
|
|
142
|
-
|
|
143
|
-
:param text: text
|
|
144
|
-
:param model: model name
|
|
145
|
-
:return: number of tokens
|
|
146
|
-
"""
|
|
147
|
-
model, per_message, per_name = Tokens.get_config(model)
|
|
148
|
-
num = 0
|
|
149
|
-
|
|
150
|
-
if text is None or text == "":
|
|
143
|
+
if not text:
|
|
151
144
|
return 0
|
|
152
|
-
|
|
153
145
|
try:
|
|
154
|
-
|
|
146
|
+
return Tokens.from_str(text, model)
|
|
155
147
|
except Exception as e:
|
|
156
148
|
print("Tokens calc exception", e)
|
|
157
|
-
|
|
158
|
-
return num
|
|
149
|
+
return 0
|
|
159
150
|
|
|
160
151
|
@staticmethod
|
|
161
152
|
def from_messages(
|
|
162
153
|
messages: List[dict],
|
|
163
154
|
model: str = "gpt-4"
|
|
164
155
|
) -> int:
|
|
165
|
-
"""
|
|
166
|
-
Return number of tokens from messages list
|
|
167
|
-
|
|
168
|
-
:param messages: messages
|
|
169
|
-
:param model: model name
|
|
170
|
-
:return: number of tokens
|
|
171
|
-
"""
|
|
172
156
|
model, per_message, per_name = Tokens.get_config(model)
|
|
173
157
|
num = 0
|
|
158
|
+
f = Tokens.from_str
|
|
174
159
|
for message in messages:
|
|
175
160
|
num += per_message
|
|
176
161
|
for key, value in message.items():
|
|
177
|
-
# text message
|
|
178
162
|
if isinstance(value, str):
|
|
179
|
-
num +=
|
|
163
|
+
num += f(value)
|
|
180
164
|
if key == "name":
|
|
181
165
|
num += per_name
|
|
182
|
-
# multimodal message
|
|
183
166
|
elif key == "content" and isinstance(value, list):
|
|
184
167
|
for part in value:
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
168
|
+
t = part.get("type")
|
|
169
|
+
if t == "text":
|
|
170
|
+
tv = part.get("text")
|
|
171
|
+
if tv:
|
|
172
|
+
num += f(tv)
|
|
173
|
+
num += 3
|
|
189
174
|
return num
|
|
190
175
|
|
|
191
176
|
@staticmethod
|
|
@@ -193,19 +178,13 @@ class Tokens:
|
|
|
193
178
|
messages: List,
|
|
194
179
|
model: str = "gpt-4"
|
|
195
180
|
) -> int:
|
|
196
|
-
"""
|
|
197
|
-
Return number of tokens from prompt
|
|
198
|
-
|
|
199
|
-
:param messages: messages
|
|
200
|
-
:param model: model name
|
|
201
|
-
:return: number of tokens
|
|
202
|
-
"""
|
|
203
181
|
model, per_message, per_name = Tokens.get_config(model)
|
|
204
182
|
num = 0
|
|
183
|
+
f = Tokens.from_str
|
|
205
184
|
for message in messages:
|
|
206
185
|
num += per_message
|
|
207
|
-
num +=
|
|
208
|
-
num += 3
|
|
186
|
+
num += f(message.content)
|
|
187
|
+
num += 3
|
|
209
188
|
return num
|
|
210
189
|
|
|
211
190
|
@staticmethod
|
|
@@ -214,21 +193,14 @@ class Tokens:
|
|
|
214
193
|
messages: List[ChatMessageLlama],
|
|
215
194
|
model: str = "gpt-4"
|
|
216
195
|
) -> int:
|
|
217
|
-
"""
|
|
218
|
-
Return number of tokens from prompt
|
|
219
|
-
|
|
220
|
-
:param query: query
|
|
221
|
-
:param messages: messages
|
|
222
|
-
:param model: model name
|
|
223
|
-
:return: number of tokens
|
|
224
|
-
"""
|
|
225
196
|
model, per_message, per_name = Tokens.get_config(model)
|
|
226
197
|
num = 0
|
|
227
|
-
|
|
198
|
+
f = Tokens.from_str
|
|
199
|
+
num += f(query)
|
|
228
200
|
for message in messages:
|
|
229
201
|
num += per_message
|
|
230
|
-
num +=
|
|
231
|
-
num += 3
|
|
202
|
+
num += f(message.content)
|
|
203
|
+
num += 3
|
|
232
204
|
return num
|
|
233
205
|
|
|
234
206
|
@staticmethod
|
|
@@ -237,94 +209,56 @@ class Tokens:
|
|
|
237
209
|
mode: str = MODE_CHAT,
|
|
238
210
|
model: str = "gpt-4"
|
|
239
211
|
) -> int:
|
|
240
|
-
"""
|
|
241
|
-
Return number of tokens from context ctx
|
|
242
|
-
|
|
243
|
-
:param ctx: CtxItem
|
|
244
|
-
:param mode: mode
|
|
245
|
-
:param model: model ID
|
|
246
|
-
:return: number of tokens
|
|
247
|
-
"""
|
|
248
212
|
model, per_message, per_name = Tokens.get_config(model)
|
|
249
213
|
num = 0
|
|
250
|
-
|
|
214
|
+
f = Tokens.from_str
|
|
251
215
|
if mode in CHAT_MODES:
|
|
252
|
-
# input message
|
|
253
216
|
try:
|
|
254
|
-
num +=
|
|
217
|
+
num += f(str(ctx.final_input), model)
|
|
255
218
|
except Exception as e:
|
|
256
219
|
print("Tokens calc exception", e)
|
|
257
|
-
|
|
258
|
-
# output message
|
|
259
220
|
try:
|
|
260
|
-
num +=
|
|
221
|
+
num += f(str(ctx.final_output), model)
|
|
261
222
|
except Exception as e:
|
|
262
223
|
print("Tokens calc exception", e)
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
num += per_message * 2 # input + output
|
|
266
|
-
num += per_name * 2 # input + output
|
|
224
|
+
num += per_message * 2
|
|
225
|
+
num += per_name * 2
|
|
267
226
|
try:
|
|
268
|
-
num += Tokens.
|
|
227
|
+
num += Tokens._const_tokens("system", model) * 2
|
|
269
228
|
except Exception as e:
|
|
270
229
|
print("Tokens calc exception", e)
|
|
271
|
-
|
|
272
|
-
# input name
|
|
273
|
-
if ctx.input_name is not None and ctx.input_name != "":
|
|
274
|
-
name = ctx.input_name
|
|
275
|
-
else:
|
|
276
|
-
name = "user"
|
|
230
|
+
name = ctx.input_name if ctx.input_name else "user"
|
|
277
231
|
try:
|
|
278
|
-
num +=
|
|
232
|
+
num += f(name, model)
|
|
279
233
|
except Exception as e:
|
|
280
234
|
print("Tokens calc exception", e)
|
|
281
|
-
|
|
282
|
-
# output name
|
|
283
|
-
if ctx.output_name is not None and ctx.output_name != "":
|
|
284
|
-
name = ctx.output_name
|
|
285
|
-
else:
|
|
286
|
-
name = "assistant"
|
|
235
|
+
name = ctx.output_name if ctx.output_name else "assistant"
|
|
287
236
|
try:
|
|
288
|
-
num +=
|
|
237
|
+
num += f(name, model)
|
|
289
238
|
except Exception as e:
|
|
290
239
|
print("Tokens calc exception", e)
|
|
291
|
-
|
|
292
|
-
# build tmp message if completion mode
|
|
293
240
|
elif mode == MODE_COMPLETION:
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
if ctx.final_input is not None and ctx.final_input != "":
|
|
301
|
-
message += "\n" + ctx.input_name + ": " + ctx.final_input
|
|
302
|
-
if ctx.final_output is not None and ctx.final_output != "":
|
|
303
|
-
message += "\n" + ctx.output_name + ": " + ctx.final_output
|
|
304
|
-
# if without names
|
|
241
|
+
parts = []
|
|
242
|
+
if ctx.input_name and ctx.output_name:
|
|
243
|
+
if ctx.final_input:
|
|
244
|
+
parts.append("\n" + ctx.input_name + ": " + ctx.final_input)
|
|
245
|
+
if ctx.final_output:
|
|
246
|
+
parts.append("\n" + ctx.output_name + ": " + ctx.final_output)
|
|
305
247
|
else:
|
|
306
|
-
if ctx.final_input
|
|
307
|
-
|
|
308
|
-
if ctx.final_output
|
|
309
|
-
|
|
248
|
+
if ctx.final_input:
|
|
249
|
+
parts.append("\n" + ctx.final_input)
|
|
250
|
+
if ctx.final_output:
|
|
251
|
+
parts.append("\n" + ctx.final_output)
|
|
310
252
|
try:
|
|
311
|
-
num +=
|
|
253
|
+
num += f("".join(parts), model)
|
|
312
254
|
except Exception as e:
|
|
313
255
|
print("Tokens calc exception", e)
|
|
314
|
-
|
|
315
256
|
return num
|
|
316
257
|
|
|
317
258
|
def get_current(
|
|
318
259
|
self,
|
|
319
260
|
input_prompt: str
|
|
320
261
|
) -> Tuple[int, int, int, int, int, int, int, int, int]:
|
|
321
|
-
"""
|
|
322
|
-
Return current number of used tokens
|
|
323
|
-
|
|
324
|
-
:param input_prompt: input prompt
|
|
325
|
-
:return: A tuple of (input_tokens, system_tokens, extra_tokens, ctx_tokens, ctx_len, ctx_len_all, \
|
|
326
|
-
sum_tokens, max_current, threshold)
|
|
327
|
-
"""
|
|
328
262
|
model = self.window.core.config.get('model')
|
|
329
263
|
model_id = ""
|
|
330
264
|
model_data = self.window.core.models.get(model)
|
|
@@ -341,64 +275,45 @@ class Tokens:
|
|
|
341
275
|
extra_tokens = self.get_extra(model)
|
|
342
276
|
|
|
343
277
|
if mode in CHAT_MODES:
|
|
344
|
-
# system prompt (without extra tokens)
|
|
345
278
|
system_prompt = str(self.window.core.config.get('prompt')).strip()
|
|
346
|
-
system_prompt = self.window.core.prompt.build_final_system_prompt(system_prompt, mode, model_data)
|
|
347
|
-
|
|
348
|
-
if system_prompt is not None and system_prompt != "":
|
|
279
|
+
system_prompt = self.window.core.prompt.build_final_system_prompt(system_prompt, mode, model_data)
|
|
280
|
+
if system_prompt:
|
|
349
281
|
system_tokens = self.from_prompt(system_prompt, "", model_id)
|
|
350
|
-
system_tokens +=
|
|
351
|
-
|
|
352
|
-
# input prompt
|
|
353
|
-
if input_prompt is not None and input_prompt != "":
|
|
282
|
+
system_tokens += Tokens._const_tokens("system", model_id)
|
|
283
|
+
if input_prompt:
|
|
354
284
|
input_tokens = self.from_prompt(input_prompt, "", model_id)
|
|
355
|
-
input_tokens +=
|
|
285
|
+
input_tokens += Tokens._const_tokens("user", model_id)
|
|
356
286
|
elif mode == MODE_COMPLETION:
|
|
357
|
-
# system prompt (without extra tokens)
|
|
358
287
|
system_prompt = str(self.window.core.config.get('prompt')).strip()
|
|
359
|
-
system_prompt = self.window.core.prompt.build_final_system_prompt(system_prompt, mode, model_data)
|
|
288
|
+
system_prompt = self.window.core.prompt.build_final_system_prompt(system_prompt, mode, model_data)
|
|
360
289
|
system_tokens = self.from_text(system_prompt, model_id)
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
message = ""
|
|
365
|
-
if user_name is not None \
|
|
366
|
-
and ai_name is not None \
|
|
367
|
-
and user_name != "" \
|
|
368
|
-
and ai_name != "":
|
|
369
|
-
message += "\n" + user_name + ": " + str(input_prompt)
|
|
370
|
-
message += "\n" + ai_name + ":"
|
|
290
|
+
if input_prompt:
|
|
291
|
+
if user_name and ai_name:
|
|
292
|
+
message = "\n" + user_name + ": " + str(input_prompt) + "\n" + ai_name + ":"
|
|
371
293
|
else:
|
|
372
|
-
message
|
|
294
|
+
message = "\n" + str(input_prompt)
|
|
373
295
|
input_tokens = self.from_text(message, model_id)
|
|
374
|
-
extra_tokens = 0
|
|
296
|
+
extra_tokens = 0
|
|
375
297
|
|
|
376
|
-
# TMP system prompt (for debug purposes)
|
|
377
298
|
self.window.core.ctx.current_sys_prompt = system_prompt
|
|
378
299
|
|
|
379
|
-
# used tokens
|
|
380
300
|
used_tokens = system_tokens + input_tokens
|
|
381
301
|
|
|
382
|
-
# check model max allowed ctx tokens
|
|
383
302
|
max_current = max_total_tokens
|
|
384
303
|
model_ctx = self.window.core.models.get_num_ctx(model)
|
|
385
304
|
if max_current > model_ctx:
|
|
386
305
|
max_current = model_ctx
|
|
387
306
|
|
|
388
|
-
# context threshold (reserved for output)
|
|
389
307
|
threshold = self.window.core.config.get('context_threshold')
|
|
390
308
|
max_to_check = max_current - threshold
|
|
391
309
|
|
|
392
|
-
# context tokens
|
|
393
310
|
ctx_len_all = self.window.core.ctx.count_items()
|
|
394
311
|
ctx_len, ctx_tokens = self.window.core.ctx.count_prompt_items(model_id, mode, used_tokens, max_to_check)
|
|
395
312
|
|
|
396
|
-
# empty ctx tokens if context is not used
|
|
397
313
|
if not self.window.core.config.get('use_context'):
|
|
398
314
|
ctx_tokens = 0
|
|
399
315
|
ctx_len = 0
|
|
400
316
|
|
|
401
|
-
# sum of input tokens
|
|
402
317
|
sum_tokens = system_tokens + input_tokens + ctx_tokens + extra_tokens
|
|
403
318
|
|
|
404
319
|
return input_tokens, system_tokens, extra_tokens, ctx_tokens, ctx_len, ctx_len_all, \
|
|
@@ -409,39 +324,25 @@ class Tokens:
|
|
|
409
324
|
system_prompt: str,
|
|
410
325
|
input_prompt: str
|
|
411
326
|
) -> int:
|
|
412
|
-
"""
|
|
413
|
-
Count per-user used tokens
|
|
414
|
-
|
|
415
|
-
:param system_prompt: system prompt
|
|
416
|
-
:param input_prompt: input prompt
|
|
417
|
-
:return: used tokens
|
|
418
|
-
"""
|
|
419
327
|
model = self.window.core.config.get('model')
|
|
420
328
|
model_id = self.window.core.models.get_id(model)
|
|
421
329
|
mode = self.window.core.config.get('mode')
|
|
422
330
|
tokens = 0
|
|
423
331
|
if mode in [MODE_CHAT, MODE_VISION, MODE_AUDIO, MODE_RESEARCH]:
|
|
424
|
-
tokens += self.from_prompt(system_prompt, "", model_id)
|
|
332
|
+
tokens += self.from_prompt(system_prompt, "", model_id)
|
|
425
333
|
tokens += self.from_text("system", model_id)
|
|
426
|
-
tokens += self.from_prompt(input_prompt, "", model_id)
|
|
334
|
+
tokens += self.from_prompt(input_prompt, "", model_id)
|
|
427
335
|
tokens += self.from_text("user", model_id)
|
|
428
336
|
else:
|
|
429
|
-
|
|
430
|
-
tokens += self.from_text(
|
|
431
|
-
|
|
432
|
-
tokens += self.
|
|
433
|
-
tokens += self.get_extra(model_id) # extra tokens (required for output)
|
|
337
|
+
tokens += self.from_text(system_prompt, model_id)
|
|
338
|
+
tokens += self.from_text(input_prompt, model_id)
|
|
339
|
+
tokens += self.window.core.config.get('context_threshold')
|
|
340
|
+
tokens += self.get_extra(model_id)
|
|
434
341
|
return tokens
|
|
435
342
|
|
|
436
343
|
@staticmethod
|
|
437
344
|
def get_config(model: str) -> Tuple[str, int, int]:
|
|
438
|
-
|
|
439
|
-
Return tokens config values
|
|
440
|
-
|
|
441
|
-
:param model: model ID
|
|
442
|
-
:return: model, per_message, per_name
|
|
443
|
-
"""
|
|
444
|
-
per_message = 4 # message follows <|start|>{role/name}\n{content}<|end|>\n
|
|
345
|
+
per_message = 4
|
|
445
346
|
per_name = -1
|
|
446
347
|
|
|
447
348
|
if model is not None:
|
|
@@ -456,8 +357,8 @@ class Tokens:
|
|
|
456
357
|
per_message = 3
|
|
457
358
|
per_name = 1
|
|
458
359
|
elif model == "gpt-3.5-turbo-0301":
|
|
459
|
-
per_message = 4
|
|
460
|
-
per_name = -1
|
|
360
|
+
per_message = 4
|
|
361
|
+
per_name = -1
|
|
461
362
|
elif "gpt-3.5-turbo" in model:
|
|
462
363
|
return Tokens.get_config(model="gpt-3.5-turbo-0613")
|
|
463
364
|
elif "gpt-4" in model:
|
|
@@ -466,4 +367,4 @@ class Tokens:
|
|
|
466
367
|
per_message = 1
|
|
467
368
|
per_name = 0
|
|
468
369
|
|
|
469
|
-
return model, per_message, per_name
|
|
370
|
+
return model, per_message, per_name
|