Skip to content

Commit 2249247

Browse files
committed
fix token estimation and address counts
1 parent ca9cedb commit 2249247

File tree

1 file changed

+30
-14
lines changed

1 file changed

+30
-14
lines changed

app/backend/approaches/chatreadretrieveread.py

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -95,15 +95,15 @@ def run(self, history: Sequence[dict[str, str]], overrides: dict[str, Any]) -> A
9595
messages = self.get_messages_from_history(prompt_override=prompt_override, follow_up_questions_prompt=follow_up_questions_prompt,history=history, sources=content)
9696

9797
# 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(
9999
deployment_id=self.chatgpt_deployment,
100100
model=self.chatgpt_model,
101101
messages=messages,
102102
temperature=overrides.get("temperature") or 0.7,
103103
max_tokens=1024,
104104
n=1)
105105

106-
chatContent = chatCompletion.choices[0].message.content
106+
chat_content = chat_completion.choices[0].message.content
107107

108108
return {"data_points": results, "answer": chat_content, "thoughts": f"Searched for:<br>{q}<br><br>Prompt:<br>" + prompt.replace('\n', '<br>')}
109109

@@ -127,37 +127,53 @@ def get_messages_from_history(self, prompt_override, follow_up_questions_prompt,
127127
system_message = prompt_override.format(follow_up_questions_prompt=follow_up_questions_prompt)
128128

129129
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)
131131

132132
#latest conversation
133-
userContent = history[-1]["user"] + " \nSources:" + sources
133+
user_content = history[-1]["user"] + " \nSources:" + sources
134134
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)
136136

137137
'''
138138
Enqueue in reverse order
139139
if limit exceeds truncate old messages
140140
leaving system message behind
141+
Keep track of token count for each conversation
142+
If token count exceeds limit, break
141143
'''
142144
for h in reversed(history[:-1]):
143145
if h.get("bot"):
144146
messages.insert(1, {"role": self.ASSISTANT, "content" : h.get("bot")})
147+
token_count += self.num_tokens_from_messages(messages[1], self.chatgpt_model)
145148
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)
147150
if token_count > approx_max_tokens*4:
148151
break
149-
150152
return messages
151153

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):
153170
encoding = tiktoken.encoding_for_model(self.get_oai_chatmodel_tiktok(model))
154171
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
161177
return num_tokens
162178

163179
def get_oai_chatmodel_tiktok(self, aoaimodel: str):

0 commit comments

Comments
 (0)