import gradio as gr import os from langchain_openai import ChatOpenAI from langchain.agents import AgentExecutor, create_tool_calling_agent from langchain_community.tools.tavily_search import TavilySearchResults from langchain_core.prompts import ChatPromptTemplate from datetime import datetime, timedelta # Set up API keys OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") TAVILY_API_KEY = os.getenv("TAVILY_API_KEY") os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY os.environ["TAVILY_API_KEY"] = TAVILY_API_KEY # Initialize the LLM llm = ChatOpenAI(model="gpt-4o-mini") # Set Up the Tavily Search Tool tools = [TavilySearchResults(max_results=3)] # CEFR levels and their descriptions CEFR_LEVELS = { "Pre-A1": "Foundation", "A1": "Elementary", "A2": "Pre-intermediate", "B1": "Intermediate", "B2": "Upper Intermediate", "C1": "Advanced", "C2": "Proficiency" } # Create a Chat Prompt Template prompt = ChatPromptTemplate.from_messages([ ("system", "You are a helpful assistant for ESL learners. Use the tavily_search_results_json tool to find current news information about the students' interests."), ("human", "{input}"), ("placeholder", "{agent_scratchpad}"), ]) # Construct the Tools agent agent = create_tool_calling_agent(llm, tools, prompt) # Create an agent executor agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True) def get_news_content(topic): current_date = datetime.now().strftime("%Y-%m-%d") week_ago = (datetime.now() - timedelta(days=7)).strftime("%Y-%m-%d") query = f"Summarize the latest news about {topic} from {week_ago} to {current_date}" result = agent_executor.invoke({"input": query}) return result['output'], result.get('intermediate_steps', []) def generate_article(topic, content, urls, level, learning_objective=None): objective_text = f"Focus on the following learning objective: {learning_objective}. " if learning_objective else "" prompt = f""" Write a news article about {topic} suitable for a {level} ({CEFR_LEVELS[level]}) English language learner. {objective_text} Select from the following content to write an interesting article: {content} The article should be engaging, informative, and appropriate for the specified language level. Include a 'Sources' section at the end of the article with the following URLs: {urls} """ result = agent_executor.invoke({"input": prompt}) return result['output'] def extract_language_elements(article, level, learning_objective=None): objective_text = f"Additionally, focus on the following learning objective: {learning_objective}. " if learning_objective else "" prompt = f""" Analyze the following article for a {level} ({CEFR_LEVELS[level]}) English language learner: Extract and categorize the following language elements: 1. Vocabulary: Important or challenging words with their definitions 2. Grammar: Key grammar structures or patterns used in the article 3. Conjugation: Notable verb conjugations present in the article 4. Expressions: Idiomatic expressions or phrasal verbs 5. Sentence structures: Complex or notable sentence structures Ensure that the extracted elements are appropriate and relevant for a {level} learner. {objective_text} Return the results in a structured format. Article: {article} """ result = agent_executor.invoke({"input": prompt}) return result['output'] def generate_feedback_and_takeaway(article, level, language_elements, learning_objective=None): objective_text = f"Additionally, focus on the following learning objective: {learning_objective}. " if learning_objective else "" prompt = f""" Based on the following article for a {level} ({CEFR_LEVELS[level]}) English language learner and the extracted language elements: Create a takeaway section about key language learning points including: - A list of important vocabulary words with their definitions - A list of important grammar rules or patterns used in the article - A list of notable conjugations - A list of idiomatic expressions or phrasal verbs - Examples of complex or notable sentence structures Use the provided language elements as a basis, but feel free to expand or adjust as needed. {objective_text} Article: {article} Language Elements: {language_elements} """ result = agent_executor.invoke({"input": prompt}) return result['output'] def generate_response(user_input, context, level, learning_objective=None): objective_text = f"Additionally, focus on the following learning objective: {learning_objective}. " if learning_objective else "" prompt = f""" #Role You are an ESL teacher You answer students' questions about an article, helping them understand it, with a focus on language elements. You also ask the student questions aligned with relevant language elemnts to check their understanding. #Instructions 1. If the student asks a question: Based on the following article for a {level} ({CEFR_LEVELS[level]}) English language learner, respond to the user's input in a helpful manner. 2. When the user is ready, ask relevant questions to check for their understanding of the article, but also to practice the relevant language elements. Ensure questions are challenging but appropriate for the {level} level. Avoid questions with obvious answers. Types of questions should include: - Multiple-choice questions - True/false questions - Fill-in-the-blank questions - Questions to apply the elements of language learned in new contexts - Open-ended questions Questions should become increasingly difficult as the student succeeds, and easier if they struggle. When the student fails, provide a hint. If they fail again, provide the answer and an explanation. {objective_text} Article: {context['article']} Language Elements: {context['language_elements']} User Input: {user_input} """ result = agent_executor.invoke({"input": prompt}) return result['output'] def main(topic, level, learning_objective=None): try: news_content, intermediate_steps = get_news_content(topic) if news_content: urls = [step[1].get('url', '') for step in intermediate_steps if isinstance(step[1], dict)] article = generate_article(topic, news_content, urls, level, learning_objective) language_elements = extract_language_elements(article, level, learning_objective) feedback_and_takeaway = generate_feedback_and_takeaway(article, level, language_elements, learning_objective) context = { 'article': article, 'language_elements': language_elements, 'feedback_and_takeaway': feedback_and_takeaway } return article, feedback_and_takeaway, context else: return "No relevant news content found. Please try a different topic.", "", None except Exception as e: return f"An error occurred: {str(e)}", "", None def chat(user_input, context): response = generate_response(user_input, context) return response # Gradio Interface with gr.Blocks() as demo: topic_input = gr.Textbox(label="Enter your topic of interest") level_input = gr.Dropdown(label="Select your English proficiency level", choices=list(CEFR_LEVELS.keys())) objective_input = gr.Textbox(label="Enter a specific learning objective (optional)") generate_btn = gr.Button("Generate Article") article_output = gr.Markdown() feedback_output = gr.Markdown() chat_input = gr.Textbox(label="Ask questions about the article or English language here") chat_output = gr.Chatbot() context = gr.State() def on_generate(topic, level, learning_objective): article, feedback, ctx = main(topic, level, learning_objective) context.set(ctx) return article, feedback generate_btn.click(on_generate, inputs=[topic_input, level_input, objective_input], outputs=[article_output, feedback_output]) def on_chat(user_input): if context.get() is not None: response = chat(user_input, context.get()) return response else: return "Please generate an article first." chat_input.submit(on_chat, inputs=[chat_input], outputs=[chat_output]) demo.launch()