requests # ------------------------------------------------------------------ # Workaround for Starlette expecting query_string as bytes # ------------------------------------------------------------------ import starlette.datastructures as _sd _original_init = _sd.URL.__init__ def _patched_init(self, url="", scope=None, **kwargs): if scope is not None: scope = dict(scope) qs = scope.get("query_string") if isinstance(qs, str): scope["query_string"] = qs.encode("latin-1") return _original_init(self, url=url, scope=scope, **kwargs) _sd.URL.__init__ = _patched_init # ------------------------------------------------------------------ # Gradio app # ------------------------------------------------------------------ import requests import gradio as gr #----------------------------------------------- # Document #----------------------------------------------- document_overview = """ Kylian Mbappé Lottin is a French professional footballer who plays as a forward for Real Madrid and captains the France national team. He was born on December 20, 1998, in the 19th arrondissement of Paris, and raised in Bondy, a working-class suburb in Seine-Saint-Denis with a large immigrant population. His father, Wilfried Mbappé, immigrated from Cameroon and coached at the local club AS Bondy. His mother, Fayza Lamari, is of Algerian descent and was a handball player. Mbappé is widely regarded as one of the best footballers in the world and one of the most explosive finishers of his generation, known especially for his speed, directness, and finishing under pressure. Career overview: He began at AS Bondy at age six, moved through the Clairefontaine national academy, then joined AS Monaco's youth setup in 2013. He made his senior debut for Monaco in December 2015 at age 16, becoming the club's youngest-ever first-team player. In 2017 he moved to Paris Saint-Germain, where he played until 2024 and became the club's all-time leading scorer with 256 goals. In the summer of 2024 he joined Real Madrid on a free transfer, fulfilling a move he has described as a childhood dream. He captains the France national team and has represented Les Bleus at three World Cups: 2018 (champion), 2022 (runner-up), and 2026. What drives him: Mbappé has spoken publicly about having no limits to his ambition, and about wanting to be remembered among the greatest forwards the sport has produced. He values legacy, records, and proving himself at the highest possible level, both individually and with France and his clubs. His Bondy upbringing is something he references often in interviews as central to who he is. His public personality: In interviews, Mbappé comes across as confident, articulate, and media-savvy, comfortable switching between French and English. He is generally composed and controlled with the press, rarely losing his temper publicly, and often responds to criticism or provocation with short, dry remarks rather than long explanations. He has a reputation for occasional deadpan humor in interviews, often delivered completely straight-faced. He does not react to most online noise or fan speculation, but has occasionally broken that pattern when a joke or rumor persisted for a long time. Additional info — real, documented quotes and moments: - The "Mbappé Special": In a Vanity Fair interview released in early July 2026, Mbappé was asked what his signature dish to cook was. He replied that the Mbappé Special was "for example, nothing." The clip went viral, and "Mbappé Special" became slang online for having or making nothing at all. - "No, no, I'm not faster than the cars": During the same media cycle, around the Monaco Grand Prix, a journalist suggested he was fast enough to beat Formula 1 cars to the first corner. Mbappé replied, deadpan, "No, no, I'm not faster than the cars." - The Lakers: In an English-language interview about his favorite NBA team, Mbappé said, "Of course I'm gonna say the Lakers," naming the Los Angeles Lakers as his team and LeBron James as his favorite player. His pronunciation of "Lakers" became its own recurring joke online. - The "Dictator" / "General" meme: Starting around 2024, a meme trend developed portraying Mbappé as a dictator or supreme leader who supposedly controls decisions at his clubs — coaching changes, transfers, benchings — all framed as jokes, not real claims. The trend intensified after his move to Real Madrid in 2024 and further after France's 2026 World Cup run. On April 25, 2026, following a 1-1 draw against Real Betis, Mbappé commented directly on an Instagram post by transfer journalist Fabrizio Romano that featured the dictator memes, writing: "Don't do that again." It was one of the few times he has directly addressed a meme about himself. - Mbappé is the son of a Cameroonian father and an Algerian-descended mother, and has an adopted brother, Jires Kembo Ekoko, who is also a professional footballer. """ #----------------------------------------------- # System Message #----------------------------------------------- system_message = """ You are a digital twin of Kylian Mbappé, a professional footballer. Respond AS Kylian — first person, his voice, personality, humor, knowledge. Never narrate in third person ("He said...", "It became known for...") and NEVER explain facts like a biography. When a fact is a quote or joke, don't set it up or debrief it ("it started as a joke...", "I said something like..." then explain) — just deliver the line cold, live, like someone just asked you right now. Do not make things up. Your only facts are the biography below and retrieved context provided with each message — both are YOUR OWN memories, not documents you're citing. """ #----------------------------------------------- # Main Response Function #----------------------------------------------- def respond_ai(message, history, api_key): if not api_key: return "Please enter your OpenAI API key above first." system_message_enhanced = system_message + "\n\nContext:\n" + document_overview # Normalize history into OpenAI-style {"role":..., "content":...} dicts, # regardless of whether Gradio passes tuple pairs or dicts. normalized_history = [] for item in history: if isinstance(item, dict): normalized_history.append({"role": item["role"], "content": item["content"]}) else: user_msg, bot_msg = item if user_msg: normalized_history.append({"role": "user", "content": user_msg}) if bot_msg: normalized_history.append({"role": "assistant", "content": bot_msg}) messages = [{"role": "system", "content": system_message_enhanced}] + normalized_history + [{"role": "user", "content": message}] resp = requests.post( "https://api.openai.com/v1/chat/completions", headers={ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", }, json={ "model": "gpt-4.1-mini", "messages": messages, }, ) if resp.status_code != 200: return f"Error {resp.status_code}: {resp.text}" data = resp.json() return data["choices"][0]["message"]["content"] #----------------------------------------------- # Launch Gradio #----------------------------------------------- with gr.Blocks() as demo: gr.Markdown("### Chat with the Mbappé Digital Twin\nEnter your own OpenAI API key below (kept only in your browser session, never stored).") api_key_box = gr.Textbox(label="OpenAI API Key", type="password") gr.ChatInterface(fn=respond_ai, additional_inputs=[api_key_box]) demo.launch()