{"id":29604418,"url":"https://github.com/gewoonjaap/gemini-cli-openai","last_synced_at":"2025-07-20T15:07:28.135Z","repository":{"id":301349694,"uuid":"1008961851","full_name":"GewoonJaap/gemini-cli-openai","owner":"GewoonJaap","description":"Expose Gemini CLI endpoints as OpenAI API with Cloudflare Workers","archived":false,"fork":false,"pushed_at":"2025-07-14T17:53:24.000Z","size":340,"stargazers_count":311,"open_issues_count":1,"forks_count":64,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-07-14T19:48:29.206Z","etag":null,"topics":["gemini","gemini-cli","llm","openai","openai-api"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/GewoonJaap.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["GewoonJaap"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"lfx_crowdfunding":null,"polar":null,"buy_me_a_coffee":"mrproper","thanks_dev":null,"custom":null}},"created_at":"2025-06-26T11:07:33.000Z","updated_at":"2025-07-14T17:53:26.000Z","dependencies_parsed_at":"2025-07-14T17:09:29.402Z","dependency_job_id":"20000bd9-9909-48b6-bd9d-d8496c1553f7","html_url":"https://github.com/GewoonJaap/gemini-cli-openai","commit_stats":null,"previous_names":["gewoonjaap/gemini-cli-openai"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/GewoonJaap/gemini-cli-openai","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GewoonJaap%2Fgemini-cli-openai","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GewoonJaap%2Fgemini-cli-openai/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GewoonJaap%2Fgemini-cli-openai/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GewoonJaap%2Fgemini-cli-openai/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GewoonJaap","download_url":"https://codeload.github.com/GewoonJaap/gemini-cli-openai/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GewoonJaap%2Fgemini-cli-openai/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266144327,"owners_count":23883163,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["gemini","gemini-cli","llm","openai","openai-api"],"created_at":"2025-07-20T15:07:21.120Z","updated_at":"2025-07-20T15:07:28.114Z","avatar_url":"https://github.com/GewoonJaap.png","language":"TypeScript","funding_links":["https://github.com/sponsors/GewoonJaap","https://buymeacoffee.com/mrproper","https://www.buymeacoffee.com/mrproper"],"categories":["CLIs"],"sub_categories":[],"readme":"# 🚀 Gemini CLI OpenAI Worker\n\n[![\"Buy Me A Coffee\"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/mrproper)\n\nTransform Google's Gemini models into OpenAI-compatible endpoints using Cloudflare Workers. Access Google's most advanced AI models through familiar OpenAI API patterns, powered by OAuth2 authentication and the same infrastructure that drives the official Gemini CLI.\n\n## ✨ Features\n\n- 🔐 **OAuth2 Authentication** - No API keys required, uses your Google account\n- 🎯 **OpenAI-Compatible API** - Drop-in replacement for OpenAI endpoints\n- 📚 **OpenAI SDK Support** - Works with official OpenAI SDKs and libraries\n- 🖼️ **Vision Support** - Multi-modal conversations with images (base64 \u0026 URLs)\n- 🔧 **Tool Calling Support** - Function calling with Gemini API integration\n- 🧠 **Advanced Reasoning** - Support for Gemini's thinking capabilities with effort controls\n- 🛡️ **Content Safety** - Configurable Gemini moderation settings\n- 🌐 **Third-party Integration** - Compatible with Open WebUI, ChatGPT clients, and more\n- ⚡ **Cloudflare Workers** - Global edge deployment with low latency\n- 🔄 **Smart Token Caching** - Intelligent token management with KV storage\n- 🆓 **Free Tier Access** - Leverage Google's free tier through Code Assist API\n- 📡 **Real-time Streaming** - Server-sent events for live responses with token usage\n- 🎭 **Multiple Models** - Access to latest Gemini models including experimental ones\n\n## 🤖 Supported Models\n\n| Model ID | Context Window | Max Tokens | Thinking Support | Description |\n|----------|----------------|------------|------------------|-------------|\n| `gemini-2.5-pro` | 1M | 65K | ✅ | Latest Gemini 2.5 Pro model with reasoning capabilities |\n| `gemini-2.5-flash` | 1M | 65K | ✅ | Fast Gemini 2.5 Flash model with reasoning capabilities |\n\n\u003e **Note:** Gemini 2.5 models have thinking enabled by default. The API automatically manages this:\n\u003e - When real thinking is disabled (environment), thinking budget is set to 0 to disable it\n\u003e - When real thinking is enabled (environment), thinking budget defaults to -1 (dynamic allocation by Gemini)\n\u003e\n\u003e **Thinking support** has two modes:\n\u003e - **Fake thinking**: Set `ENABLE_FAKE_THINKING=true` to generate synthetic reasoning text (good for testing)\n\u003e - **Real thinking**: Set `ENABLE_REAL_THINKING=true` to use Gemini's native reasoning capabilities\n\u003e \n\u003e Real thinking is controlled entirely by the `ENABLE_REAL_THINKING` environment variable. You can optionally set a `\"thinking_budget\"` in your request (token limit for reasoning, -1 for dynamic allocation, 0 to disable thinking entirely).\n\n- **Reasoning Effort Support**: You can control the reasoning effort of thinking models by including `reasoning_effort` in the request body (e.g., `extra_body` or `model_params`). This parameter allows you to fine-tune the model's internal reasoning process, balancing between speed and depth of thought.\n  - `none`: Disables thinking (`thinking_budget = 0`).\n  - `low`: Sets `thinking_budget = 1024`.\n  - `medium`: Sets `thinking_budget = 12288` for flash models, `16384` for other models.\n  - `high`: Sets `thinking_budget = 24576` for flash models, `32768` for other models.\n\u003e \n\u003e Set `STREAM_THINKING_AS_CONTENT=true` to stream reasoning as content with `\u003cthinking\u003e` tags (DeepSeek R1 style) instead of using the reasoning field.\n\n## �🛠️ Setup\n\n### Prerequisites\n\n1. **Google Account** with access to Gemini\n2. **Cloudflare Account** with Workers enabled\n3. **Wrangler CLI** installed (`npm install -g wrangler`)\n\n### Step 1: Get OAuth2 Credentials\n\nYou need OAuth2 credentials from a Google account that has accessed Gemini. The easiest way to get these is through the official Gemini CLI.\n\n#### Using Gemini CLI\n\n1. **Install Gemini CLI**:\n   ```bash\n   npm install -g @google/gemini-cli\n   ```\n\n2. **Start the Gemini CLI**:\n   ```bash\n   gemini\n   ```\n3. **Authenticate with Google**:\n   \n   Select `● Login with Google`.\n   \n   A browser window will now open prompting you to login with your Google account.\n   \n4. **Locate the credentials file**:\n   \n   **Windows:**\n   ```\n   C:\\Users\\USERNAME\\.gemini\\oauth_creds.json\n   ```\n   \n   **macOS/Linux:**\n   ```\n   ~/.gemini/oauth_creds.json\n   ```\n\n5. **Copy the credentials**:\n   The file contains JSON in this format:\n   ```json\n   {\n     \"access_token\": \"ya29.a0AS3H6Nx...\",\n     \"refresh_token\": \"1//09FtpJYpxOd...\",\n     \"scope\": \"https://www.googleapis.com/auth/cloud-platform ...\",\n     \"token_type\": \"Bearer\",\n     \"id_token\": \"eyJhbGciOiJSUzI1NiIs...\",\n     \"expiry_date\": 1750927763467\n   }\n   ```\n\n### Step 2: Create KV Namespace\n\n```bash\n# Create a KV namespace for token caching\nwrangler kv namespace create \"GEMINI_CLI_KV\"\n```\n\nNote the namespace ID returned.\nUpdate `wrangler.toml` with your KV namespace ID:\n```toml\nkv_namespaces = [\n  { binding = \"GEMINI_CLI_KV\", id = \"your-kv-namespace-id\" }\n]\n```\n\n### Step 3: Environment Setup\n\nCreate a `.dev.vars` file:\n```bash\n# Required: OAuth2 credentials JSON from Gemini CLI authentication\nGCP_SERVICE_ACCOUNT={\"access_token\":\"ya29...\",\"refresh_token\":\"1//...\",\"scope\":\"...\",\"token_type\":\"Bearer\",\"id_token\":\"eyJ...\",\"expiry_date\":1750927763467}\n\n# Optional: Google Cloud Project ID (auto-discovered if not set)\n# GEMINI_PROJECT_ID=your-project-id\n\n# Optional: API key for authentication (if not set, API is public)\n# When set, clients must include \"Authorization: Bearer \u003cyour-api-key\u003e\" header\n# Example: sk-1234567890abcdef1234567890abcdef\nOPENAI_API_KEY=sk-your-secret-api-key-here\n```\n\nFor production, set the secrets:\n```bash\nwrangler secret put GCP_SERVICE_ACCOUNT\nwrangler secret put OPENAI_API_KEY  # Optional, only if you want authentication\n```\n\n### Step 4: Deploy\n\n```bash\n# Install dependencies\nnpm install\n\n# Deploy to Cloudflare Workers\nnpm run deploy\n\n# Or run locally for development\nnpm run dev\n```\n\n## 🔧 Configuration\n\n### Environment Variables\n\n| Variable | Required | Description |\n|----------|----------|-------------|\n| `GCP_SERVICE_ACCOUNT` | ✅ | OAuth2 credentials JSON string |\n| `GEMINI_PROJECT_ID` | ❌ | Google Cloud Project ID (auto-discovered if not set) |\n| `OPENAI_API_KEY` | ❌ | API key for authentication (if not set, API is public) |\n| `ENABLE_FAKE_THINKING` | ❌ | Enable synthetic thinking output for thinking models (set to \"true\" to enable) |\n| `ENABLE_REAL_THINKING` | ❌ | Enable real Gemini thinking output (set to \"true\" to enable) |\n| `STREAM_THINKING_AS_CONTENT` | ❌ | Stream thinking as content with `\u003cthinking\u003e` tags (DeepSeek R1 style) |\n| `ENABLE_AUTO_MODEL_SWITCHING` | ❌ | Enable automatic fallback from pro to flash models on rate limits (set to \"true\" to enable) |\n| `GEMINI_MODERATION_HARASSMENT_THRESHOLD` | ❌ | Sets the moderation threshold for harassment content (e.g., `BLOCK_NONE`, `BLOCK_FEW`, `BLOCK_SOME`, `BLOCK_ONLY_HIGH`, `HARM_BLOCK_THRESHOLD_UNSPECIFIED`) |\n| `GEMINI_MODERATION_HATE_SPEECH_THRESHOLD` | ❌ | Sets the moderation threshold for hate speech content (e.g., `BLOCK_NONE`, `BLOCK_FEW`, `BLOCK_SOME`, `BLOCK_ONLY_HIGH`, `HARM_BLOCK_THRESHOLD_UNSPECIFIED`) |\n| `GEMINI_MODERATION_SEXUALLY_EXPLICIT_THRESHOLD` | ❌ | Sets the moderation threshold for sexually explicit content (e.g., `BLOCK_NONE`, `BLOCK_FEW`, `BLOCK_SOME`, `BLOCK_ONLY_HIGH`, `HARM_BLOCK_THRESHOLD_UNSPECIFIED`) |\n| `GEMINI_MODERATION_DANGEROUS_CONTENT_THRESHOLD` | ❌ | Sets the moderation threshold for dangerous content (e.g., `BLOCK_NONE`, `BLOCK_FEW`, `BLOCK_SOME`, `BLOCK_ONLY_HIGH`, `HARM_BLOCK_THRESHOLD_UNSPECIFIED`) |\n\n**Authentication Security:**\n- When `OPENAI_API_KEY` is set, all `/v1/*` endpoints require authentication\n- Clients must include the header: `Authorization: Bearer \u003cyour-api-key\u003e`\n- Without this environment variable, the API is publicly accessible\n- Recommended format: `sk-` followed by a random string (e.g., `sk-1234567890abcdef...`)\n\n**Thinking Models:**\n- **Fake Thinking**: When `ENABLE_FAKE_THINKING` is set to \"true\", models marked with `thinking: true` will generate synthetic reasoning text before their actual response\n- **Real Thinking**: When `ENABLE_REAL_THINKING` is set to \"true\", requests with `include_reasoning: true` will use Gemini's native thinking capabilities\n- Real thinking provides genuine reasoning from Gemini and requires thinking-capable models (like Gemini 2.5 Pro/Flash)\n- You can control the reasoning token budget with the `thinking_budget` parameter\n- By default, reasoning output is streamed as `reasoning` chunks in the OpenAI-compatible response format\n- When `STREAM_THINKING_AS_CONTENT` is also set to \"true\", reasoning will be streamed as regular content wrapped in `\u003cthinking\u003e\u003c/thinking\u003e` tags (DeepSeek R1 style)\n- **Optimized UX**: The `\u003c/thinking\u003e` tag is only sent when the actual LLM response begins, eliminating awkward pauses between thinking and response\n- If neither thinking mode is enabled, thinking models will behave like regular models\n\n**Auto Model Switching:**\n- When `ENABLE_AUTO_MODEL_SWITCHING` is set to \"true\", the system will automatically fall back from `gemini-2.5-pro` to `gemini-2.5-flash` when encountering rate limit errors (HTTP 429 or 503)\n- This provides seamless continuity when the Pro model quota is exhausted\n- The fallback is indicated in the response with a notification message\n- Only applies to supported model pairs (currently: pro → flash)\n- Works for both streaming and non-streaming requests\n\n### KV Namespaces\n\n| Binding | Purpose |\n|---------|---------|\n| `GEMINI_CLI_KV` | Token caching and session management |\n\n## 🚨 Troubleshooting\n\n### Common Issues\n\n**401 Authentication Error**\n- Check if your OAuth2 credentials are valid\n- Ensure the refresh token is working\n- Verify the credentials format matches exactly\n\n**Token Refresh Failed**\n- Credentials might be from wrong OAuth2 client\n- Refresh token might be expired or revoked\n- Check the debug cache endpoint for token status\n\n**Project ID Discovery Failed**\n- Set `GEMINI_PROJECT_ID` environment variable manually\n- Ensure your Google account has access to Gemini\n\n## 💻 Usage Examples\n\n### Cline Integration\n\n[Cline](https://github.com/cline/cline) is a powerful AI assistant extension for VS Code. You can easily configure it to use your Gemini models:\n\n1. **Install Cline** in VS Code from the Extensions marketplace\n\n2. **Configure OpenAI API settings**:\n   - Open Cline settings\n   - Set **API Provider** to \"OpenAI\"\n   - Set **Base URL** to: `https://your-worker.workers.dev/v1`\n   - Set **API Key** to: `sk-your-secret-api-key-here` (use your OPENAI_API_KEY if authentication is enabled)\n\n3. **Select a model**:\n   - Choose `gemini-2.5-pro` for complex reasoning tasks\n   - Choose `gemini-2.5-flash` for faster responses\n\n### Open WebUI Integration\n\n1. **Add as OpenAI-compatible endpoint**:\n   - Base URL: `https://your-worker.workers.dev/v1`\n   - API Key: `sk-your-secret-api-key-here` (use your OPENAI_API_KEY if authentication is enabled)\n\n2. **Configure models**:\n   Open WebUI will automatically discover available Gemini models through the `/v1/models` endpoint.\n\n3. **Start chatting**:\n   Use any Gemini model just like you would with OpenAI models!\n\n### LiteLLM Integration\n\n[LiteLLM](https://github.com/BerriAI/litellm) works seamlessly with this worker, especially when using the DeepSeek R1-style thinking streams:\n\n```python\nimport litellm\n\n# Configure LiteLLM to use your worker\nlitellm.api_base = \"https://your-worker.workers.dev/v1\"\nlitellm.api_key = \"sk-your-secret-api-key-here\"\n\n# Use thinking models with LiteLLM\nresponse = litellm.completion(\n    model=\"gemini-2.5-flash\",\n    messages=[\n        {\"role\": \"user\", \"content\": \"Solve this step by step: What is 15 * 24?\"}\n    ],\n    stream=True\n)\n\nfor chunk in response:\n    if chunk.choices[0].delta.content:\n        print(chunk.choices[0].delta.content, end=\"\")\n```\n\n**Pro Tip**: Set `STREAM_THINKING_AS_CONTENT=true` for optimal LiteLLM compatibility. The `\u003cthinking\u003e` tags format works better with LiteLLM's parsing and various downstream tools.\n\n### OpenAI SDK (Python)\n```python\nfrom openai import OpenAI\n\n# Initialize with your worker endpoint\nclient = OpenAI(\n    base_url=\"https://your-worker.workers.dev/v1\",\n    api_key=\"sk-your-secret-api-key-here\"  # Use your OPENAI_API_KEY if authentication is enabled\n)\n\n# Chat completion\nresponse = client.chat.completions.create(\n    model=\"gemini-2.5-flash\",\n    messages=[\n        {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},\n        {\"role\": \"user\", \"content\": \"Explain machine learning in simple terms\"}\n    ],\n    stream=True\n)\n\nfor chunk in response:\n    if chunk.choices[0].delta.content:\n        print(chunk.choices[0].delta.content, end=\"\")\n\n# Real thinking mode\nresponse = client.chat.completions.create(\n    model=\"gemini-2.5-pro\",\n    messages=[\n        {\"role\": \"user\", \"content\": \"Solve this step by step: What is the derivative of x^3 + 2x^2 - 5x + 3?\"}\n    ],\n    extra_body={\n        \"include_reasoning\": True,\n        \"thinking_budget\": 1024\n    },\n    stream=True\n)\n\nfor chunk in response:\n    # Real thinking appears in the reasoning field\n    if hasattr(chunk.choices[0].delta, 'reasoning') and chunk.choices[0].delta.reasoning:\n        print(f\"[Thinking] {chunk.choices[0].delta.reasoning}\")\n    if chunk.choices[0].delta.content:\n        print(chunk.choices[0].delta.content, end=\"\")\n```\n\n### OpenAI SDK (JavaScript/TypeScript)\n```typescript\nimport OpenAI from 'openai';\n\nconst openai = new OpenAI({\n  baseURL: 'https://your-worker.workers.dev/v1',\n  apiKey: 'sk-your-secret-api-key-here', // Use your OPENAI_API_KEY if authentication is enabled\n});\n\nconst stream = await openai.chat.completions.create({\n  model: 'gemini-2.5-flash',\n  messages: [\n    { role: 'user', content: 'Write a haiku about coding' }\n  ],\n  stream: true,\n});\n\nfor await (const chunk of stream) {\n  const content = chunk.choices[0]?.delta?.content || '';\n  process.stdout.write(content);\n}\n```\n\n### cURL\n```bash\ncurl -X POST https://your-worker.workers.dev/v1/chat/completions \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Authorization: Bearer sk-your-secret-api-key-here\" \\\n  -d '{\n    \"model\": \"gemini-2.5-flash\",\n    \"messages\": [\n      {\"role\": \"user\", \"content\": \"Explain quantum computing\"}\n    ]\n  }'\n```\n\n### Raw JavaScript/TypeScript\n```javascript\nconst response = await fetch('https://your-worker.workers.dev/v1/chat/completions', {\n  method: 'POST',\n  headers: {\n    'Content-Type': 'application/json',\n  },\n  body: JSON.stringify({\n    model: 'gemini-2.5-flash',\n    messages: [\n      { role: 'user', content: 'Hello, world!' }\n    ]\n  })\n});\n\nconst reader = response.body.getReader();\nconst decoder = new TextDecoder();\n\nwhile (true) {\n  const { done, value } = await reader.read();\n  if (done) break;\n  \n  const chunk = decoder.decode(value);\n  const lines = chunk.split('\\n');\n  \n  for (const line of lines) {\n    if (line.startsWith('data: ') \u0026\u0026 line !== 'data: [DONE]') {\n      const data = JSON.parse(line.substring(6));\n      const content = data.choices[0]?.delta?.content;\n      if (content) {\n        console.log(content);\n      }\n    }\n  }\n}\n```\n\n### Raw Python (without SDK)\n```python\nimport requests\nimport json\n\nurl = \"https://your-worker.workers.dev/v1/chat/completions\"\ndata = {\n    \"model\": \"gemini-2.5-flash\",\n    \"messages\": [\n        {\"role\": \"user\", \"content\": \"Write a Python function to calculate fibonacci\"}\n    ]\n}\n\nresponse = requests.post(url, json=data, stream=True)\n\nfor line in response.iter_lines():\n    if line and line.startswith(b'data: '):\n        try:\n            chunk = json.loads(line[6:].decode())\n            content = chunk['choices'][0]['delta'].get('content', '')\n            if content:\n                print(content, end='')\n        except json.JSONDecodeError:\n            continue\n```\n\n## � Tool Calling Support\n\nThe worker supports OpenAI-compatible tool calling (function calling) with seamless integration to Gemini's function calling capabilities.\n\n### Using Tool Calls\n\nInclude `tools` and optionally `tool_choice` in your request:\n\n```javascript\nconst response = await fetch('/v1/chat/completions', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({\n    model: 'gemini-2.5-pro',\n    messages: [\n      { role: 'user', content: 'What is the weather in New York?' }\n    ],\n    tools: [\n      {\n        type: 'function',\n        function: {\n          name: 'get_weather',\n          description: 'Get weather information for a location',\n          parameters: {\n            type: 'object',\n            properties: {\n              location: { type: 'string', description: 'City name' }\n            },\n            required: ['location']\n          }\n        }\n      }\n    ],\n    tool_choice: 'auto'\n  })\n});\n```\n\n### Tool Choice Options\n\n- `auto`: Let the model decide whether to call a function\n- `none`: Disable function calling\n- `{\"type\": \"function\", \"function\": {\"name\": \"function_name\"}}`: Force a specific function call\n\n## 🛡️ Content Safety Settings\n\nConfigure Gemini's built-in safety filters using environment variables in the dev.vars:\n\n```bash\n# Safety threshold options: BLOCK_NONE, BLOCK_FEW, BLOCK_SOME, BLOCK_ONLY_HIGH, HARM_BLOCK_THRESHOLD_UNSPECIFIED\nGEMINI_MODERATION_HARASSMENT_THRESHOLD=BLOCK_NONE\nGEMINI_MODERATION_HATE_SPEECH_THRESHOLD=BLOCK_NONE  \nGEMINI_MODERATION_SEXUALLY_EXPLICIT_THRESHOLD=BLOCK_SOME\nGEMINI_MODERATION_DANGEROUS_CONTENT_THRESHOLD=BLOCK_ONLY_HIGH\n```\n\n**Safety Categories:**\n- `HARASSMENT`: Content that promotes hatred or violence against individuals/groups\n- `HATE_SPEECH`: Derogatory or demeaning language targeting specific groups\n- `SEXUALLY_EXPLICIT`: Content containing sexual or adult material\n- `DANGEROUS_CONTENT`: Content promoting dangerous or harmful activities\n\n## 📡 API Endpoints\n\n### Base URL\n```\nhttps://your-worker.your-subdomain.workers.dev\n```\n\n### List Models\n```http\nGET /v1/models\n```\n\n**Response:**\n```json\n{\n  \"object\": \"list\",\n  \"data\": [\n    {\n      \"id\": \"gemini-2.5-pro\",\n      \"object\": \"model\",\n      \"created\": 1708976947,\n      \"owned_by\": \"google-gemini-cli\"\n    }\n  ]\n}\n```\n\n### Chat Completions\n```http\nPOST /v1/chat/completions\nContent-Type: application/json\n\n{\n  \"model\": \"gemini-2.5-flash\",\n  \"messages\": [\n    {\n      \"role\": \"system\",\n      \"content\": \"You are a helpful assistant.\"\n    },\n    {\n      \"role\": \"user\", \n      \"content\": \"Hello! How are you?\"\n    }\n  ]\n}\n```\n\n#### Thinking Mode (Real Reasoning)\nFor models that support thinking, you can enable real reasoning from Gemini:\n\n```http\nPOST /v1/chat/completions\nContent-Type: application/json\n\n{\n  \"model\": \"gemini-2.5-pro\",\n  \"messages\": [\n    {\n      \"role\": \"user\", \n      \"content\": \"Solve this math problem step by step: What is 15% of 240?\"\n    }\n  ],\n  \"include_reasoning\": true,\n  \"thinking_budget\": 1024\n}\n```\n\nThe `include_reasoning` parameter enables Gemini's native thinking mode, and `thinking_budget` sets the token limit for reasoning.\n\n**Response (Streaming):**\n```\ndata: {\"id\":\"chatcmpl-123\",\"object\":\"chat.completion.chunk\",\"created\":1708976947,\"model\":\"gemini-2.5-flash\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"Hello\"},\"finish_reason\":null}]}\n\ndata: {\"id\":\"chatcmpl-123\",\"object\":\"chat.completion.chunk\",\"created\":1708976947,\"model\":\"gemini-2.5-flash\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"! I'm\"},\"finish_reason\":null}]}\n\ndata: {\"id\":\"chatcmpl-123\",\"object\":\"chat.completion.chunk\",\"created\":1708976947,\"model\":\"gemini-2.5-flash\",\"choices\":[{\"index\":0,\"delta\":{},\"finish_reason\":\"stop\"}],\"usage\":{\"prompt_tokens\":22,\"completion_tokens\":553,\"total_tokens\":575}}\n\ndata: [DONE]\n```\n\n### Debug Endpoints\n\n#### Check Token Cache\n```http\nGET /v1/debug/cache\n```\n\n#### Test Authentication\n```http\nPOST /v1/token-test\nPOST /v1/test\n```\n\n### Image Support (Vision)\n\nThe worker supports multimodal conversations with images for vision-capable models. Images can be provided as base64-encoded data URLs or as external URLs.\n\n#### Supported Image Formats\n- JPEG, PNG, GIF, WebP\n- Base64 encoded (recommended for reliability)\n- External URLs (may have limitations with some services)\n\n#### Vision-Capable Models\n- `gemini-2.5-pro`\n- `gemini-2.5-flash` \n- `gemini-2.0-flash-001`\n- `gemini-2.0-flash-lite-preview-02-05`\n- `gemini-2.0-pro-exp-02-05`\n\n#### Example with Base64 Image\n```python\nfrom openai import OpenAI\nimport base64\n\n# Encode your image\nwith open(\"image.jpg\", \"rb\") as image_file:\n    base64_image = base64.b64encode(image_file.read()).decode('utf-8')\n\nclient = OpenAI(\n    base_url=\"https://your-worker.workers.dev/v1\",\n    api_key=\"sk-your-secret-api-key-here\"\n)\n\nresponse = client.chat.completions.create(\n    model=\"gemini-2.5-flash\",\n    messages=[\n        {\n            \"role\": \"user\",\n            \"content\": [\n                {\n                    \"type\": \"text\",\n                    \"text\": \"What do you see in this image?\"\n                },\n                {\n                    \"type\": \"image_url\",\n                    \"image_url\": {\n                        \"url\": f\"data:image/jpeg;base64,{base64_image}\"\n                    }\n                }\n            ]\n        }\n    ]\n)\n\nprint(response.choices[0].message.content)\n```\n\n#### Example with Image URL\n```bash\ncurl -X POST https://your-worker.workers.dev/v1/chat/completions \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Authorization: Bearer sk-your-secret-api-key-here\" \\\n  -d '{\n    \"model\": \"gemini-2.5-pro\",\n    \"messages\": [\n      {\n        \"role\": \"user\",\n        \"content\": [\n          {\n            \"type\": \"text\",\n            \"text\": \"Describe this image in detail.\"\n          },\n          {\n            \"type\": \"image_url\",\n            \"image_url\": {\n              \"url\": \"https://example.com/image.jpg\",\n              \"detail\": \"high\"\n            }\n          }\n        ]\n      }\n    ]\n  }'\n```\n\n#### Multiple Images\nYou can include multiple images in a single message:\n```json\n{\n  \"model\": \"gemini-2.5-pro\",\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": [\n        {\n          \"type\": \"text\",\n          \"text\": \"Compare these two images.\"\n        },\n        {\n          \"type\": \"image_url\",\n          \"image_url\": {\n            \"url\": \"data:image/jpeg;base64,...\"\n          }\n        },\n        {\n          \"type\": \"image_url\",\n          \"image_url\": {\n            \"url\": \"data:image/png;base64,...\"\n          }\n        }\n      ]\n    }\n  ]\n}\n```\n\n### Debug Commands\n\n```bash\n# Check KV cache status\ncurl https://your-worker.workers.dev/v1/debug/cache\n\n# Test authentication only\ncurl -X POST https://your-worker.workers.dev/v1/token-test\n\n# Test full flow\ncurl -X POST https://your-worker.workers.dev/v1/test\n```\n\n\n## 🏗️ How It Works\n\n```mermaid\ngraph TD\n    A[Client Request] --\u003e B[Cloudflare Worker]\n    B --\u003e C{Token in KV Cache?}\n    C --\u003e|Yes| D[Use Cached Token]\n    C --\u003e|No| E[Check Environment Token]\n    E --\u003e F{Token Valid?}\n    F --\u003e|Yes| G[Cache \u0026 Use Token]\n    F --\u003e|No| H[Refresh Token]\n    H --\u003e I[Cache New Token]\n    D --\u003e J[Call Gemini API]\n    G --\u003e J\n    I --\u003e J\n    J --\u003e K[Stream Response]\n    K --\u003e L[OpenAI Format]\n    L --\u003e M[Client Response]\n```\n\nThe worker acts as a translation layer, converting OpenAI API calls to Google's Code Assist API format while managing OAuth2 authentication automatically.\n\n## 🤝 Contributing\n\n1. Fork the repository\n2. Create a feature branch\n3. Make your changes\n4. Test thoroughly\n5. Submit a pull request\n\n## 📄 License\n\nThis project is licensed under the MIT License - see the LICENSE file for details.\n\n## 🙏 Acknowledgments\n\n- Inspired by the official [Google Gemini CLI](https://github.com/google-gemini/gemini-cli)\n- Built on [Cloudflare Workers](https://workers.cloudflare.com/)\n- Uses [Hono](https://hono.dev/) web framework\n\n---\n\n**⚠️ Important**: This project uses Google's Code Assist API which may have usage limits and terms of service. Please ensure compliance with Google's policies when using this worker.\n\n\n[![Star History Chart](https://api.star-history.com/svg?repos=GewoonJaap/gemini-cli-openai\u0026type=Date)](https://www.star-history.com/#GewoonJaap/gemini-cli-openai\u0026Date)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgewoonjaap%2Fgemini-cli-openai","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgewoonjaap%2Fgemini-cli-openai","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgewoonjaap%2Fgemini-cli-openai/lists"}