diff --git a/.env.example b/.env.example index 8bedaa9..8239e67 100644 --- a/.env.example +++ b/.env.example @@ -3,9 +3,13 @@ # OpenAI OPENAI_API_KEY=your-openai-api-key-here +# Optional: Custom base URL for OpenAI-compatible APIs (e.g., vLLM, LocalAI, Ollama) +# OPENAI_BASE_URL=https://api.openai.com # Anthropic ANTHROPIC_API_KEY=your-anthropic-api-key-here +# Optional: Custom base URL for Anthropic-compatible APIs (e.g., MiniMax) +# ANTHROPIC_BASE_URL=https://api.anthropic.com # BFL (Black Forest Labs) BFL_API_KEY=your-bfl-api-key-here @@ -57,3 +61,15 @@ PERPLEXITY_API_KEY=your-perplexity-api-key-here # Gradium GRADIUM_API_KEY=your-gradium-api-key-here + +# ============================================================================ +# Google Cloud / Vertex AI Configuration +# ============================================================================ + +# Vertex AI project and location +GOOGLE_CLOUD_PROJECT=your-gcp-project-id +GOOGLE_CLOUD_LOCATION=global + +# Optional: Path to service account JSON file +# GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json +# If not, please do in console/terminal: gcloud auth application-default login diff --git a/README.md b/README.md index a900598..253ef07 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,147 @@ response = await client.generate("Extract user info: John is 30", output_schema= > `capability` is still supported but deprecated. Prefer `modality` + `operation`. +### Using Google Vertex AI + +Celeste supports Google's Vertex AI platform for enterprise-scale AI deployments. Vertex AI requires authentication via Google Cloud credentials. + +📓 **[See full example notebook](notebooks/vertexai-example.ipynb)** + +**Setup:** + +1. Configure your Google Cloud project in `.env`: + +```bash +# .env file +GOOGLE_CLOUD_PROJECT=your-gcp-project-id +GOOGLE_CLOUD_LOCATION=global # or us-central1, europe-west4, etc. +``` + +2. Authenticate using one of these methods: + - **Application Default Credentials (recommended)**: Run `gcloud auth application-default login` + - **Service Account**: Set `GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json` + +**Usage:** + +```python +import os +from celeste import create_client, Modality, Operation, Provider +from celeste.providers.google.auth import GoogleADC +from dotenv import load_dotenv + +load_dotenv() + +client = create_client( + modality=Modality.TEXT, + operation=Operation.GENERATE, + provider=Provider.GOOGLE, + model="gemini-3.1-pro-preview", + auth=GoogleADC( + project_id=os.getenv("GOOGLE_CLOUD_PROJECT"), + location=os.getenv("GOOGLE_CLOUD_LOCATION") + ) +) + +# Use built-in web search (grounded generation) +response = await client.generate( + "Explain quantum computing with citation links", + web_search=True +) +print(response.content) +``` + +See [.env.example](.env.example) for complete Vertex AI configuration options. + +### Using OpenAI/Anthropic-Compatible APIs + +Celeste cannot cover every AI provider in existence. However, many providers offer OpenAI-compatible or Anthropic-compatible APIs without releasing their own SDKs. Example: **[MiniMax](https://platform.minimax.io/docs/guides/text-generation)**. + +📓 **[See full example notebook](notebooks/anthropic_compat.ipynb)** + +> ⚠️ **Warning:** Always check the provider's documentation first to confirm whether they are compatible with OpenAI or Anthropic APIs. Not all providers support the same endpoints or features. + +For Anthropic-compatible providers, you can configure a custom base URL using the `ANTHROPIC_BASE_URL` environment variable: + +```bash +# .env file +ANTHROPIC_API_KEY=your-minimax-api-key-here +ANTHROPIC_BASE_URL=https://api.minimax.io/anthropic +``` + +Then use it like any Anthropic model: + +```python +import os +from celeste import create_client, Modality, Operation, Provider +from dotenv import load_dotenv + +load_dotenv() + +client = create_client( + modality=Modality.TEXT, + operation=Operation.GENERATE, + provider=Provider.ANTHROPIC, + model="MiniMax-M2.5", # Custom model ID + api_key=os.getenv("ANTHROPIC_API_KEY") +) + +response = await client.generate("Explain quantum computing") +print(response.content) +``` + +> **Note:** You may see a warning like `Model 'MiniMax-M2.5' not registered in Celeste`. This is expected—parameter validation is disabled, but the API call works normally. + +**Optional: Register Custom Models** + +To eliminate the warning and enable parameter validation, you can register custom models: + +```python +from celeste.models import Model, register_models +from celeste.constraints import Range +from celeste import Modality, Operation, Provider + +minimax_model = Model( + id="MiniMax-M2.5", + provider=Provider.ANTHROPIC, + display_name="MiniMax M2.5", + operations={Modality.TEXT: {Operation.GENERATE}}, + streaming=True, + parameter_constraints={ + "temperature": Range(min=0.0, max=1.0), + "max_tokens": Range(min=1, max=4096), + } +) + +register_models([minimax_model]) +``` + +After registration, parameter validation will be enabled for the custom model. + +Similarly, for OpenAI-compatible providers, use the `OPENAI_BASE_URL` environment variable: + +```bash +# .env file +OPENAI_API_KEY=your-api-key-here +OPENAI_BASE_URL=https://your-openai-compatible-endpoint.com +``` + +Then use it with the OpenAI provider: + +```python +from celeste import create_client, Modality, Operation, Provider + +client = create_client( + modality=Modality.TEXT, + operation=Operation.GENERATE, + provider=Provider.OPENAI, + model="your-custom-model-id" +) + +response = await client.generate("Your prompt here") +``` + +See [.env.example](.env.example) for all available `*_BASE_URL` configuration options. + --- ## 🪶 Install ```bash diff --git a/notebooks/anthropic_compat.ipynb b/notebooks/anthropic_compat.ipynb new file mode 100644 index 0000000..a904ab9 --- /dev/null +++ b/notebooks/anthropic_compat.ipynb @@ -0,0 +1,166 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "8d123904", + "metadata": {}, + "source": [ + "### Call the Anthropic compat model, the MiniMax\n", + "\n", + "1. Although most providers on the market support OpenAI's schema, some also support Anthropic's\n", + "2. Utilizing the [MiniMax](https://platform.minimax.io/docs/guides/text-generation) model enables “adaptation” for compatibility with Anthropic." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "93c8a2aa", + "metadata": {}, + "outputs": [], + "source": [ + "from celeste import create_client, Modality, Operation, Provider" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "e185f552", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import os\n", + "from dotenv import load_dotenv\n", + "load_dotenv()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "495afed2", + "metadata": {}, + "outputs": [], + "source": [ + "# This just registers a model to suppress the warning:\n", + "# /var/folders/sk/qxy7wgts58qgp87f1ktl705r0000gn/T/ipykernel_21624/3280120426.py:1: UserWarning: Model 'MiniMax-M2.5' not registered in Celeste for provider anthropic. Parameter validation disabled.\n", + " \n", + "from celeste.models import Model, register_models\n", + "from celeste.constraints import Range, Schema\n", + "\n", + "minimax_model = Model(\n", + " id=\"MiniMax-M2.5\",\n", + " provider=Provider.ANTHROPIC,\n", + " display_name=\"MiniMax M2.5\",\n", + " operations={Modality.TEXT: {Operation.GENERATE}},\n", + " streaming=True,\n", + " parameter_constraints={\n", + " \"temperature\": Range(min=0.0, max=1.0),\n", + " \"max_tokens\": Range(min=1, max=4096),\n", + " }\n", + ")\n", + "\n", + "register_models([minimax_model])" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "95a967ee", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'https://api.minimax.io/anthropic'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from celeste.providers.anthropic.messages.config import BASE_URL\n", + "BASE_URL" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "fad327bd", + "metadata": {}, + "outputs": [], + "source": [ + "client = create_client(\n", + " modality=Modality.TEXT,\n", + " operation=Operation.GENERATE,\n", + " provider=Provider.ANTHROPIC,\n", + " model=\"MiniMax-M2.5\", \n", + " api_key=os.getenv(\"ANTHROPIC_API_KEY\") \n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "480ccaa3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hier eine kurze Einführung in das Quantencomputing und anschließend eine Auswahl von vertrauenswürdigen Quellen, die du für ein tieferes Studium nutzen kannst.\n", + "\n", + "---\n", + "\n", + "## Was ist Quantencomputing?\n", + "\n", + "| Begriff | Erklärung |\n", + "|---------|-----------|\n", + "| **Qubit (Quantenbit)** | Die Grundeinheit der Quanteninformation. Im Gegensatz zu klassischen Bits (0 oder 1) kann ein Qubit durch Superposition gleichzeitig beide Zustände \\(|0\\rangle\\) und \\(|1\\rangle\\) darstellen: \\(|\\psi\\rangle = \\alpha|0\\rangle + \\beta|1\\rangle\\) mit \\(\\alpha,\\beta \\in \\mathbb{C}\\) und \\(|\\alpha|^2+|\\beta|^2=1\\). |\n", + "| **Superposition** | Fähigkeit eines Quantensystems, in mehreren Zuständen gleichzeitig zu sein, bis eine Messung durchgeführt wird. |\n", + "| **Verschränkung (Entanglement)** | Phänomen,\n" + ] + } + ], + "source": [ + "response = await client.generate(\n", + " \"Um Quantencomputing zu erklären, könnt ihr mir Zitatlinks anbieten, wenn ihr fertig seid.\"\n", + ")\n", + "print(response.content)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "celeste-python", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/vertexai-example.ipynb b/notebooks/vertexai-example.ipynb new file mode 100644 index 0000000..0d1b58d --- /dev/null +++ b/notebooks/vertexai-example.ipynb @@ -0,0 +1,135 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "dad67e6c", + "metadata": {}, + "source": [ + "### In diesem Notizbuch wird die grundlegende Verwendung von VertexAI mit Celeste demonstriert.\n", + "1. Set correct vertexai project number including location and flag\n", + "2. Turn on the web_search (online rag)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "5802cfd0", + "metadata": {}, + "outputs": [], + "source": [ + "from celeste import create_client, Modality, Operation, Provider\n", + "from celeste.providers.google.auth import GoogleADC" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "82a619f4", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from dotenv import load_dotenv\n", + "load_dotenv()\n", + "\n", + "GOOGLE_CLOUD_PROJECT = os.getenv(\"GOOGLE_CLOUD_PROJECT\", \"xxxx\")\n", + "GOOGLE_CLOUD_LOCATION = os.getenv(\"GOOGLE_CLOUD_LOCATION\", \"global\")\n", + "GOOGLE_GENAI_USE_VERTEXAI=\"True\"" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "3976c9ac", + "metadata": {}, + "outputs": [], + "source": [ + "client = create_client(\n", + " modality=Modality.TEXT,\n", + " operation=Operation.GENERATE,\n", + " provider=Provider.GOOGLE,\n", + " model=\"gemini-3.1-pro-preview\",\n", + " auth=GoogleADC(\n", + " project_id=GOOGLE_CLOUD_PROJECT, \n", + " location=GOOGLE_CLOUD_LOCATION \n", + " )\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "99c95566", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Um Quantencomputing verständlich zu erklären, ist es am besten, sich zuerst den Unterschied zu unseren herkömmlichen Computern (wie dem PC oder Smartphone) anzusehen.\n", + "\n", + "**1. Bits vs. Qubits**\n", + "Ein klassischer Computer rechnet mit sogenannten „Bits“. Ein Bit ist im Grunde wie ein Lichtschalter: Es kennt nur genau zwei Zustände, nämlich „0“ (Strom aus) oder „1“ (Strom an).\n", + "Ein Quantencomputer nutzt stattdessen „Qubits“ (Quantenbits). Ein Qubit kann nicht nur den Zustand 0 oder 1 haben, sondern – dank der Gesetze der Quantenmechanik – **beide Zustände gleichzeitig** annehmen. \n", + "\n", + "**2. Die wichtigsten Prinzipien der Quantenphysik**\n", + "Damit ein Quantencomputer funktioniert, macht er sich verrückt klingende Effekte aus der mikroskopischen Welt der Atome zunutze:\n", + "* **Superposition (Überlagerung):** Wie oben erwähnt, kann ein Qubit in einer Überlagerung aus 0 und 1 existieren. Stell dir eine Münze vor: Ein klassisches Bit ist eine liegende Münze (sie zeigt entweder Kopf oder Zahl). Ein Qubit ist eine *sich drehende* Münze. Während sie sich dreht, ist sie irgendwie Kopf und Zahl gleichzeitig. Erst wenn man sie misst (also stoppt), fällt sie auf eine konkrete Seite. Dadurch kann das System unfassbar viele Lösungswege auf einmal prüfen, anstatt sie wie ein normaler PC nacheinander auszuprobieren.\n", + "* **Verschränkung (Entanglement):** Zwei Qubits können so miteinander verbunden werden, dass das, was mit dem einen passiert, sofort auch das andere beeinflusst. Wenn man den Zustand eines Qubits misst, kennt man sofort auch den des verschränkten Partners. Das ermöglicht extrem schnelle, parallele Informationsverarbeitungen im Computer.\n", + "* **Interferenz:** Quantencomputer nutzen dieses wellenartige Verhalten, um die Wahrscheinlichkeit der *richtigen* Antworten zu verstärken und die *falschen* Lösungswege gegenseitig auszulöschen (ähnlich wie sich Wasserwellen überlagern und entweder höher werden oder sich glätten).\n", + "\n", + "**3. Wofür braucht man das?**\n", + "Ein Quantencomputer ist kein \"schnellerer\" Alltags-PC, mit dem man besser Videospiele spielen oder im Internet surfen kann. Er ist eine völlig neue Art von Maschine für hochkomplexe, spezielle mathematische Aufgaben. Er soll Probleme lösen, für die heutige Supercomputer Jahrhunderte oder Jahrtausende bräuchten:\n", + "* **Medizin & Chemie:** Simulation von hochkomplexen Molekülen, um neue Medikamente, Impfstoffe oder Materialien effizienter zu entwickeln.\n", + "* **Kryptografie (Verschlüsselung):** Quantencomputer könnten zukünftig heutige Internet-Verschlüsselungen sehr schnell knacken. Gleichzeitig bieten sie aber die Grundlage für neue, physikalisch absolut abhörsichere Verschlüsselungsmethoden.\n", + "* **Komplexe Optimierung & Simulationen:** Bessere Routenplanungen in der globalen Logistik, Finanzmodellierungen oder sehr präzise Wetter- und Klimavorhersagen.\n", + "\n", + "Aktuell befindet sich die Technologie noch in der Entwicklungs- und Forschungsphase. Die Wissenschaft arbeitet derzeit hart daran, den sogenannten „Quantenvorteil“ (den Moment, ab dem der Quantencomputer einen echten Supercomputer in einer nützlichen Aufgabe überholt) dauerhaft und fehlerfrei zu erreichen.\n", + "\n", + "***\n", + "\n", + "**Hier sind die gewünschten Zitatlinks und Quellen zum Weiterlesen, aus denen diese Erklärung zusammengefasst wurde:**\n", + "\n", + "* ** Amazon Web Services (AWS):** [Was ist Quantencomputing?](https://aws.amazon.com/de/what-is/quantum-computing/)\n", + "* ** Cloudflare:** [Was ist Quantencomputing? | Risiken und Herausforderungen](https://www.cloudflare.com/de-de/learning/privacy/what-is-quantum-computing/)\n", + "* ** Microsoft Azure:** [Was ist Quantencomputing? (Superposition und Verschränkung erklärt)](https://learn.microsoft.com/de-de/azure/quantum/overview-what-is-quantum-computing)\n", + "* ** OVHcloud:** [Quantencomputing erklärt – Hype und Realität](https://www.ovhcloud.com/de/learn/what-is-quantum-computing/)\n", + "* ** IBM:** [Was ist Quantencomputing? (Anwendungsfälle und Qubits)](https://www.ibm.com/de-de/topics/quantum-computing)\n", + "* ** quantencomputer-info.de:** [Der unglaubliche Quantencomputer einfach erklärt (Shor-Algorithmus)](https://quantencomputer-info.de/quantencomputer-einfach-erklaert/)\n", + "* ** Plattform Industrie 4.0:** [Quantencomputer und ihre Bedrohung für Verschlüsselungen](https://www.plattform-i40.de/IP/Navigation/DE/Industrie40/Technologie/Quantencomputer/quantencomputer.html)\n", + "* ** Steffen Lippke:** [Wie funktioniert ein Quantencomputer? Einfache Erklärung (Potenziale & Wettervorhersage)](https://steffen-lippke.de/quantencomputer/)\n", + "* ** Dr. Erhard Henkes:** [Quantencomputer - einfach erklärt (Beschleunigung durch Parallelität)](https://www.henkes.de/quantencomputer_einfach_erklaert.htm)\n" + ] + } + ], + "source": [ + "response = await client.generate(\n", + " \"Um Quantencomputing zu erklären, könnt ihr mir Zitatlinks anbieten, wenn ihr fertig seid.\",\n", + " web_search=True,\n", + ")\n", + "print(response.content)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "celeste-python", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/src/celeste/providers/anthropic/messages/config.py b/src/celeste/providers/anthropic/messages/config.py index 0ee1242..e237de2 100644 --- a/src/celeste/providers/anthropic/messages/config.py +++ b/src/celeste/providers/anthropic/messages/config.py @@ -1,5 +1,6 @@ """Configuration for Anthropic Messages API.""" +import os from enum import StrEnum @@ -19,7 +20,8 @@ class VertexAnthropicEndpoint(StrEnum): STREAM_MESSAGE = "/v1/projects/{project_id}/locations/{location}/publishers/anthropic/models/{model_id}:streamRawPredict" -BASE_URL = "https://api.anthropic.com" +# Support custom base URL via environment variable (for Anthropic-compatible APIs like MiniMax) +BASE_URL = os.getenv("ANTHROPIC_BASE_URL", "https://api.anthropic.com") # Required ANTHROPIC_VERSION = "2023-06-01" diff --git a/src/celeste/providers/openai/audio/config.py b/src/celeste/providers/openai/audio/config.py index 9f7b8b2..70364f1 100644 --- a/src/celeste/providers/openai/audio/config.py +++ b/src/celeste/providers/openai/audio/config.py @@ -1,5 +1,6 @@ """Configuration for OpenAI Audio API.""" +import os from enum import StrEnum @@ -11,4 +12,5 @@ class OpenAIAudioEndpoint(StrEnum): CREATE_TRANSLATION = "/v1/audio/translations" -BASE_URL = "https://api.openai.com" +# Support custom base URL via environment variable (for OpenAI-compatible APIs) +BASE_URL = os.getenv("OPENAI_BASE_URL", "https://api.openai.com") diff --git a/src/celeste/providers/openai/images/config.py b/src/celeste/providers/openai/images/config.py index 74e74ea..ad40fa7 100644 --- a/src/celeste/providers/openai/images/config.py +++ b/src/celeste/providers/openai/images/config.py @@ -1,5 +1,6 @@ """Configuration for OpenAI Images API.""" +import os from enum import StrEnum @@ -11,4 +12,5 @@ class OpenAIImagesEndpoint(StrEnum): CREATE_VARIATION = "/v1/images/variations" -BASE_URL = "https://api.openai.com" +# Support custom base URL via environment variable (for OpenAI-compatible APIs) +BASE_URL = os.getenv("OPENAI_BASE_URL", "https://api.openai.com") diff --git a/src/celeste/providers/openai/responses/config.py b/src/celeste/providers/openai/responses/config.py index b448d7a..9ca5bea 100644 --- a/src/celeste/providers/openai/responses/config.py +++ b/src/celeste/providers/openai/responses/config.py @@ -1,5 +1,6 @@ """Configuration for OpenAI Responses API.""" +import os from enum import StrEnum @@ -11,4 +12,5 @@ class OpenAIResponsesEndpoint(StrEnum): GET_MODEL = "/v1/models/{model_id}" -BASE_URL = "https://api.openai.com" +# Support custom base URL via environment variable (for OpenAI-compatible APIs) +BASE_URL = os.getenv("OPENAI_BASE_URL", "https://api.openai.com") diff --git a/src/celeste/providers/openai/videos/config.py b/src/celeste/providers/openai/videos/config.py index 479ae99..68091e8 100644 --- a/src/celeste/providers/openai/videos/config.py +++ b/src/celeste/providers/openai/videos/config.py @@ -1,5 +1,6 @@ """Configuration for OpenAI Videos API.""" +import os from enum import StrEnum @@ -9,7 +10,8 @@ class OpenAIVideosEndpoint(StrEnum): CREATE_VIDEO = "/v1/videos" -BASE_URL = "https://api.openai.com" +# Support custom base URL via environment variable (for OpenAI-compatible APIs) +BASE_URL = os.getenv("OPENAI_BASE_URL", "https://api.openai.com") CONTENT_ENDPOINT_SUFFIX = "/content" # Polling Configuration