import os from openai import OpenAI from utils.errors import APIError class PromptManager: def __init__(self, prompts): self.prompts = prompts self.limit = os.getenv("DEMO_WORD_LIMIT") def add_limit(self, prompt): if self.limit: prompt += f" Keep your responses very short and simple, no more than {self.limit} words." return prompt def get_system_prompt(self, key): prompt = self.prompts[key] return self.add_limit(prompt) def get_problem_requirements_prompt(self, type, difficulty=None, topic=None, requirements=None): prompt = f"Create a {type} problem. Difficulty: {difficulty}. Topic: {topic} " f"Additional requirements: {requirements}. " return self.add_limit(prompt) class LLMManager: def __init__(self, config, prompts): self.config = config self.client = OpenAI(base_url=config.llm.url, api_key=config.llm.key) self.prompt_manager = PromptManager(prompts) self.status = self.test_llm() if self.status: self.streaming = self.test_llm_stream() else: self.streaming = False if self.streaming: self.end_interview = self.end_interview_stream self.get_problem = self.get_problem_stream self.send_request = self.send_request_stream else: self.end_interview = self.end_interview_full self.get_problem = self.get_problem_full self.send_request = self.send_request_full def text_processor(self): def ans_full(response): return response def ans_stream(response): yield from response if self.streaming: return ans_full else: return ans_stream def get_text(self, messages): try: response = self.client.chat.completions.create(model=self.config.llm.name, messages=messages, temperature=1) if not response.choices: raise APIError("LLM Get Text Error", details="No choices in response") return response.choices[0].message.content.strip() except Exception as e: raise APIError(f"LLM Get Text Error: Unexpected error: {e}") def get_text_stream(self, messages): try: response = self.client.chat.completions.create( model=self.config.llm.name, messages=messages, temperature=1, stream=True, ) except Exception as e: raise APIError(f"LLM End Interview Error: Unexpected error: {e}") text = "" for chunk in response: if chunk.choices[0].delta.content: text += chunk.choices[0].delta.content yield text test_messages = [ {"role": "system", "content": "You just help me test the connection."}, {"role": "user", "content": "Hi!"}, {"role": "user", "content": "Ping!"}, ] def test_llm(self): try: self.get_text(self.test_messages) return True except: return False def test_llm_stream(self): try: for _ in self.get_text_stream(self.test_messages): pass return True except: return False def init_bot(self, problem, interview_type="coding"): system_prompt = self.prompt_manager.get_system_prompt(f"{interview_type}_interviewer_prompt") return [ {"role": "system", "content": system_prompt + f"\nThe candidate is solving the following problem:\n {problem}"}, ] def get_problem_prepare_messages(self, requirements, difficulty, topic, interview_type): system_prompt = self.prompt_manager.get_system_prompt(f"{interview_type}_problem_generation_prompt") full_prompt = self.prompt_manager.get_problem_requirements_prompt(interview_type, difficulty, topic, requirements) messages = [ {"role": "system", "content": system_prompt}, {"role": "user", "content": full_prompt}, ] return messages def get_problem_full(self, requirements, difficulty, topic, interview_type="coding"): messages = self.get_problem_prepare_messages(requirements, difficulty, topic, interview_type) return self.get_text(messages) def get_problem_stream(self, requirements, difficulty, topic, interview_type="coding"): messages = self.get_problem_prepare_messages(requirements, difficulty, topic, interview_type) yield from self.get_text_stream(messages) def update_chat_history(self, code, previous_code, chat_history, chat_display): message = chat_display[-1][0] if code != previous_code: chat_history.append({"role": "user", "content": f"My latest solution:\n{code}"}) chat_history.append({"role": "user", "content": message}) return chat_history def send_request_full(self, code, previous_code, chat_history, chat_display): chat_history = self.update_chat_history(code, previous_code, chat_history, chat_display) reply = self.get_text(chat_history) chat_display.append([None, reply.split("#NOTES#")[0].strip()]) chat_history.append({"role": "assistant", "content": reply}) return chat_history, chat_display, code def send_request_stream(self, code, previous_code, chat_history, chat_display): chat_history = self.update_chat_history(code, previous_code, chat_history, chat_display) chat_display.append([None, ""]) chat_history.append({"role": "assistant", "content": ""}) reply = self.get_text_stream(chat_history) for message in reply: chat_display[-1][1] = message.split("#NOTES#")[0].strip() chat_history[-1]["content"] = message yield chat_history, chat_display, code def end_interview_prepare_messages(self, problem_description, chat_history, interview_type): transcript = [f"{message['role'].capitalize()}: {message['content']}" for message in chat_history[1:]] system_prompt = self.prompt_manager.get_system_prompt(f"{interview_type}_grading_feedback_prompt") messages = [ {"role": "system", "content": system_prompt}, {"role": "user", "content": f"The original problem to solve: {problem_description}"}, {"role": "user", "content": "\n\n".join(transcript)}, {"role": "user", "content": "Grade the interview based on the transcript provided and give feedback."}, ] return messages def end_interview_full(self, problem_description, chat_history, interview_type="coding"): if len(chat_history) <= 2: return "No interview history available" else: messages = self.end_interview_prepare_messages(problem_description, chat_history, interview_type) return self.get_text(messages) def end_interview_stream(self, problem_description, chat_history, interview_type="coding"): if len(chat_history) <= 2: yield "No interview history available" else: messages = self.end_interview_prepare_messages(problem_description, chat_history, interview_type) yield from self.get_text_stream(messages)