diff --git a/docs/concepts/vendors.md b/docs/concepts/vendors.md index e214b5d..b82c2d7 100644 --- a/docs/concepts/vendors.md +++ b/docs/concepts/vendors.md @@ -24,7 +24,7 @@ Used with `agent.with_llm()` for the cascading flow (ASR → LLM → TTS). | `OpenAI` | OpenAI | `model` for Agora-managed global models; `api_key`, `base_url`, `model` for BYOK | | `AzureOpenAI` | Azure OpenAI | `api_key`, `model`, `endpoint`, `deployment_name` | | `Anthropic` | Anthropic | `api_key`, `model`, `url`, `headers`, `max_tokens` | -| `Gemini` | Google Gemini | `api_key`, `model` | +| `Gemini` | Google Gemini | `api_key`, `model`; optional `url` | | `Groq` | Groq | `api_key`, `model`, `base_url` | | `VertexAILLM` | Google Vertex AI | `api_key`, `model`, `project_id`, `location` | | `AmazonBedrock` | Amazon Bedrock | `access_key`, `secret_key`, `region`, `model` | diff --git a/docs/reference/vendors.md b/docs/reference/vendors.md index d1d96c2..697109e 100644 --- a/docs/reference/vendors.md +++ b/docs/reference/vendors.md @@ -176,6 +176,7 @@ llm = Anthropic( |---|---|---|---|---| | `api_key` | `str` | Yes | — | Google AI API key | | `model` | `str` | Yes | — | Model name | +| `url` | `str` | No | `None` | Custom Gemini `streamGenerateContent` URL. When omitted, the SDK constructs one from `model` and `api_key`. | | `temperature` | `float` | No | `None` | Sampling temperature (0.0–2.0) | | `top_p` | `float` | No | `None` | Nucleus sampling (0.0–1.0) | | `top_k` | `int` | No | `None` | Top-k sampling | @@ -194,7 +195,10 @@ llm = Anthropic( ```python from agora_agent import Gemini -llm = Gemini(api_key='your-google-key', model='gemini-2.0-flash-exp') +llm = Gemini( + api_key='your-google-key', + model='gemini-2.0-flash-exp', +) ``` ### Other LLM vendors diff --git a/src/agora_agent/agentkit/vendors/llm.py b/src/agora_agent/agentkit/vendors/llm.py index bfcb201..3e31992 100644 --- a/src/agora_agent/agentkit/vendors/llm.py +++ b/src/agora_agent/agentkit/vendors/llm.py @@ -304,8 +304,10 @@ def to_config(self) -> Dict[str, Any]: params["max_output_tokens"] = self.options.max_output_tokens config: Dict[str, Any] = { - "url": self.options.url or "https://generativelanguage.googleapis.com/v1beta/models", - "api_key": self.options.api_key, + "url": self.options.url or ( + f"https://generativelanguage.googleapis.com/v1beta/models/" + f"{self.options.model}:streamGenerateContent?alt=sse&key={self.options.api_key}" + ), "params": params, "style": "gemini", "input_modalities": self.options.input_modalities or ["text"], @@ -394,7 +396,9 @@ def to_config(self) -> Dict[str, Any]: f"{self.options.project_id}/locations/{self.options.location}/" f"publishers/google/models/{self.options.model}:streamGenerateContent?alt=sse" ) - return Gemini(**options).to_config() + config = Gemini(**options).to_config() + config["api_key"] = self.options.api_key + return config class AmazonBedrockOptions(BaseModel): diff --git a/tests/custom/test_llm_vendors.py b/tests/custom/test_llm_vendors.py index 0d07cf4..b9cb90a 100644 --- a/tests/custom/test_llm_vendors.py +++ b/tests/custom/test_llm_vendors.py @@ -69,6 +69,32 @@ def test_vertex_ai_llm_includes_project_routing() -> None: assert "location" not in config.get("params", {}) +def test_vertex_ai_llm_preserves_explicit_url() -> None: + config = VertexAILLM( + api_key="vertex-token", + model="gemini-2.0-flash", + project_id="project", + location="us-central1", + url="https://vertex.example.com/custom-endpoint", + ).to_config() + + assert config["url"] == "https://vertex.example.com/custom-endpoint" + assert config["api_key"] == "vertex-token" + assert config["params"]["model"] == "gemini-2.0-flash" + + +def test_gemini_constructs_url_from_api_key_and_model() -> None: + config = Gemini( + api_key="google-key", + model="gemini-2.0-flash", + ).to_config() + + assert config["url"] == "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:streamGenerateContent?alt=sse&key=google-key" + assert "api_key" not in config + assert config["style"] == "gemini" + assert config["params"]["model"] == "gemini-2.0-flash" + + def test_amazon_bedrock_serializes_as_bedrock_style() -> None: config = AmazonBedrock( access_key="aws-access", diff --git a/tests/custom/test_request_body.py b/tests/custom/test_request_body.py index 083ed0a..689dbfa 100644 --- a/tests/custom/test_request_body.py +++ b/tests/custom/test_request_body.py @@ -898,9 +898,15 @@ def test_byok_anthropic_llm_params() -> None: def test_byok_gemini_llm_params() -> None: - agent = Agent(test_client()).with_llm(Gemini(api_key="gemini-key", model="gemini-2.0-flash")) + agent = Agent(test_client()).with_llm( + Gemini( + api_key="gemini-key", + model="gemini-2.0-flash", + ) + ) props = build_properties(agent, allow_missing={"asr", "tts"}) - assert props["llm"]["api_key"] == "gemini-key" + assert "api_key" not in props["llm"] + assert props["llm"]["url"] == "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:streamGenerateContent?alt=sse&key=gemini-key" assert props["llm"]["style"] == "gemini" assert props["llm"]["params"]["model"] == "gemini-2.0-flash"