import time from tqdm.auto import tqdm from rag_helper import RAGBase def calc_price(usage): input_price_per_million = 0.75 output_price_per_million = 4.50 input_cost = (usage.input_tokens / 1_000_000) * input_price_per_million output_cost = (usage.output_tokens / 1_000_000) * output_price_per_million total_cost = input_cost + output_cost return { "input_cost": input_cost, "output_cost": output_cost, "total_cost": total_cost, } def calc_total_price(usages): total_cost = 0.0 for usage in usages: cost = calc_price(usage) total_cost = total_cost + cost["total_cost"] return total_cost def llm_structured(client, instructions, user_prompt, output_type, model="gpt-5.4-mini"): messages = [ {"role": "developer", "content": instructions}, {"role": "user", "content": user_prompt} ] response = client.responses.parse( model=model, input=messages, text_format=output_type ) return response.output_parsed, response.usage def llm_structured_retry( client, instructions, user_prompt, output_type, model="gpt-5.4-mini", max_retries=3, ): for attempt in range(max_retries): try: return llm_structured( client, instructions, user_prompt, output_type, model=model, ) except Exception: if attempt == max_retries - 1: raise time.sleep(2 ** attempt) class RAGWithUsage(RAGBase): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.usages = [] self.last_usage = None def reset_usage(self): self.usages = [] self.last_usage = None def search(self, query, num_results=5): boost_dict = {"question": 1.0, "answer": 2.0, "section": 0.1} filter_dict = {"course": self.course} return self.index.search( query, num_results=num_results, boost_dict=boost_dict, filter_dict=filter_dict ) def llm(self, prompt): input_messages = [ {"role": "developer", "content": self.instructions}, {"role": "user", "content": prompt} ] response = self.llm_client.responses.create( model=self.model, input=input_messages ) self.last_usage = response.usage self.usages.append(response.usage) return response.output_text def total_cost(self): return calc_total_price(self.usages) def map_progress(pool, seq, f): results = [] with tqdm(total=len(seq)) as progress: futures = [] for el in seq: future = pool.submit(f, el) future.add_done_callback(lambda p: progress.update()) futures.append(future) for future in futures: result = future.result() results.append(result) return results