diff --git a/README.md b/README.md index 03f12d25f..8a7c57d21 100644 --- a/README.md +++ b/README.md @@ -251,7 +251,7 @@ __Note__: Support for individual models is dependent on the specific inference p | Azure | gpt-5-chat, gpt-4.1, gpt-4.1-mini, gpt-4.1-nano, o1-mini | No or limited | remote::azure | | | VertexAI | google/gemini-3.5-flash-lite [^1] | Yes | remote::vertexai | [1](examples/vertexai-run.yaml) | | WatsonX | openai/gpt-oss-120b | Yes | remote::watsonx | [1](examples/watsonx-run.yaml) | -| AWS Bedrock | google.gemma-4-31b | Yes | remote::bedrock | [1](examples/bedrock-run.yaml) | +| AWS Bedrock | google.gemma-4-31b (Mantle via `remote::openai` + `base_url`) | Yes | remote::openai | [1](examples/bedrock-run.yaml) | [^1]: List of models is limited by design in OGX, future versions will probably allow to use more models (see [here](https://github.com/ogx-ai/ogx/blob/main/ogx/providers/remote/inference/vertexai/vertexai.py#L54)) diff --git a/docs/devel_doc/providers.md b/docs/devel_doc/providers.md index 9df629460..b1c8382db 100644 --- a/docs/devel_doc/providers.md +++ b/docs/devel_doc/providers.md @@ -102,7 +102,7 @@ inference: config: # api_key: ${env.AZURE_API_KEY} # Can be omitted when Entra ID configured in LCORE base_url: ${env.AZURE_API_BASE} - api_version: 2025-01-01-preview + api_version: preview # use with base_url .../openai/v1 (v1 preview inference API) model_validation: false # added automatically by Lightspeed enrichment ``` @@ -167,6 +167,41 @@ make run CONFIG=examples/lightspeed-stack-azure-entraid-service.yaml - `CONFIG` - Lightspeed configuration file (default: `lightspeed-stack.yaml`) - `OGX_CONFIG` - OGX configuration file to enrich and run (default: `run.yaml`) +### Vertex AI (`remote::vertexai`) + +OGX field name is **`thought_signature_store`** (not `thought_signature_storage`): a KV store reference for Gemini `thought_signature` values during tool loops. Use the same `kv_default` backend as the rest of the run config unless you run multi-replica production (then use a shared KV backend). + +```yaml + - provider_id: google-vertex + provider_type: remote::vertexai + config: + project: ${env.VERTEX_AI_PROJECT} + location: ${env.VERTEX_AI_LOCATION:=global} + allowed_models: ["publishers/google/models/gemini-3.5-flash-lite"] + thought_signature_store: + backend: kv_default + namespace: vertexai_thought_signatures +``` + +### AWS Bedrock Mantle (Gemma 4) + +`remote::bedrock` in OGX always uses `https://bedrock-runtime.{region}.amazonaws.com/openai/v1`. **Gemma 4** models (`google.gemma-4-31b`, etc.) are served only on **Bedrock Mantle**: + +`https://bedrock-mantle.{region}.api.aws/openai/v1` + +OGX `remote::bedrock` does not expose a `base_url` field (unlike `remote::azure`). For Mantle, register the provider as **`remote::openai`** with a Mantle `base_url` and a Bedrock short-term API key as `api_key` (same bearer token as `AWS_BEARER_TOKEN_BEDROCK`). Keep `provider_id: aws-bedrock` if that is your logical provider name in LCS. + +```yaml + - provider_id: aws-bedrock + provider_type: remote::openai + config: + api_key: ${env.AWS_BEARER_TOKEN_BEDROCK} + base_url: ${env.BEDROCK_MANTLE_BASE_URL:=https://bedrock-mantle.us-east-2.api.aws/openai/v1} + allowed_models: ["google.gemma-4-31b"] +``` + +Classic runtime models (e.g. `deepseek.v3-v1:0`) can continue to use `remote::bedrock` with `region_name`. + --- ## Agent Providers diff --git a/examples/azure-run.yaml b/examples/azure-run.yaml index e924eca72..af59eeb60 100644 --- a/examples/azure-run.yaml +++ b/examples/azure-run.yaml @@ -18,7 +18,7 @@ providers: provider_type: remote::azure config: base_url: https://ols-test.openai.azure.com/openai/v1 - api_version: 2024-02-15-preview + api_version: preview model_validation: false - provider_id: openai provider_type: remote::openai diff --git a/examples/bedrock-run.yaml b/examples/bedrock-run.yaml index ceb7554a8..c1e463e5b 100644 --- a/examples/bedrock-run.yaml +++ b/examples/bedrock-run.yaml @@ -16,9 +16,10 @@ providers: inference: - config: api_key: ${env.AWS_BEARER_TOKEN_BEDROCK} - region_name: us-east-2 + base_url: ${env.BEDROCK_MANTLE_BASE_URL:=https://bedrock-mantle.us-east-2.api.aws/openai/v1} + allowed_models: ["google.gemma-4-31b"] provider_id: aws-bedrock - provider_type: remote::bedrock + provider_type: remote::openai - provider_id: openai provider_type: remote::openai config: diff --git a/examples/vertexai-run.yaml b/examples/vertexai-run.yaml index 5d15d7d8a..fb955e364 100644 --- a/examples/vertexai-run.yaml +++ b/examples/vertexai-run.yaml @@ -18,8 +18,11 @@ providers: provider_type: remote::vertexai config: project: ${env.VERTEX_AI_PROJECT} - location: ${env.VERTEX_AI_LOCATION} + location: ${env.VERTEX_AI_LOCATION:=global} allowed_models: ["publishers/google/models/gemini-3.5-flash-lite"] + thought_signature_store: + backend: kv_default + namespace: vertexai_thought_signatures - provider_id: openai provider_type: remote::openai config: diff --git a/examples/watsonx-run.yaml b/examples/watsonx-run.yaml index 4d1679ca2..b30d77935 100644 --- a/examples/watsonx-run.yaml +++ b/examples/watsonx-run.yaml @@ -21,6 +21,7 @@ providers: api_key: ${env.WATSONX_API_KEY:=key-not-set} project_id: ${env.WATSONX_PROJECT_ID:=project-not-set} timeout: 1200 + allowed_models: ["openai/gpt-oss-120b"] - provider_id: openai provider_type: remote::openai config: diff --git a/src/utils/responses.py b/src/utils/responses.py index bb350023b..9cacadf91 100644 --- a/src/utils/responses.py +++ b/src/utils/responses.py @@ -1893,6 +1893,20 @@ async def _resolve_server_tools( ) +def _openai_profile_model_name(model_id: str) -> str: + """Return the model name segment used for pydantic-ai OpenAI profile lookup. + + Nested catalog ids (for example ``watsonx/openai/gpt-oss-120b``) leave a + model suffix like ``openai/gpt-oss-120b``. Passing that whole string to + ``openai_model_profile`` prefix-matches the wrong profile and can enable + reasoning overrides the backend does not support. + """ + _, model_name = extract_provider_and_model_from_model_id(model_id) + if "/" in model_name: + model_name = model_name.rsplit("/", 1)[-1] + return model_name + + def model_reasoning_enabled_by_default(model_id: str) -> bool: """Return whether the model applies non-``none`` reasoning when omitted. @@ -1901,8 +1915,7 @@ def model_reasoning_enabled_by_default(model_id: str) -> bool: present on the OGX chat-completions path — e.g. gpt-5.6-terra, not gpt-4o-mini or opt-in gpt-5.4. """ - _, model_name = extract_provider_and_model_from_model_id(model_id) - profile = openai_model_profile(model_name) + profile = openai_model_profile(_openai_profile_model_name(model_id)) return bool(profile.get("openai_reasoning_enabled_by_default", False)) @@ -1929,9 +1942,13 @@ def apply_reasoning_for_resolved_tools( otherwise. """ - if ( - not tools and not skills_configuration - ) or not model_reasoning_enabled_by_default(model_id): + if not tools and not skills_configuration: + return reasoning + + profile = openai_model_profile(_openai_profile_model_name(model_id)) + if not profile.get("openai_reasoning_enabled_by_default", False): + return reasoning + if not profile.get("openai_supports_reasoning_effort_none", False): return reasoning base = reasoning or OpenAIResponseReasoning() diff --git a/tests/e2e/configs/run-azure.yaml b/tests/e2e/configs/run-azure.yaml index c5c87ff01..2a5eda0aa 100644 --- a/tests/e2e/configs/run-azure.yaml +++ b/tests/e2e/configs/run-azure.yaml @@ -18,7 +18,8 @@ providers: provider_type: remote::azure config: base_url: https://ols-test.openai.azure.com/openai/v1 - api_version: 2024-02-15-preview + # v1 endpoint: REST api-version is "preview", not the model catalog date (2026-07-09). + api_version: preview allowed_models: ["gpt-5.6-luna"] model_validation: false - provider_id: openai diff --git a/tests/e2e/configs/run-bedrock.yaml b/tests/e2e/configs/run-bedrock.yaml index e6ec04107..118aaa467 100644 --- a/tests/e2e/configs/run-bedrock.yaml +++ b/tests/e2e/configs/run-bedrock.yaml @@ -14,11 +14,14 @@ apis: providers: inference: + # Gemma 4 is Mantle-only. remote::bedrock targets bedrock-runtime; use remote::openai + # with the Mantle OpenAI-compatible base URL (same idea as Azure base_url). - config: api_key: ${env.AWS_BEARER_TOKEN_BEDROCK} - region_name: us-east-2 + base_url: ${env.BEDROCK_MANTLE_BASE_URL:=https://bedrock-mantle.us-east-2.api.aws/openai/v1} + allowed_models: ["google.gemma-4-31b"] provider_id: aws-bedrock - provider_type: remote::bedrock + provider_type: remote::openai - provider_id: openai provider_type: remote::openai config: diff --git a/tests/e2e/configs/run-vertexai.yaml b/tests/e2e/configs/run-vertexai.yaml index 011bd6abc..597ed4a87 100644 --- a/tests/e2e/configs/run-vertexai.yaml +++ b/tests/e2e/configs/run-vertexai.yaml @@ -18,8 +18,11 @@ providers: provider_type: remote::vertexai config: project: ${env.VERTEX_AI_PROJECT} - location: ${env.VERTEX_AI_LOCATION} + location: ${env.VERTEX_AI_LOCATION:=global} allowed_models: ["publishers/google/models/gemini-3.5-flash-lite"] + thought_signature_store: + backend: kv_default + namespace: vertexai_thought_signatures - provider_id: openai provider_type: remote::openai config: diff --git a/tests/e2e/configs/run-watsonx.yaml b/tests/e2e/configs/run-watsonx.yaml index 1581ab2b5..e56221912 100644 --- a/tests/e2e/configs/run-watsonx.yaml +++ b/tests/e2e/configs/run-watsonx.yaml @@ -21,6 +21,7 @@ providers: api_key: ${env.WATSONX_API_KEY:=key-not-set} project_id: ${env.WATSONX_PROJECT_ID:=project-not-set} timeout: 1200 + allowed_models: ["openai/gpt-oss-120b"] - provider_id: openai provider_type: remote::openai config: diff --git a/tests/unit/utils/test_responses.py b/tests/unit/utils/test_responses.py index 4a4110ee8..2b9e09284 100644 --- a/tests/unit/utils/test_responses.py +++ b/tests/unit/utils/test_responses.py @@ -1202,6 +1202,12 @@ def test_gpt_5_6_luna_default_on(self) -> None: """gpt-5.6-luna reasons by default when reasoning is omitted.""" assert model_reasoning_enabled_by_default("openai/gpt-5.6-luna") is True + def test_watsonx_nested_openai_model_id_not_reasoning_default(self) -> None: + """WatsonX catalog ids must not prefix-match unrelated OpenAI profiles.""" + assert ( + model_reasoning_enabled_by_default("watsonx/openai/gpt-oss-120b") is False + ) + class TestApplyReasoningForResolvedTools: """Tests for apply_reasoning_for_resolved_tools.""" @@ -1243,6 +1249,18 @@ def test_skips_reasoning_for_non_reasoning_model_with_tools(self) -> None: is None ) + def test_skips_reasoning_for_watsonx_gpt_oss_with_tools(self) -> None: + """Harmony on WatsonX does not accept reasoning_effort none with tools.""" + assert ( + apply_reasoning_for_resolved_tools( + None, + self._sample_tools(), + "watsonx/openai/gpt-oss-120b", + None, + ) + is None + ) + def test_skips_reasoning_for_opt_in_model_with_tools(self) -> None: """Do not inject reasoning for opt-in models that default to off.""" assert (