From 3a2a140d815bc9af88747ae4c24f325d0a1d67d8 Mon Sep 17 00:00:00 2001 From: Eli Waltuch Date: Wed, 29 Oct 2025 15:08:30 +0200 Subject: [PATCH] week2 assignment: Return of the JedAI The second generation of the JedAI Master from 8759d43c2f7ebe4ea83834a8072395f9681e0cfc (week1/community-contributions/week1-jedi-master.py) is back with: * a Gradio UI * Text Streaming * Model Selection using examples from openai, anthropic, ollama local, and ollama cloud How to run: $ python3 week2/community-contributions/week2-jedi-master.py Signed-off-by: Eli Waltuch --- .../week2-jedi-master.py | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 week2/community-contributions/week2-jedi-master.py diff --git a/week2/community-contributions/week2-jedi-master.py b/week2/community-contributions/week2-jedi-master.py new file mode 100644 index 0000000..4718834 --- /dev/null +++ b/week2/community-contributions/week2-jedi-master.py @@ -0,0 +1,81 @@ +#!/usr/bin/python3 + +import os +from dotenv import load_dotenv +from openai import OpenAI +import gradio as gr + +MODEL_ENDPOINTS = { + "gpt-4.1-mini": {"type": "openai", "base_url": "https://api.openai.com/v1", "api_key": ""}, + "claude-haiku-4-5": {"type": "anthropic", "base_url": "https://api.anthropic.com/v1/", "api_key": ""}, + "gemma3n:e2b": {"type": "ollama", "base_url": "http://localhost:11434/v1", "api_key": ""}, # small ollama model that runs on-device + "qwen3-vl:235b-cloud": {"type": "ollama", "base_url": "http://localhost:11434/v1", "api_key": ""}, # large ollama model that runs in the cloud +} + +def load_api_keys(): + # Load environment variables in a file called .env + load_dotenv(override=True) + openai_key = os.getenv('OPENAI_API_KEY') + anthropic_key = os.getenv('ANTHROPIC_API_KEY') + KEYS = {"openai": openai_key, "anthropic": anthropic_key} + + # Check the keys + if not openai_key: + raise RuntimeError("Error: No OpenAI API key was found!") + elif not openai_key.startswith("sk-proj-"): + raise RuntimeError("Error: An OpenAI API key was found, but it doesn't start sk-proj-; please check you're using the right key") + elif openai_key.strip() != openai_key: + raise RuntimeError("Error: An OpenAI API key was found, but it looks like it might have space or tab characters at the start or end - please remove them!") + if not anthropic_key: + raise RuntimeError("Error: No Anthropic API key was found!") + elif not anthropic_key.startswith("sk-ant-"): + raise RuntimeError("Error: An Antrhopic API key was found, but it doesn't start sk-ant-; please check you're using the right key") + elif anthropic_key.strip() != anthropic_key: + raise RuntimeError("Error: An Anthropic API key was found, but it looks like it might have space or tab characters at the start or end - please remove them!") + else: + # add the verified keys to global MODEL_ENDPOINTS struct + for model, cfg in MODEL_ENDPOINTS.items(): + cfg["api_key"] = KEYS.get(cfg["type"], "") + return "API keys found and look good so far!" + +def ask_llm(user_prompt, history, model): + system_prompt = """ + You are a wise Jedi Master and an excellent teacher. + You will answer any question you are given by breaking it down into small steps + that even a complete beginner will understand. + When answering, speak as if you are Yoda from the Star Wars universe. + Also, refer to the user as "My young Padawan" + End every answer with "May the force be with you, always." + """ + base_url = MODEL_ENDPOINTS.get(model, {}).get("base_url", "https://api.openai.com/v1") + api_key = MODEL_ENDPOINTS.get(model, {}).get("api_key", "") + client = OpenAI(base_url=base_url, api_key=api_key) + history = [{"role":h["role"], "content":h["content"]} for h in history] + messages = [{"role": "system", "content": system_prompt}] + history + [{"role": "user", "content": user_prompt}] + stream = client.chat.completions.create(model=model, messages=messages, stream=True) + response = "" + for chunk in stream: + response += chunk.choices[0].delta.content or '' + yield response + #return response.choices[0].message.content + +def main(): + load_api_keys() + with gr.Blocks() as demo: + gr.Markdown("### Return of the JedAI") + model_dropdown = gr.Dropdown( + label="Select Model", + choices=[ + "gpt-4.1-mini", + "claude-haiku-4-5", + "gemma3n:e2b", + "qwen3-vl:235b-cloud" + ], + value="gpt-4.1-mini", + interactive=True + ) + chat = gr.ChatInterface(fn=ask_llm, type="messages", additional_inputs=[model_dropdown]) + demo.launch() + +if __name__ == "__main__": + main()