@@ -95,15 +95,15 @@ def run(self, history: Sequence[dict[str, str]], overrides: dict[str, Any]) -> A
95
95
messages = self .get_messages_from_history (prompt_override = prompt_override , follow_up_questions_prompt = follow_up_questions_prompt ,history = history , sources = content )
96
96
97
97
# STEP 3: Generate a contextual and content specific answer using the search results and chat history
98
- chatCompletion = openai .ChatCompletion .create (
98
+ chat_completion = openai .ChatCompletion .create (
99
99
deployment_id = self .chatgpt_deployment ,
100
100
model = self .chatgpt_model ,
101
101
messages = messages ,
102
102
temperature = overrides .get ("temperature" ) or 0.7 ,
103
103
max_tokens = 1024 ,
104
104
n = 1 )
105
105
106
- chatContent = chatCompletion .choices [0 ].message .content
106
+ chat_content = chat_completion .choices [0 ].message .content
107
107
108
108
return {"data_points" : results , "answer" : chat_content , "thoughts" : f"Searched for:<br>{ q } <br><br>Prompt:<br>" + prompt .replace ('\n ' , '<br>' )}
109
109
@@ -127,37 +127,53 @@ def get_messages_from_history(self, prompt_override, follow_up_questions_prompt,
127
127
system_message = prompt_override .format (follow_up_questions_prompt = follow_up_questions_prompt )
128
128
129
129
messages .append ({"role" :self .SYSTEM , "content" : system_message })
130
- token_count += self .num_tokens_from_messages (messages , self .chatgpt_model )
130
+ token_count += self .num_tokens_from_messages (messages [ - 1 ] , self .chatgpt_model )
131
131
132
132
#latest conversation
133
- userContent = history [- 1 ]["user" ] + " \n Sources:" + sources
133
+ user_content = history [- 1 ]["user" ] + " \n Sources:" + sources
134
134
messages .append ({"role" : self .USER , "content" : user_content })
135
- token_count += self .num_tokens_from_messages (messages , self .chatgpt_model )
135
+ token_count += token_count + self .num_tokens_from_messages (messages [ - 1 ] , self .chatgpt_model )
136
136
137
137
'''
138
138
Enqueue in reverse order
139
139
if limit exceeds truncate old messages
140
140
leaving system message behind
141
+ Keep track of token count for each conversation
142
+ If token count exceeds limit, break
141
143
'''
142
144
for h in reversed (history [:- 1 ]):
143
145
if h .get ("bot" ):
144
146
messages .insert (1 , {"role" : self .ASSISTANT , "content" : h .get ("bot" )})
147
+ token_count += self .num_tokens_from_messages (messages [1 ], self .chatgpt_model )
145
148
messages .insert (1 , {"role" : self .USER , "content" : h .get ("user" )})
146
- token_count = token_count + self .num_tokens_from_messages (messages , self .chatgpt_model )
149
+ token_count += self .num_tokens_from_messages (messages [ 1 ] , self .chatgpt_model )
147
150
if token_count > approx_max_tokens * 4 :
148
151
break
149
-
150
152
return messages
151
153
152
- def num_tokens_from_messages (self , messages , model : str ):
154
+ '''
155
+ Source: https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb
156
+ Adapted: https://learn.microsoft.com/en-us/azure/cognitive-services/openai/how-to/chatgpt?pivots=programming-language-chat-completions#managing-conversations
157
+
158
+ Method takes in a single conversation and calculate prompt tokens
159
+ for chat api
160
+
161
+ Keys role and content are accounted seperately.
162
+
163
+ Values of content are encoded by model type and calculated the length.
164
+
165
+ This gives close proximity of token length measurement used in gpt models
166
+
167
+ message = {"role":"assistant", "content":"how can I assist you?"}
168
+ '''
169
+ def num_tokens_from_messages (self , message : any , model : str ):
153
170
encoding = tiktoken .encoding_for_model (self .get_oai_chatmodel_tiktok (model ))
154
171
num_tokens = 0
155
- for message in messages :
156
- num_tokens += 2 # every message follows {role/name}\n{content}\n
157
- for key , value in message .items ():
158
- num_tokens += len (encoding .encode (value ))
159
- if key == "name" : # if there's a name, the role is omitted
160
- num_tokens += - 1 # role is always required and always 1 token
172
+ num_tokens += 2 # every message follows {role/name}\n{content}\n
173
+ for key , value in message .items ():
174
+ num_tokens += len (encoding .encode (value ))
175
+ if key == "name" : # if there's a name, the role is omitted
176
+ num_tokens += - 1 # role is always required and always 1 token
161
177
return num_tokens
162
178
163
179
def get_oai_chatmodel_tiktok (self , aoaimodel : str ):
0 commit comments