diff --git a/GROK_INTEGRATION.md b/GROK_INTEGRATION.md new file mode 100644 index 000000000..d2488c42f --- /dev/null +++ b/GROK_INTEGRATION.md @@ -0,0 +1,40 @@ +Grok bot integration for Codespaces Jupyter + +Overview +- Adds a minimal Grok API client and an IPython magic to call it from notebooks. + +Authentication +- Set the GROK_API_KEY environment variable to your API key, e.g. in Codespaces /devcontainer.json or your shell: + + export GROK_API_KEY="sk-..." # Linux/macOS + # PowerShell + $env:GROK_API_KEY = "sk-..." + +- Optionally set GROK_API_URL to a custom endpoint if your provider requires it. + +Usage in notebooks +- Load the extension once per kernel: + + %load_ext grok_bot.magic + +- Use line magic: + + %grok What is the summary of this dataset? + +- Use cell magic for multi-line prompts: + + %%grok + Analyze the following: + - Column A distribution + - Suggestions for visualization + +Notes +- The client uses the GROK_API_KEY env var by default. The API URL is configurable via GROK_API_URL. +- Error handling prints helpful messages in the notebook; exceptions from the HTTP client are surfaced as text. + +Developer notes +- The package lives in grok_bot/ and exposes GrokClient and IPython extension loader. +- Requirements updated to include `requests`. + +Security +- Do not commit API keys to the repository. Prefer Codespaces secrets or devcontainer environment variables. diff --git a/grok_bot/__init__.py b/grok_bot/__init__.py new file mode 100644 index 000000000..33e62ead1 --- /dev/null +++ b/grok_bot/__init__.py @@ -0,0 +1,4 @@ +from .api import GrokClient +from .magic import load_ipython_extension, unload_ipython_extension + +__all__ = ["GrokClient", "load_ipython_extension", "unload_ipython_extension"] diff --git a/grok_bot/api.py b/grok_bot/api.py new file mode 100644 index 000000000..7d4524cf0 --- /dev/null +++ b/grok_bot/api.py @@ -0,0 +1,34 @@ +import os +import requests +from typing import Optional + + +class GrokClient: + """Minimal Grok API wrapper. + + Authentication: set the GROK_API_KEY environment variable (or pass api_key). + Configure endpoint with GROK_API_URL if your provider uses a custom URL. + """ + + def __init__(self, api_key: Optional[str] = None, api_url: Optional[str] = None, timeout: int = 30): + self.api_key = api_key or os.getenv("GROK_API_KEY") + self.api_url = api_url or os.getenv("GROK_API_URL") or "https://api.grok.example/v1/complete" + self.timeout = timeout + if not self.api_key: + raise ValueError("GROK_API_KEY environment variable not set. Set it or pass api_key to GrokClient.") + + def ask(self, prompt: str, model: str = "grok-1") -> str: + payload = {"model": model, "prompt": prompt} + headers = {"Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json"} + try: + resp = requests.post(self.api_url, json=payload, headers=headers, timeout=self.timeout) + resp.raise_for_status() + data = resp.json() + # Support a couple common response shapes + if isinstance(data, dict) and "text" in data: + return data["text"] + if isinstance(data, dict) and "choices" in data and data["choices"]: + return data["choices"][0].get("text") or str(data) + return str(data) + except requests.RequestException as e: + raise RuntimeError(f"Grok API request failed: {e}") from e diff --git a/grok_bot/magic.py b/grok_bot/magic.py new file mode 100644 index 000000000..7eca73c68 --- /dev/null +++ b/grok_bot/magic.py @@ -0,0 +1,48 @@ +from IPython.core.magic import Magics, magics_class, line_cell_magic +from IPython.display import Markdown, display +from .api import GrokClient +import os + + +@magics_class +class GrokMagics(Magics): + def __init__(self, shell): + super().__init__(shell) + api_key = os.getenv("GROK_API_KEY") + self.client = None + if api_key: + try: + self.client = GrokClient(api_key=api_key) + except Exception: + self.client = None + + @line_cell_magic + def grok(self, line, cell=None): + """%grok or %%grok + + Outputs Grok's response as rendered Markdown. + """ + prompt = cell if cell is not None else line + if not prompt: + print("Usage: %grok or %%grok ") + return + if not self.client: + try: + self.client = GrokClient() + except Exception as e: + print(f"Grok client initialization error: {e}") + return + try: + resp = self.client.ask(prompt) + display(Markdown("**Grok**\n\n" + resp)) + except Exception as e: + print(f"Grok request failed: {e}") + + +def load_ipython_extension(ipython): + ipython.register_magics(GrokMagics) + + +def unload_ipython_extension(ipython): + # IPython does not provide a stable unregister API for magics; keep this as a noop. + pass diff --git a/requirements.txt b/requirements.txt index c40ef94b8..0a023d417 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,3 +8,4 @@ pillow>=12.1.1 fonttools>=4.60.0 filelock>=3.20.3 Pygments==2.20.0 +requests>=2.30.0