Skip to content

Feature Request: Support for Generic/Custom Endpoints with API Key Authentication #3168

Closed

Description

@coreglass

Hello OpenCode Team,

First of all, thank you for creating this promising tool. I'm writing to suggest a feature that I believe is critical for wider adoption and flexibility.

Currently, connecting to an AI model in OpenCode requires logging into a specific, pre-configured service provider (e.g., OpenAI, Azure, Google). This workflow is restrictive and doesn't support a large and growing number of use cases where developers only have an API endpoint and an API key.

This is in stark contrast to tools like Postman, Insomnia, or other API clients, where one can interact with any HTTP endpoint simply by providing the URL, headers (like Authorization: Bearer <API_KEY>), and a body.

Why This is a Major Limitation (The "Disaster")
The current provider-specific login model is a significant barrier for anyone using:

Self-Hosted Models: Developers using open-source models via frameworks like Ollama, vLLM, LocalAI, or custom inference servers. These services expose an OpenAI-compatible endpoint but have no "login with provider" flow.
Proxy Services: Many organizations and developers use proxy layers like LiteLLM, Cloudflare AI Gateway, or internal company gateways. These proxies manage routing, logging, and key management, providing a unified endpoint and key for various underlying models. OpenCode currently cannot connect to these.
Smaller or Niche Cloud Providers: The AI ecosystem is vast. Many smaller providers offer competitive services but aren't integrated into OpenCode's login list. They almost always provide a standard API key and endpoint.
Any OpenAI-Compatible API: Many new services and models are launching with an API that mirrors OpenAI's structure. The ability to simply point OpenCode to a new base URL and provide a key would make it instantly compatible with this entire ecosystem.
Proposed Solution
I propose adding a "Custom" or "Generic Provider" option to the model configuration.

When a user selects this option, the UI should simply ask for two main fields:

