| |
| """ |
| AI Debate Platform - Gradio version for Hugging Face Spaces |
| Full MetaGPT integration |
| """ |
|
|
| import gradio as gr |
| import asyncio |
| import os |
| import sys |
|
|
| |
| sys.path.insert(0, os.path.dirname(__file__)) |
|
|
| |
| os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY", "") |
|
|
| |
| try: |
| from metagpt.actions import Action |
| from metagpt.roles import Role |
| from metagpt.schema import Message |
| from research_actions import CollectLinks, WebBrowseAndSummarize, ConductResearch |
| from metagpt.roles.role import RoleReactMode |
| import openai |
|
|
| openai.api_key = os.getenv("OPENAI_API_KEY") |
| METAGPT_AVAILABLE = True |
| except ImportError as e: |
| print(f"MetaGPT import failed: {e}") |
| METAGPT_AVAILABLE = False |
| |
| import openai |
| from duckduckgo_search import DDGS |
| import aiohttp |
| from bs4 import BeautifulSoup |
|
|
| openai.api_key = os.getenv("OPENAI_API_KEY") |
|
|
|
|
| |
| if METAGPT_AVAILABLE: |
| |
| class Researcher(Role): |
| name: str = "Researcher" |
| profile: str = "Research Assistant" |
|
|
| def __init__(self, **data): |
| super().__init__(**data) |
| self.set_actions([CollectLinks, WebBrowseAndSummarize, ConductResearch]) |
| self._set_react_mode(RoleReactMode.BY_ORDER.value, len(self.actions)) |
|
|
| async def research_topic(self, topic: str) -> str: |
| collect_action = CollectLinks() |
| links = await collect_action.run(topic, decomposition_nums=2, url_per_query=2) |
|
|
| browse_action = WebBrowseAndSummarize() |
| summaries = [] |
| url_count = 0 |
| for query, urls in links.items(): |
| if urls and url_count < 4: |
| remaining_urls = 4 - url_count |
| limited_urls = urls[:remaining_urls] |
| result = await browse_action.run(*limited_urls, query=query) |
| summaries.extend(result.values()) |
| url_count += len(limited_urls) |
|
|
| research_action = ConductResearch() |
| content = "\n---\n".join(summaries) |
| report = await research_action.run(topic, content) |
| return report |
|
|
| class Debater(Role): |
| name: str = "" |
| profile: str = "" |
| opponent_name1: str = "" |
| opponent_name2: str = "" |
| research_info: str = "" |
|
|
| def __init__(self, **data): |
| super().__init__(**data) |
| from metagpt.actions import UserRequirement |
| class SpeakAloud(Action): |
| name: str = "SpeakAloud" |
| async def run(self, context: str, name: str, opponent_name1: str, opponent_name2: str, |
| idea: str = "", profile: str = "", round_num: int = 1, research_info: str = "") -> str: |
| if round_num <= 3: |
| instruction = f"Round {round_num}/3: State your {profile} view with evidence" |
| else: |
| instruction = f"Round {round_num}: Defend and rebut with evidence" |
|
|
| prompt = f"""You are {name}, debating: {idea} |
| Previous: {context[-1000:]} |
| Research: {research_info[:800]} |
| {instruction} |
| Response:""" |
| rsp = await self._aask(prompt) |
| return rsp |
|
|
| self.set_actions([SpeakAloud]) |
| self._watch([UserRequirement, SpeakAloud]) |
|
|
| async def request_research(self, topic: str, researcher): |
| from metagpt.actions import Action |
| class RequestResearch(Action): |
| name: str = "RequestResearch" |
| async def run(self, name: str, profile: str, topic: str) -> str: |
| prompt = f"You are {name}, a {profile}. What would you research about: {topic}? (1 query)" |
| return await self._aask(prompt) |
|
|
| request_action = RequestResearch() |
| query = await request_action.run(name=self.name, profile=self.profile, topic=topic) |
| research_result = await researcher.research_topic(query) |
| self.research_info = f"Query: {query}\nResult: {research_result}" |
| return research_result |
|
|
| else: |
| |
| async def call_gpt(prompt: str) -> str: |
| try: |
| response = openai.ChatCompletion.create( |
| model="gpt-3.5-turbo", |
| messages=[{"role": "user", "content": prompt}], |
| temperature=0.7, |
| max_tokens=800 |
| ) |
| return response.choices[0].message.content |
| except Exception as e: |
| return f"Error: {str(e)}" |
|
|
| class Debater: |
| def __init__(self, name: str, profile: str, opponent1: str, opponent2: str): |
| self.name = name |
| self.profile = profile |
| self.research_info = "" |
|
|
| async def request_research(self, topic: str, researcher=None): |
| self.research_info = f"Basic research on: {topic}" |
| return self.research_info |
|
|
| async def speak(self, topic: str, context: str, round_num: int) -> str: |
| prompt = f"You are {self.name} ({self.profile}), debating: {topic}\nRound {round_num}.\nRespond (200 words):" |
| return await call_gpt(prompt) |
|
|
|
|
| async def run_debate(topic: str, n_rounds: int = 6): |
| """Run debate and return formatted results""" |
|
|
| if not os.getenv("OPENAI_API_KEY"): |
| return "β Error: OPENAI_API_KEY not configured", "", "" |
|
|
| try: |
| |
| if METAGPT_AVAILABLE: |
| researcher = Researcher() |
| principal = Debater(name="Principal", profile="School", opponent_name1="John", opponent_name2="Mom") |
| student = Debater(name="John", profile="Student", opponent_name1="Mom", opponent_name2="Principal") |
| parent = Debater(name="Mom", profile="Parent", opponent_name1="Principal", opponent_name2="John") |
| else: |
| principal = Debater("Principal", "School", "John", "Mom") |
| student = Debater("John", "Student", "Mom", "Principal") |
| parent = Debater("Mom", "Parent", "Principal", "John") |
| researcher = None |
|
|
| |
| research_text = "## π Research Phase\n\n" |
| for debater in [principal, student, parent]: |
| result = await debater.request_research(topic, researcher) |
| research_text += f"**{debater.name}**: {result[:200]}...\n\n" |
|
|
| |
| debate_text = "## π£οΈ Debate\n\n" |
| speakers = [principal, student, parent] |
| context = "" |
|
|
| for round_num in range(min(n_rounds, 6)): |
| current = speakers[round_num % 3] |
|
|
| if METAGPT_AVAILABLE: |
| msg = Message(content=topic, role="user", send_to={current.name}, sent_from="User") |
| response = await current.run(msg) |
| content = response.content |
| else: |
| content = await current.speak(topic, context, round_num + 1) |
|
|
| debate_text += f"### Round {round_num + 1} - {current.name}\n{content}\n\n---\n\n" |
| context += f"\n{current.name}: {content}" |
|
|
| |
| eval_prompt = f"Summarize this debate and provide recommendations:\n\n{context[-2000:]}" |
|
|
| if METAGPT_AVAILABLE: |
| from metagpt.actions import Action |
| class EvaluateDebate(Action): |
| name: str = "EvaluateDebate" |
| async def run(self, topic: str, content: str) -> str: |
| prompt = f"Evaluate debate on '{topic}':\n{content}\nProvide summary and recommendations:" |
| return await self._aask(prompt) |
|
|
| evaluator = EvaluateDebate() |
| evaluation = await evaluator.run(topic, context) |
| else: |
| evaluation = await call_gpt(eval_prompt) |
|
|
| eval_text = f"## π Evaluation\n\n{evaluation}" |
|
|
| return research_text, debate_text, eval_text |
|
|
| except Exception as e: |
| return f"β Error: {str(e)}", "", "" |
|
|
|
|
| def debate_interface(topic: str, rounds: int): |
| """Gradio interface wrapper""" |
| if not topic: |
| return "β οΈ Please enter a debate topic", "", "" |
|
|
| try: |
| research, debate, evaluation = asyncio.run(run_debate(topic, rounds)) |
| return research, debate, evaluation |
| except Exception as e: |
| return f"β Error: {str(e)}", "", "" |
|
|
|
|
| |
| with gr.Blocks(title="AI Debate Platform", theme=gr.themes.Soft()) as demo: |
| gr.Markdown("# π£οΈ AI Debate Platform") |
| gr.Markdown("Three AI agents debate different perspectives on school policy topics") |
|
|
| |
| if not os.getenv("OPENAI_API_KEY"): |
| gr.Markdown("### β οΈ Configuration Required") |
| gr.Markdown(""" |
| **For Hugging Face Spaces:** |
| 1. Go to Settings β Repository secrets |
| 2. Add secret: `OPENAI_API_KEY` = `sk-your-key` |
| 3. Restart the Space |
| |
| **For local:** |
| ```bash |
| export OPENAI_API_KEY="sk-your-key" |
| ``` |
| """) |
|
|
| with gr.Row(): |
| with gr.Column(scale=2): |
| topic_input = gr.Textbox( |
| label="Debate Topic", |
| placeholder="e.g., Should schools ban smartphones?", |
| lines=2 |
| ) |
| rounds_slider = gr.Slider( |
| minimum=3, |
| maximum=9, |
| value=6, |
| step=1, |
| label="Number of Rounds" |
| ) |
| submit_btn = gr.Button("Start Debate", variant="primary", size="lg") |
|
|
| with gr.Column(scale=1): |
| gr.Markdown("### Example Topics") |
| gr.Markdown(""" |
| - Should schools ban smartphones? |
| - Is homework necessary? |
| - Should school start later? |
| - Should uniforms be required? |
| """) |
|
|
| gr.Markdown("---") |
|
|
| with gr.Tabs(): |
| with gr.Tab("π Evaluation"): |
| eval_output = gr.Markdown(label="Evaluation") |
|
|
| with gr.Tab("π£οΈ Full Debate"): |
| debate_output = gr.Markdown(label="Debate Transcript") |
|
|
| with gr.Tab("π Research"): |
| research_output = gr.Markdown(label="Research Phase") |
|
|
| |
| submit_btn.click( |
| fn=debate_interface, |
| inputs=[topic_input, rounds_slider], |
| outputs=[research_output, debate_output, eval_output] |
| ) |
|
|
| gr.Markdown("---") |
| gr.Markdown("π‘ **Powered by**: " + ("MetaGPT Framework" if METAGPT_AVAILABLE else "OpenAI API")) |
|
|
| |
| if __name__ == "__main__": |
| demo.launch() |
|
|