Endpoint Base URL: (e.g., https://api.example.com/v1)
API Key: (This would typically be sent in the Authorization: Bearer <API_KEY> header, as is standard practice).
This would provide the necessary flexibility to connect to virtually any RESTful AI service, dramatically increasing the utility of OpenCode.

Activity

  1. github-actions commented on Oct 14, 2025

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    Feel free to ignore if none of these address your specific case.

  2. Laxsilence commented on Oct 14, 2025

    @Laxsilence

    I agree, because our parent company has deployed Google Vertex, but it goes through network management and proxies. The way it's provided to subsidiaries is by accessing an internal endpoint using an API key, and the API key must be included in the HTTP headers. Below is an python example of the request code. Now, I'm unsure how to configure this in OpenCode:

    import requests
    ​
    ENDPOINT = "https://**********"
    API_KEY = "**********"
    ​
    headers = {
        "Content-type": "application/json",
        "api-key": API_KEY
    }
    ​
    messages = {
        "contents": [
            {
                "role": "user",
                "parts": [
                    {
                        "text": "What do you want to play today?"
                    }
                ]
            }
        ]
        ## To use Thinking with the 2.5 Flash model, add the following parameters:
        # ,"generationConfig": {
        #     "thinkingConfig": {
        #         "thinkingBudget": 500,  # Use up to 500 tokens for thinking.
        #         "include_thoughts": True # Include thinking contents
        #     }
        # }
    }
    ​
    response = requests.post(ENDPOINT, headers=headers, json=messages)
    print(response.json())
    ​
  3. OpeOginni commented on Oct 14, 2025

    @OpeOginni
    Contributor

    @coreglass Please let me know if I didnt quite get your request, but from what I understand you want to be able to use Custom Models based on a custom endpoint and api key. This is currently supported on OpenCode by defining the provider and models in the opencode.json. https://opencode.ai/docs/models/#configure-models.

    Here is an example where I am running a model on Ollama and adding to Opencode

    {
        "$schema": "https://opencode.ai/config.json",
        "provider": {
          "ollama": {
            "npm": "@ai-sdk/openai-compatible",
            "name": "Ollama (local)",
            "options": {
              "baseURL": "http://localhost:11434/v1",
              "apiKey": "<api-key>"
            },
            "models": {
              "llama3.2:latest": {
                "name": "Llama 3.2"
              },
              "another-supported-model": {
                "name": "Another Model"
              }
            }
          }
        }
    }

    For the npm field you need to pick a packe on the ai-sdk that works with the provider you are using AI-SDK Providers

  4. OpeOginni commented on Oct 14, 2025

    @OpeOginni
    Contributor

    @Laxsilence I am not sure if there is another way to pass in apikey creds as a different header but the Authorization: Bearer <API_KEY> one. Maybe @rekram1-node can answer if he has an idea, if not id be happy to make a pr to support it.

  5. rekram1-node commented on Oct 14, 2025

    @rekram1-node
    Contributor

    You can pass any headers you want like so:

    {
      "$schema": "https://opencode.ai/config.json",
      "provider": {
        "my-custom-provider": {
          "options": {
            "headers": {
              "x-api-key": "{env:MY_API_KEY}"
            }
          }
        }
      }
    }
    
  6. self-assigned this
    on Oct 14, 2025
  7. rekram1-node commented on Oct 14, 2025

    @rekram1-node
    Contributor

    @coreglass I think this already exists?

    I propose adding a "Custom" or "Generic Provider" option to the model configuration.

    Image

    It is called "Other" instead of "Custom" tho...

  8. Laxsilence commented on Oct 15, 2025

    @Laxsilence

    You can pass any headers you want like so:

    {
      "$schema": "https://opencode.ai/config.json",
      "provider": {
        "my-custom-provider": {
          "options": {
            "headers": {
              "x-api-key": "{env:MY_API_KEY}"
            }
          }
        }
      }
    }
    

    @rekram1-node Thank you for your reply. I tried using the method you provided, and here is my config.json:

    {
      "$schema": "https://opencode.ai/config.json",
      "theme": "tokyonight",
      "provider": {
        "my-custom-provider": {
          "name": "my AI",
          "options": {
            "baseURL": "https://xxx/ai-foundation/chat-ai/gemini/pro-thinking",
            "headers": {
                "Content-type": "application/json",
                "x-api-key": "EQxxxd"
            }
          },
          "models": {
            "gemini-2.5-pro": {
              "name": "Gemini 2.5 Pro",
              "reasoning": true,
              "tool_call": true,
              "temperature": true
            }
          }
        }
      },
      "model": "my AI/Gemini 2.5 Pro",
      "autoshare": false,
      "autoupdate": true
    }

    However, after switching to my model in the opencode, the following error occurred when making the call:

    Image
  9. rekram1-node commented on Oct 15, 2025

    @rekram1-node
    Contributor

    @coreglass your config is missing a few pieces, this guide may be helpful: https://opencode.ai/docs/providers/#custom-provider

    What's primarily missing is that you aren't specifying the package to use for your provider, normally this would be the openai compatible one:

          "npm": "@ai-sdk/openai-compatible",
    

    Here is an example config from the docs:

    {
      "$schema": "https://opencode.ai/config.json",
      "provider": {
        "myprovider": {
          "npm": "@ai-sdk/openai-compatible",
          "name": "My AI ProviderDisplay Name",
          "options": {
            "baseURL": "https://api.myprovider.com/v1"
          },
          "models": {
            "my-model-name": {
              "name": "My Model Display Name"
            }
          }
        }
      }
    }
    
  10. coreglass commented on Oct 15, 2025

    @coreglass
    Author

    @rekram1-node
    Following your guidance, we initially used the configuration below:

    {
      "$schema": "https://opencode.ai/config.json",
      "theme": "tokyonight",
      "provider": {
        "myAI": {
          "npm": "@ai-sdk/openai-compatible",
          "name": "myAI",
          "options": {
            "baseURL": "https://xxx/ai-foundation/chat-ai/gemini/pro-thinking",
            "apiKey": "EQxxxd"
          },
          "models": {
            "gemini-2.5-pro": {
              "name": "Gemini 2.5 Pro",
              "reasoning": true,
              "tool_call": true,
              "temperature": true
            }
          }
        }
      },
      "autoshare": false,
      "autoupdate": true
    }
    

    Image
    Judging from the logs, it appears we successfully reached our custom model's service endpoint. However, a request body validation failure occurred. Our hypothesis is that this is due to a mismatch: opencode.ai was sending a request formatted for the OpenAI API (as specified by "npm": "@ai-sdk/openai-compatible") to our endpoint, which expects a Gemini-native format.

    To resolve this, we adjusted the configuration to use what we believed was the correct Google provider:

    {
      "$schema": "https://opencode.ai/config.json",
      "theme": "tokyonight",
      "provider": {
        "myAI": {
          "npm": "@ai-sdk/google-vertex",
          "name": "myAI",
          "options": {
            "baseURL": "https://xxx/ai-foundation/chat-ai/gemini/pro-thinking",
            "apiKey": "EQxxxd"
          },
          "models": {
            "gemini-2.5-pro": {
              "name": "Gemini 2.5 Pro",
              "reasoning": true,
              "tool_call": true,
              "temperature": true
            }
          }
        }
      },
      "autoshare": false,
      "autoupdate": true
    }
    
    

    Image

    This change, however, resulted in more severe errors that seem to require additional, platform-specific parameters (likely for Google Cloud Vertex AI), which has left us confused.

    Our objective is straightforward: we simply want opencode.ai to send requests in a format that our Gemini-based service can understand and process correctly. Is there a better or more direct way to configure this?

  11. rekram1-node commented on Oct 15, 2025

    @rekram1-node
    Contributor

    @coreglass

    the one confusing thing here is there is a google vertext but there is also google ai studio

    have you tried using:

    @ai-sdk/google

    instead of google vertex?

  12. rekram1-node commented on Oct 15, 2025

    @rekram1-node
    Contributor

    also does the url need to include the model and then the model needs to be sent in request body too? Unfamiliar with your system but that thing is sticking out to me as potentially a problem

  13. Laxsilence commented on Oct 15, 2025

    @Laxsilence

    @rekram1-node @coreglass

    Thank you both for your assistance. Here's my current progress:

    I've written a Python service primarily designed to forward requests. Currently, the URL configured in Opencode is set to localhost:port. When a request is initiated from Opencode to my Python service, I forward it to an endpoint provided by the company. The logs confirm that my Python service does receive responses from the company's AI, but for some reason, these responses aren't being reflected on the Opencode interface. Below is my config.json from Opencode:

    {
      "$schema": "https://opencode.ai/config.json",
      "theme": "tokyonight",
      "provider": {
        "myAI": {
            "npm": "@ai-sdk/google",
          "name": "myAI",
          "options": {
            "baseURL": "http://localhost:18080",
            "apiKey": "EQxxx"
          },
          "models": {
            "gemini-2.5-pro": {
              "name": "Gemini 2.5 Pro",
              "reasoning": true,
              "tool_call": true,
              "temperature": true
            }
          }
        }
      },
      "model": "myAI/Gemini 2.5 Pro",
      "autoshare": false,
      "autoupdate": true
    } 

    Here's the log I receive when making a call through the Opencode interface:

    PS C:\Users\liuj> opencode run "hello" --print-logs --model myAI/gemini-2.5-pro
    INFO  2025-10-15T05:29:42 +83ms service=default version=0.14.7 args=["run","hello","--print-logs","--model","myAI/gemini-2.5-pro"] opencode
    INFO  2025-10-15T05:29:42 +1ms service=project directory=C:\Users\liuj fromDirectory
    INFO  2025-10-15T05:29:42 +9ms service=config path=C:\Users\liuj\.config\opencode\config.json loading
    INFO  2025-10-15T05:29:42 +6ms service=config path=C:\Users\liuj\.config\opencode\opencode.json loading
    INFO  2025-10-15T05:29:42 +0ms service=config path=C:\Users\liuj\.config\opencode\opencode.jsonc loading
    INFO  2025-10-15T05:29:42 +4ms service=plugin path=opencode-copilot-auth@0.0.3 loading plugin
    INFO  2025-10-15T05:29:42 +2ms service=plugin path=opencode-anthropic-auth@0.0.2 loading plugin
    INFO  2025-10-15T05:29:42 +45ms service=bus type=* subscribing
    INFO  2025-10-15T05:29:42 +0ms service=bus type=session.updated subscribing
    INFO  2025-10-15T05:29:42 +0ms service=bus type=message.updated subscribing
    INFO  2025-10-15T05:29:42 +0ms service=bus type=message.part.updated subscribing
    INFO  2025-10-15T05:29:42 +1ms service=format init
    INFO  2025-10-15T05:29:42 +0ms service=bus type=file.edited subscribing
    INFO  2025-10-15T05:29:42 +3ms service=session id=ses_619a78374ffe7o0x1iTK74jFRg version=0.14.7 projectID=global directory=C:\Users\liuj title=New session - 2025-10-15T05:29:42.796Z time={"created":1760506182796,"updated":1760506182796} created
    INFO  2025-10-15T05:29:42 +0ms service=lsp serverIds=typescript, vue, eslint, gopls, ruby-lsp, pyright, elixir-ls, zls, csharp, rust, clangd, svelte, jdtls enabled LSP servers
    INFO  2025-10-15T05:29:42 +24ms service=bus type=session.updated publishing
    INFO  2025-10-15T05:29:42 +1ms service=bus type=message.part.updated subscribing
    INFO  2025-10-15T05:29:42 +1ms service=bus type=session.error subscribing
    INFO  2025-10-15T05:29:42 +1ms service=session.prompt session=ses_619a78374ffe7o0x1iTK74jFRg prompt
    INFO  2025-10-15T05:29:42 +19ms service=bus type=message.updated publishing
    INFO  2025-10-15T05:29:42 +5ms service=bus type=message.part.updated publishing
    INFO  2025-10-15T05:29:42 +8ms service=bus type=session.updated publishing
    INFO  2025-10-15T05:29:42 +3ms service=models.dev file={} refreshing
    INFO  2025-10-15T05:29:42 +10ms service=provider init
    INFO  2025-10-15T05:29:42 +29ms service=provider providerID=zhipuai found
    INFO  2025-10-15T05:29:42 +0ms service=provider providerID=zhipuai-coding-plan found
    INFO  2025-10-15T05:29:42 +0ms service=provider providerID=opencode found
    INFO  2025-10-15T05:29:42 +0ms service=provider providerID=myAI found
    INFO  2025-10-15T05:29:42 +0ms service=provider providerID=myAI modelID=gemini-2.5-pro getModel
    INFO  2025-10-15T05:29:42 +1ms service=provider status=started providerID=myAI getSDK
    INFO  2025-10-15T05:29:43 +120ms service=provider status=completed duration=120 providerID=myAI getSDK
    INFO  2025-10-15T05:29:43 +1ms service=provider providerID=myAI modelID=gemini-2.5-pro found
    INFO  2025-10-15T05:29:43 +0ms service=session.prompt session=ses_619a78374ffe7o0x1iTK74jFRg sessionID=ses_619a78374ffe7o0x1iTK74jFRg locking
    INFO  2025-10-15T05:29:43 +71ms service=bus type=message.updated publishing
    INFO  2025-10-15T05:29:43 +6ms service=session.prompt session=ses_619a78374ffe7o0x1iTK74jFRg process
    INFO  2025-10-15T05:29:43 +2ms service=session.prompt session=ses_619a78374ffe7o0x1iTK74jFRg type=start part
    INFO  2025-10-15T05:29:59 +16298ms service=bus type=session.updated publishing
    INFO  2025-10-15T05:30:02 +2787ms service=session.prompt session=ses_619a78374ffe7o0x1iTK74jFRg type=start-step part
    INFO  2025-10-15T05:30:02 +7ms service=bus type=message.part.updated publishing
    INFO  2025-10-15T05:30:02 +1ms service=session.prompt session=ses_619a78374ffe7o0x1iTK74jFRg type=finish-step part
    INFO  2025-10-15T05:30:02 +3ms service=bus type=message.part.updated publishing
    INFO  2025-10-15T05:30:02 +0ms service=bus type=message.updated publishing
    INFO  2025-10-15T05:30:02 +1ms service=session.prompt session=ses_619a78374ffe7o0x1iTK74jFRg type=finish part
    INFO  2025-10-15T05:30:02 +0ms service=bus type=message.updated publishing
    INFO  2025-10-15T05:30:02 +11ms service=bus type=message.updated publishing
    INFO  2025-10-15T05:30:02 +1ms service=bus type=message.updated publishing
    INFO  2025-10-15T05:30:02 +1ms service=session.compaction pruning
    INFO  2025-10-15T05:30:02 +0ms service=session.prompt session=ses_619a78374ffe7o0x1iTK74jFRg sessionID=ses_619a78374ffe7o0x1iTK74jFRg unlocking
    
    INFO  2025-10-15T05:30:02 +17ms service=bus type=session.idle publishing
    INFO  2025-10-15T05:30:02 +6ms service=session.compaction pruned=0 total=0 found
    

    And here's the content returned by my Python service in response:

    2025-10-15 13:29:59,382 - INFO - [2025-10-15 13:29:59.382] [127.0.0.1:53282] Sending request to https://api.myAI.com/ai-foundation/chat-ai/gemini/pro-thinking:streamGenerateContent...
    2025-10-15 13:30:02,171 - INFO - [2025-10-15 13:30:02.171] [127.0.0.1:53282] Response received in 2.789 seconds
    2025-10-15 13:30:02,171 - INFO - [2025-10-15 13:30:02.171] [127.0.0.1:53282] Response Status: 200
    2025-10-15 13:30:02,171 - INFO - [2025-10-15 13:30:02.171] [127.0.0.1:53282] Response Reason: OK
    2025-10-15 13:30:02,171 - INFO - [2025-10-15 13:30:02.171] [127.0.0.1:53282] --- Response Headers ---
    2025-10-15 13:30:02,172 - INFO - [2025-10-15 13:30:02.171] [127.0.0.1:53282]   content-type: application/json; charset=UTF-8
    2025-10-15 13:30:02,172 - INFO - [2025-10-15 13:30:02.172] [127.0.0.1:53282]   vary: X-Origin,Referer,Origin,Accept-Encoding
    2025-10-15 13:30:02,172 - INFO - [2025-10-15 13:30:02.172] [127.0.0.1:53282]   date: Wed, 15 Oct 2025 05:30:02 GMT
    2025-10-15 13:30:02,172 - INFO - [2025-10-15 13:30:02.172] [127.0.0.1:53282]   x-xss-protection: 0
    2025-10-15 13:30:02,172 - INFO - [2025-10-15 13:30:02.172] [127.0.0.1:53282]   x-frame-options: SAMEORIGIN
    2025-10-15 13:30:02,172 - INFO - [2025-10-15 13:30:02.172] [127.0.0.1:53282]   x-content-type-options: nosniff
    2025-10-15 13:30:02,172 - INFO - [2025-10-15 13:30:02.172] [127.0.0.1:53282]   accept-ranges: none
    2025-10-15 13:30:02,172 - INFO - [2025-10-15 13:30:02.172] [127.0.0.1:53282]   x-accel-buffering: no
    2025-10-15 13:30:02,172 - INFO - [2025-10-15 13:30:02.172] [127.0.0.1:53282]   via: 1.1 google
    2025-10-15 13:30:02,172 - INFO - [2025-10-15 13:30:02.172] [127.0.0.1:53282]   Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
    2025-10-15 13:30:02,172 - INFO - [2025-10-15 13:30:02.172] [127.0.0.1:53282]   Connection: close
    2025-10-15 13:30:02,172 - INFO - [2025-10-15 13:30:02.172] [127.0.0.1:53282]   Transfer-Encoding: chunked
    2025-10-15 13:30:02,172 - INFO - [2025-10-15 13:30:02.172] [127.0.0.1:53282] --- End Response Headers ---
    2025-10-15 13:30:02,174 - INFO - [2025-10-15 13:30:02.174] [127.0.0.1:53282] --- Response Body (length: 1055) ---
    2025-10-15 13:30:02,174 - INFO - [2025-10-15 13:30:02.174] [127.0.0.1:53282] [{
      "candidates": [
        {
          "content": {
            "role": "model",
            "parts": [
              {
                "text": "Hello! What can I help you with today?",
                "thoughtSignature": "CiQB4/H/XntE0+TR62eju/lLr1nECKLq3j8fIFaztA2v5GHuxlMKZgHj8f9eMLFjPha11BG1DxN2BLhZDNWRV4Hfnq57k1KhKZW72NotZFL1CBmnvued2ORige1b0DOruGxiuUjsqCuFsyuC5kuOPMfo+OJ+K4ffYvl9ZxiT6STECZ0KcOIXH4lSFA//FQoyAePx/16tPmWvoSlBvSBwrRJf1uRC3IYRNhYq447oRry/QrjccdLhosSfFicDi39M2K8="
              }
            ]
          },
          "finishReason": "STOP"
        }
      ],
      "usageMetadata": {
        "promptTokenCount": 10155,
        "candidatesTokenCount": 10,
        "totalTokenCount": 10189,
        "trafficType": "ON_DEMAND",
        "promptTokensDetails": [
          {
            "modality": "TEXT",
            "tokenCount": 10155
          }
        ],
        "candidatesTokensDetails": [
          {
            "modality": "TEXT",
            "tokenCount": 10
          }
        ],
        "thoughtsTokenCount": 24
      },
      "modelVersion": "gemini-2.5-pro",
      "createTime": "2025-10-15T05:30:00.652696Z",
      "responseId": "WDHvaJjrJ9yp0u8Po-vOiA8"
    }
    ]
    2025-10-15 13:30:02,175 - INFO - [2025-10-15 13:30:02.175] [127.0.0.1:53282] --- End Response Body ---
    2025-10-15 13:30:02,175 - INFO - [2025-10-15 13:30:02.175] [127.0.0.1:53282] Sending response to client: 200
    2025-10-15 13:30:02,175 - INFO - 127.0.0.1 - "POST /models/gemini-2.5-pro:streamGenerateContent?alt=sse HTTP/1.1" 200 -
    2025-10-15 13:30:02,175 - INFO - [2025-10-15 13:30:02.175] [127.0.0.1:53282] Response sent to client (1055 bytes)
    2025-10-15 13:30:02,175 - INFO - [2025-10-15 13:30:02.175] [127.0.0.1:53282] === REQUEST END [20251015_132959_378570] - Success ===
    
  14. rekram1-node commented on Oct 15, 2025

    @rekram1-node
    Contributor

    Are you sending that response all at once or does your proxy server correctly handle streams?

  15. Laxsilence commented on Oct 15, 2025

    @Laxsilence

    Are you sending that response all at once or does your proxy server correctly handle streams?

    It's acceptable to call a non-streaming interface, and the session name can be properly returned and displayed on the interface. However, subsequent sessions use streaming calls. My Python code invokes the company's response stream interface and presumably receives all responses at once. Are you suggesting that, since OpenCode calls the response stream interface, I need to relay the content returned by the company's AI interface back to OpenCode in a streaming format within my Python code?

    Image
  16. rekram1-node commented on Oct 15, 2025

    @rekram1-node
    Contributor

    well the fact that the title generation actually works is a good sign, im kinda surprised that it would work and nothing else is? (unless you are authenticated to another provider)

    all i was trying to say is to make sure that the data is being transmitted the exact same way google transmitts it because that google package is expecting a specific format

  17. Laxsilence commented on Oct 15, 2025

    @Laxsilence

    well the fact that the title generation actually works is a good sign, im kinda surprised that it would work and nothing else is? (unless you are authenticated to another provider)

    all i was trying to say is to make sure that the data is being transmitted the exact same way google transmitts it because that google package is expecting a specific format

    Okay, I need to check the differences in the data format returned compared to Google's data format. Our company documentation mentions that the AI deployment uses Google Vertex AI, but I can't obtain Google authentication and region details. The company only provided the API and key. So, when I set the provider to google-vertex, it directly throws an error. Now, using "npm": "@ai-sdk/google" might lead to potential inconsistencies in the data format.

  18. rekram1-node commented on Oct 15, 2025

    @rekram1-node
    Contributor

    wait just to confirm are you guys Coreglass and Laxsilence working on the same thing I saw "we" mentioned a few times, I think my recommendations may be conflicting between the two if these setups are different. I misunderstood and thought yall were doing same thing for whatever reason

    If you are using google vertex you shouldn't use @ai-sdk/google you need to use the vertex one assuming you are sending requests back in vertex format.

    Maybe if you show error I can help better?

  19. Laxsilence commented on Oct 16, 2025

    @Laxsilence

    @rekram1-node Yes, we are working on one thing.

    Our parent company has deployed Google Vertex, but when we use @ai-sdk/google-vertex, we encounter an error indicating that we need to configure Google authentication and region information. Our subsidiary does not have the permissions to obtain these details. Currently, all we have is an internal network access endpoint and a personal API key—nothing else. Given only these two pieces of information, how can we configure google-vertex in opencode?

    Currently, our progress is as follows: we have configured @ai-sdk/google.
    #3168 (comment)

  20. rekram1-node commented on Oct 16, 2025

    @rekram1-node
    Contributor

    Can you give me a bit more information about the errors you see? if the two of you are working on 1 thing why are the configs different? Also is it openai compatible like the original post says or is it google vertex compatible?

    This resource may be useful: https://ai-sdk.dev/providers/ai-sdk-providers/google-vertex

    If you are using vertex you prolly need @ai-sdk/google-vertex

    if you are using vertex you will need to set these in your provider options:

     project: 'my-project', // optional
      location: 'us-central1', // optional
    

    ex:

    {
      "$schema": "https://opencode.ai/config.json",
      "theme": "tokyonight",
      "provider": {
        "myAI": {
          "npm": "@ai-sdk/google-vertex",
          "name": "myAI",
          "options": {
            "baseURL": "http://localhost:18080",
            "apiKey": "EQxxx",
            "project": "my-project",
            "location": "my-location"
          },
          "models": {
            "gemini-2.5-pro": {
              "name": "Gemini 2.5 Pro",
              "reasoning": true,
              "tool_call": true,
              "temperature": true
            }
          }
        }
      },
      "model": "myAI/Gemini 2.5 Pro",
      "autoshare": false,
      "autoupdate": true
    }
    

    It get's a bit funky here because vertex provider will try to cram those values (project, location) into a url like so (see here:https://cloud.google.com/vertex-ai/generative-ai/docs/reference/rest/v1/projects.locations.endpoints.chat/completions) :

    endpoint: projects/{project}/locations/{location}/endpoints/{endpoint}

    url: https://aiplatform.googleapis.com/v1/{endpoint}/chat/completions

  21. coreglass commented on Oct 17, 2025

    @coreglass
    Author

    @rekram1-node @OpeOginni @palmamartin @Laxsilence
    Thank you all very much for the discussion and suggestions. I want to express my sincere gratitude. I have now resolved the issue I was facing, and I am sharing the entire process in the hope that it will be helpful to others.

    Integrating a Self-Hosted Gemini 2.5 Pro with OpenCode via a Custom Adapter
    Here's a breakdown of the technical journey and the challenges I overcame while integrating a custom-hosted Gemini model with OpenCode.

    My Environment:

    Client: OpenCode running on Windows.
    Model: A self-hosted instance of Gemini 2.5 Pro on Google Vertex AI, accessible only through a specific API key and an endpoint URL (likely due to a proxy).

    The Initial Problem: Configuration Mismatch

    Initially, I was unable to configure OpenCode using its built-in model providers. Even the "Vertex" option was not viable because it required a projectId and other credentials that I did not have; my access was limited to just an API key and an endpoint.

    I then attempted to use the "Custom Provider" option, which allows for configuration with an API key and a baseUrl. However, this also failed. By inspecting the application logs, I discovered a crucial detail about OpenCode's behavior: for a single user prompt, it sends two distinct requests.

    First Request (Non-streaming): To generate a title for the current chat session.
    Second Request (Streaming): To get the actual response to the user's query.
    Figure A:
    Image
    The core issue, as illustrated in the attached "Figure A," was that OpenCode appends different sub-paths to the configured baseUrl for each of these requests. This meant the resulting URLs did not match the single, fixed endpoint I was required to use, causing all API calls to fail.

    The Solution: A Java-based Adapter

    To solve this, I decided to build an adapter using Java, which is my language of expertise. This adapter would act as a middleman:

    It would receive the incoming requests from OpenCode.
    It would then transform and forward these requests to the correct Google Vertex Gemini 2.5 Pro endpoint.
    Finally, it would receive the response from Gemini and return it to OpenCode in the expected format.
    This approach proved to be sound in principle, but it introduced a new challenge: handling streaming responses.

    Implementing Streaming with Server-Sent Events (SSE)

    How could I return a streaming response from my adapter back to OpenCode? By digging into OpenCode's source code, I found that it expects streaming data to be formatted as Server-Sent Events (SSE).

    With this knowledge, I implemented the following logic in my Java adapter:

    Receive the raw streaming response from the Gemini endpoint.
    Parse each incoming JSON chunk to extract the content from the parts array.
    Wrap this content in the SSE data: format and stream it back to OpenCode.
    The moment of truth came, and it worked perfectly! The logs from my Java adapter confirmed that the entire request-response cycle was functioning correctly.

    Pitfalls and Key Discoveries

    Throughout this process, I encountered a couple of significant hurdles that are worth noting:

    Incorrect NPM Package: I initially spent a lot of time trying to make the npm: "@ai-sdk/google-vertex" package work, without success. The solution was to switch to npm: "@ai-sdk/google". I suspect this is because the response structure from my proxied model endpoint aligns more closely with the standard Google AI API rather than the full Vertex AI SDK's expectations.

      "theme": "tokyonight",
      "provider": {
        "ollama": {
            "npm": "@ai-sdk/google",
          "name": "MyAI",
          "options": {
            "baseURL": "http://localhost:11434",
          },
          "models": {
            "gemini-2.5-pro": {
              "name": "Gemini 2.5 Pro",
              "reasoning": true,
              "tool_call": true,
              "temperature": true
            }
          }
        }
      },
      "autoshare": false,
      "autoupdate": true
    }
    

    The finishReason "Aha!" Moment: Gemini's API has a peculiar behavior: it always returns finishReason: 'STOP', even when the response contains a tool call that needs to be executed. At first, I thought this was an issue with the model and I manually changed the finishReason to function_call within my adapter. This led to incorrect behavior where OpenCode would execute a single tool call and then halt, requiring me to manually prompt it to continue.

    The breakthrough came when I revisited the source code. I discovered that OpenCode has special handling specifically for Gemini. It is designed to accept finishReason: 'STOP' and then internally inspects the response payload. If it finds tool call information, it correctly processes it as a function_call. Realizing this was the crucial insight that solved the final piece of the puzzle.

    Conclusion

    In the end, I successfully integrated my custom Gemini model with OpenCode. While it required building an intermediate adapter, the process of debugging and reverse-engineering the communication flow was an invaluable learning experience.

  22. rekram1-node commented on Oct 17, 2025

    @rekram1-node
    Contributor

    lets go yay thats awesome

  23. J0o1ey commented on Jan 27, 2026

    @J0o1ey

    @rekram1-node hellorekram~,I meet the same issue.
    If OpenCode can add self-hosted Google Vertex endpoint now?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions