diff --git a/tests/integration/proxy_config.yaml b/tests/integration/proxy_config.yaml index 97b25066a2a..927f8c74474 100644 --- a/tests/integration/proxy_config.yaml +++ b/tests/integration/proxy_config.yaml @@ -211,6 +211,69 @@ model_list: api_base: http://127.0.0.1:8191 api_key: synthetic-bedrock-key aws_region_name: us-east-1 + - model_name: vertex_ai/claude-haiku-4-5@20251001 + litellm_params: + model: vertex_ai/claude-haiku-4-5@20251001 + api_base: http://127.0.0.1:8191 + vertex_project: synthetic-vertex-project + vertex_location: global + vertex_credentials: '{"type": "external_account", "audience": "synthetic-vertex-audience", "subject_token_type": "urn:ietf:params:oauth:token-type:jwt", "token_url": "http://127.0.0.1:8190/_oauth/token", "credential_source": {"url": "http://127.0.0.1:8190/health"}}' + - model_name: vertex_ai/claude-sonnet-4-6@default + litellm_params: + model: vertex_ai/claude-sonnet-4-6@default + api_base: http://127.0.0.1:8191 + vertex_project: synthetic-vertex-project + vertex_location: global + vertex_credentials: '{"type": "external_account", "audience": "synthetic-vertex-audience", "subject_token_type": "urn:ietf:params:oauth:token-type:jwt", "token_url": "http://127.0.0.1:8190/_oauth/token", "credential_source": {"url": "http://127.0.0.1:8190/health"}}' + - model_name: vertex_ai/claude-sonnet-5@default + litellm_params: + model: vertex_ai/claude-sonnet-5@default + api_base: http://127.0.0.1:8191 + vertex_project: synthetic-vertex-project + vertex_location: global + vertex_credentials: '{"type": "external_account", "audience": "synthetic-vertex-audience", "subject_token_type": "urn:ietf:params:oauth:token-type:jwt", "token_url": "http://127.0.0.1:8190/_oauth/token", "credential_source": {"url": "http://127.0.0.1:8190/health"}}' + - model_name: vertex_ai/claude-opus-4-8@default + litellm_params: + model: vertex_ai/claude-opus-4-8@default + api_base: http://127.0.0.1:8191 + vertex_project: synthetic-vertex-project + vertex_location: global + vertex_credentials: '{"type": "external_account", "audience": "synthetic-vertex-audience", "subject_token_type": "urn:ietf:params:oauth:token-type:jwt", "token_url": "http://127.0.0.1:8190/_oauth/token", "credential_source": {"url": "http://127.0.0.1:8190/health"}}' + - model_name: vertex_ai/claude-sonnet-5-5@default + litellm_params: + model: vertex_ai/claude-sonnet-5-5@default + api_base: http://127.0.0.1:8191 + vertex_project: synthetic-vertex-project + vertex_location: global + vertex_credentials: '{"type": "external_account", "audience": "synthetic-vertex-audience", "subject_token_type": "urn:ietf:params:oauth:token-type:jwt", "token_url": "http://127.0.0.1:8190/_oauth/token", "credential_source": {"url": "http://127.0.0.1:8190/health"}}' + - model_name: vertex_ai/claude-opus-5-5@default + litellm_params: + model: vertex_ai/claude-opus-5-5@default + api_base: http://127.0.0.1:8191 + vertex_project: synthetic-vertex-project + vertex_location: global + vertex_credentials: '{"type": "external_account", "audience": "synthetic-vertex-audience", "subject_token_type": "urn:ietf:params:oauth:token-type:jwt", "token_url": "http://127.0.0.1:8190/_oauth/token", "credential_source": {"url": "http://127.0.0.1:8190/health"}}' + - model_name: vertex_ai/gemini-3.5-flash + litellm_params: + model: vertex_ai/gemini-3.5-flash + api_base: http://127.0.0.1:8191 + vertex_project: synthetic-vertex-project + vertex_location: global + vertex_credentials: '{"type": "external_account", "audience": "synthetic-vertex-audience", "subject_token_type": "urn:ietf:params:oauth:token-type:jwt", "token_url": "http://127.0.0.1:8190/_oauth/token", "credential_source": {"url": "http://127.0.0.1:8190/health"}}' + - model_name: vertex_ai/gemini-3.8-flash + litellm_params: + model: vertex_ai/gemini-3.8-flash + api_base: http://127.0.0.1:8191 + vertex_project: synthetic-vertex-project + vertex_location: global + vertex_credentials: '{"type": "external_account", "audience": "synthetic-vertex-audience", "subject_token_type": "urn:ietf:params:oauth:token-type:jwt", "token_url": "http://127.0.0.1:8190/_oauth/token", "credential_source": {"url": "http://127.0.0.1:8190/health"}}' + - model_name: vertex_ai/gemini-3.1-pro-preview + litellm_params: + model: vertex_ai/gemini-3.1-pro-preview + api_base: http://127.0.0.1:8191 + vertex_project: synthetic-vertex-project + vertex_location: global + vertex_credentials: '{"type": "external_account", "audience": "synthetic-vertex-audience", "subject_token_type": "urn:ietf:params:oauth:token-type:jwt", "token_url": "http://127.0.0.1:8190/_oauth/token", "credential_source": {"url": "http://127.0.0.1:8190/health"}}' general_settings: master_key: os.environ/LITELLM_MASTER_KEY database_url: os.environ/DATABASE_URL diff --git a/tests/integration/translation/chat_completions/bases/vertex_ai.py b/tests/integration/translation/chat_completions/bases/vertex_ai.py new file mode 100644 index 00000000000..49018605bd1 --- /dev/null +++ b/tests/integration/translation/chat_completions/bases/vertex_ai.py @@ -0,0 +1,710 @@ +from typing import Final +from unittest.mock import ANY + +from integration.translation.case import TranslationTestCase + +GEMINI_3_5_FLASH_THOUGHT_SIGNATURE: Final = "AY89a1+kriNs9YsPegJQxgjTWsMlxsqPU/UkHDyz4EK+3FRrE52ygqPIHTvkblgKKiO5Fn8R9D3iu//i+YnuuZ+e7W0yxUoFScas89b5rn3vZHsihnUA7VTe3b7beth1IftteoTGZ3F8OSLrSIP8HKn4xTIx35eeNEt5oiD3K5rTQrdklqO277HvTDhu/LuZhZ0pzaWciGQEc61epIBBpC2ygu8V4L8TekUVZnz6qzSzxipdiQStQ8dZou43334hMnxOR6JjJCGxqt/1K+C0vXX6YP6f+Bph5VhPSnvy2Z1uApi3TUxAcsDDyVewIr+ha2G6DPhN1Oi5uZZNhmbvH01Uq5OOquF1uEICq2m/35LhJIbDNM9N7oI1Z6L94P9xdugeFXM2S7wdJfc8sdg09vEzNeA++GK25zRuB4hU7XyaG4XnWjmu2kswi4sf+EyrO/K0P2tb/ZCJu7kJTtWTiD1wgSIl1eqS8ORKKsXDN2hIbFrtsNR50WIvVzd6" + +GEMINI_3_8_FLASH_THOUGHT_SIGNATURE: Final = "AY89a1+gmp4bVJhwGOCtbUalmyfhwvgE4EsZOwS4bNR02vurTt4AZC72ypTNLJDRwK8I518tGYBiDnFqD6jTxkjWTQTPGwUZ5IOYr1IskTb3pPKfvZWZ/XunV7kfb9VmEU0TBlgwjVqxXziOxvjediJyb2gbFnCVjGTSVsq0ipmpFznF22lV1I6N9GI7RKBFgYXaSzGU1+fGbLLBigaUD0qbgquAnFv6R3JGpaLcW7EUrhKDinHoFukmUwBLx07S7SfuWBT8hMLMs32vEVzwpns9JmbVtuQG24zs5qQx9sJqtO9E07k6SFL3f1tvaKZsQ6Yjb08Xcuvnh9Pcz4ChCdRJ4vRkmtRW3AgOli8/43rrrAMmvIOqEdAOOvZW3W9D8lnOC+Nfq7S0FTNWPYkIJhrtskWoQkUwDrY6TOzMjIA6Jblt2f9kKU3VJzq7O1hPjbHsJKmLBYP+De8rKx7Ufq9Shs5qLLu4aBBgHleiyFiHGQSD18RDjnyv0DLRyrp7LOkjIYWBYoIA2+BSb0ySMyeLhk8B2U8XZ6AXiw3aHoNUG/wwuE0EhXBhOUBM14FmchGfptXUr+Dn9kZvPAy8mfbq3Pk=" + +GEMINI_3_1_PRO_PREVIEW_THOUGHT_SIGNATURE: Final = "AY89a198sdR5Cy3qgtvOq/o5xsmH1g3WymzyZyB/bv3XeyqNZAgXbCocCAqUS913U8FvVUodNrm3y2B5P/mDsn3A3pINhbEiwJcWA/o8bNJq+Z/1O2UYdViO0FMhOOS1aRaCbza+6aSBLPVQ81QnyxrwH6rdDCuQlwyZ+5/Evc/EOXLVxdUF9PYwTwNJcZPREl4UMcarKvtKsNofkSfa09EWQNVVMDkasNl3YHtQJqYLu5rL7vSUrbMtLLA0sMEMh2/4zMj4gIvtjoOwiI4CBdLLH0Xp8SGGco8UljxikaIp6YvQW4cHsfkaqTBQmLgREudbnKKTZ0MvMR52G/++7r8Iv6BL29+73igMMyHoQad40OM8RY3E6akpSE5uPXH/CgYTmSLXQONtRMXzrXm996JYLaHLOo2dP1RtaJANcjNoDB4eN5vj4GOpTUYqZqRRaB5jMX2od8QcAnaXk2AYs/2rNMaLV4zgraz45gbA0It465nN5hb4JbRH2toJ9K/TvsZY/YyoLmV9RjyAmIhbq6bG0fGatsXgmDaXj/vEsBmqfRyBN31k" + +CLAUDE_HAIKU_4_5_TEST_CASE: Final = TranslationTestCase( + scenario="basic", + litellm_endpoint="/v1/chat/completions", + litellm_request={ + "model": "vertex_ai/claude-haiku-4-5@20251001", + "max_tokens": 64, + "messages": [ + {"role": "system", "content": "You are a terse assistant."}, + {"role": "user", "content": "Say hello."}, + ], + "cache": {"no-cache": True}, + }, + expected_provider_endpoint="/v1/projects/synthetic-vertex-project/locations/global/publishers/anthropic/models/claude-haiku-4-5@20251001:rawPredict", + expected_provider_headers={ + "authorization": "Bearer scripted-token", + "anthropic-version": "2023-06-01", + "content-type": "application/json", + "x-api-key": "scripted-token", + }, + expected_provider_request={ + "messages": [{"role": "user", "content": [{"type": "text", "text": "Say hello."}]}], + "max_tokens": 64, + "anthropic_version": "vertex-2023-10-16", + "system": [{"type": "text", "text": "You are a terse assistant."}], + }, + mock_provider_response={ + "model": "claude-haiku-4-5-20251001", + "id": "msg_vrtx_011CfjuySsPewSSfasuPiaHg", + "type": "message", + "role": "assistant", + "content": [{"type": "text", "text": "Hello."}], + "container": None, + "stop_reason": "end_turn", + "stop_sequence": None, + "stop_details": None, + "usage": { + "input_tokens": 17, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + "output_tokens": 5, + }, + }, + expected_litellm_status_code=200, + expected_litellm_response={ + "id": ANY, + "created": ANY, + "model": "vertex_ai/claude-haiku-4-5@20251001", + "object": "chat.completion", + "choices": [ + { + "finish_reason": "stop", + "index": 0, + "message": { + "content": "Hello.", + "role": "assistant", + "provider_specific_fields": {"citations": None, "thinking_blocks": None}, + }, + } + ], + "usage": { + "completion_tokens": 5, + "prompt_tokens": 17, + "total_tokens": 22, + "completion_tokens_details": {"reasoning_tokens": 0, "text_tokens": 5}, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 17, + "cache_write_tokens": 0, + "cache_creation_tokens": 0, + "cache_creation_token_details": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + }, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + }, + }, +) + +CLAUDE_SONNET_4_6_TEST_CASE: Final = TranslationTestCase( + scenario="basic", + litellm_endpoint="/v1/chat/completions", + litellm_request={ + "model": "vertex_ai/claude-sonnet-4-6@default", + "max_tokens": 64, + "messages": [ + {"role": "system", "content": "You are a terse assistant."}, + {"role": "user", "content": "Say hello."}, + ], + "cache": {"no-cache": True}, + }, + expected_provider_endpoint="/v1/projects/synthetic-vertex-project/locations/global/publishers/anthropic/models/claude-sonnet-4-6@default:rawPredict", + expected_provider_headers={ + "authorization": "Bearer scripted-token", + "anthropic-version": "2023-06-01", + "content-type": "application/json", + "x-api-key": "scripted-token", + }, + expected_provider_request={ + "messages": [{"role": "user", "content": [{"type": "text", "text": "Say hello."}]}], + "max_tokens": 64, + "anthropic_version": "vertex-2023-10-16", + "system": [{"type": "text", "text": "You are a terse assistant."}], + }, + mock_provider_response={ + "model": "claude-sonnet-4-6", + "id": "msg_vrtx_011CfjuxUbJsRvQZCN21qNQq", + "type": "message", + "role": "assistant", + "content": [{"type": "text", "text": "Hello!"}], + "container": None, + "stop_reason": "end_turn", + "stop_sequence": None, + "stop_details": None, + "usage": { + "input_tokens": 18, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + "output_tokens": 5, + }, + }, + expected_litellm_status_code=200, + expected_litellm_response={ + "id": ANY, + "created": ANY, + "model": "vertex_ai/claude-sonnet-4-6@default", + "object": "chat.completion", + "choices": [ + { + "finish_reason": "stop", + "index": 0, + "message": { + "content": "Hello!", + "role": "assistant", + "provider_specific_fields": {"citations": None, "thinking_blocks": None}, + }, + } + ], + "usage": { + "completion_tokens": 5, + "prompt_tokens": 18, + "total_tokens": 23, + "completion_tokens_details": {"reasoning_tokens": 0, "text_tokens": 5}, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 18, + "cache_write_tokens": 0, + "cache_creation_tokens": 0, + "cache_creation_token_details": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + }, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + }, + }, +) + +CLAUDE_SONNET_5_TEST_CASE: Final = TranslationTestCase( + scenario="basic", + litellm_endpoint="/v1/chat/completions", + litellm_request={ + "model": "vertex_ai/claude-sonnet-5@default", + "max_tokens": 64, + "messages": [ + {"role": "system", "content": "You are a terse assistant."}, + {"role": "user", "content": "Say hello."}, + ], + "cache": {"no-cache": True}, + }, + expected_provider_endpoint="/v1/projects/synthetic-vertex-project/locations/global/publishers/anthropic/models/claude-sonnet-5@default:rawPredict", + expected_provider_headers={ + "authorization": "Bearer scripted-token", + "anthropic-version": "2023-06-01", + "content-type": "application/json", + "x-api-key": "scripted-token", + }, + expected_provider_request={ + "messages": [{"role": "user", "content": [{"type": "text", "text": "Say hello."}]}], + "max_tokens": 64, + "anthropic_version": "vertex-2023-10-16", + "system": [{"type": "text", "text": "You are a terse assistant."}], + }, + mock_provider_response={ + "model": "claude-sonnet-5", + "id": "msg_vrtx_011CfjuygPVDgzuMXdyyW9UP", + "type": "message", + "role": "assistant", + "content": [{"type": "text", "text": "Hello."}], + "container": None, + "stop_reason": "end_turn", + "stop_sequence": None, + "stop_details": None, + "usage": { + "input_tokens": 21, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + "output_tokens": 6, + "output_tokens_details": {"thinking_tokens": 0}, + }, + }, + expected_litellm_status_code=200, + expected_litellm_response={ + "id": ANY, + "created": ANY, + "model": "vertex_ai/claude-sonnet-5@default", + "object": "chat.completion", + "choices": [ + { + "finish_reason": "stop", + "index": 0, + "message": { + "content": "Hello.", + "role": "assistant", + "provider_specific_fields": {"citations": None, "thinking_blocks": None}, + }, + } + ], + "usage": { + "completion_tokens": 6, + "prompt_tokens": 21, + "total_tokens": 27, + "completion_tokens_details": {"reasoning_tokens": 0, "text_tokens": 6}, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 21, + "cache_write_tokens": 0, + "cache_creation_tokens": 0, + "cache_creation_token_details": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + }, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + }, + }, +) + +CLAUDE_OPUS_4_8_TEST_CASE: Final = TranslationTestCase( + scenario="basic", + litellm_endpoint="/v1/chat/completions", + litellm_request={ + "model": "vertex_ai/claude-opus-4-8@default", + "max_tokens": 64, + "messages": [ + {"role": "system", "content": "You are a terse assistant."}, + {"role": "user", "content": "Say hello."}, + ], + "cache": {"no-cache": True}, + }, + expected_provider_endpoint="/v1/projects/synthetic-vertex-project/locations/global/publishers/anthropic/models/claude-opus-4-8@default:rawPredict", + expected_provider_headers={ + "authorization": "Bearer scripted-token", + "anthropic-version": "2023-06-01", + "content-type": "application/json", + "x-api-key": "scripted-token", + }, + expected_provider_request={ + "messages": [{"role": "user", "content": [{"type": "text", "text": "Say hello."}]}], + "max_tokens": 64, + "anthropic_version": "vertex-2023-10-16", + "system": [{"type": "text", "text": "You are a terse assistant."}], + }, + mock_provider_response={ + "model": "claude-opus-4-8", + "id": "msg_vrtx_011CfjuyofoMgSaV8Pbt16N9", + "type": "message", + "role": "assistant", + "content": [{"type": "text", "text": "Hello."}], + "container": None, + "stop_reason": "end_turn", + "stop_sequence": None, + "stop_details": None, + "usage": { + "input_tokens": 21, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + "output_tokens": 6, + "output_tokens_details": {"thinking_tokens": 0}, + }, + }, + expected_litellm_status_code=200, + expected_litellm_response={ + "id": ANY, + "created": ANY, + "model": "vertex_ai/claude-opus-4-8@default", + "object": "chat.completion", + "choices": [ + { + "finish_reason": "stop", + "index": 0, + "message": { + "content": "Hello.", + "role": "assistant", + "provider_specific_fields": {"citations": None, "thinking_blocks": None}, + }, + } + ], + "usage": { + "completion_tokens": 6, + "prompt_tokens": 21, + "total_tokens": 27, + "completion_tokens_details": {"reasoning_tokens": 0, "text_tokens": 6}, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 21, + "cache_write_tokens": 0, + "cache_creation_tokens": 0, + "cache_creation_token_details": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + }, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + }, + }, +) + +CLAUDE_SONNET_5_5_TEST_CASE: Final = TranslationTestCase( + scenario="basic", + litellm_endpoint="/v1/chat/completions", + litellm_request={ + "model": "vertex_ai/claude-sonnet-5-5@default", + "max_tokens": 64, + "messages": [ + {"role": "system", "content": "You are a terse assistant."}, + {"role": "user", "content": "Say hello."}, + ], + "cache": {"no-cache": True}, + }, + expected_provider_endpoint="/v1/projects/synthetic-vertex-project/locations/global/publishers/anthropic/models/claude-sonnet-5-5@default:rawPredict", + expected_provider_headers={ + "authorization": "Bearer scripted-token", + "anthropic-version": "2023-06-01", + "content-type": "application/json", + "x-api-key": "scripted-token", + }, + expected_provider_request={ + "messages": [{"role": "user", "content": [{"type": "text", "text": "Say hello."}]}], + "max_tokens": 64, + "anthropic_version": "vertex-2023-10-16", + "system": [{"type": "text", "text": "You are a terse assistant."}], + }, + mock_provider_response={ + "model": "claude-sonnet-5-5", + "id": "msg_vrtx_011CfjuywExcu3BdZ3x1KzVJ", + "type": "message", + "role": "assistant", + "content": [{"type": "text", "text": "Hello."}], + "container": None, + "stop_reason": "end_turn", + "stop_sequence": None, + "stop_details": None, + "usage": { + "input_tokens": 23, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + "output_tokens": 6, + "output_tokens_details": {"thinking_tokens": 0}, + }, + }, + expected_litellm_status_code=200, + expected_litellm_response={ + "id": ANY, + "created": ANY, + "model": "vertex_ai/claude-sonnet-5-5@default", + "object": "chat.completion", + "choices": [ + { + "finish_reason": "stop", + "index": 0, + "message": { + "content": "Hello.", + "role": "assistant", + "provider_specific_fields": {"citations": None, "thinking_blocks": None}, + }, + } + ], + "usage": { + "completion_tokens": 6, + "prompt_tokens": 23, + "total_tokens": 29, + "completion_tokens_details": {"reasoning_tokens": 0, "text_tokens": 6}, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 23, + "cache_write_tokens": 0, + "cache_creation_tokens": 0, + "cache_creation_token_details": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + }, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + }, + }, +) + +CLAUDE_OPUS_5_5_TEST_CASE: Final = TranslationTestCase( + scenario="basic", + litellm_endpoint="/v1/chat/completions", + litellm_request={ + "model": "vertex_ai/claude-opus-5-5@default", + "max_tokens": 64, + "messages": [ + {"role": "system", "content": "You are a terse assistant."}, + {"role": "user", "content": "Say hello."}, + ], + "cache": {"no-cache": True}, + }, + expected_provider_endpoint="/v1/projects/synthetic-vertex-project/locations/global/publishers/anthropic/models/claude-opus-5-5@default:rawPredict", + expected_provider_headers={ + "authorization": "Bearer scripted-token", + "anthropic-version": "2023-06-01", + "content-type": "application/json", + "x-api-key": "scripted-token", + }, + expected_provider_request={ + "messages": [{"role": "user", "content": [{"type": "text", "text": "Say hello."}]}], + "max_tokens": 64, + "anthropic_version": "vertex-2023-10-16", + "system": [{"type": "text", "text": "You are a terse assistant."}], + }, + mock_provider_response={ + "model": "claude-opus-5-5", + "id": "msg_vrtx_011Cfjuz6qugj8FbC3fcXdun", + "type": "message", + "role": "assistant", + "content": [{"type": "text", "text": "Hello."}], + "container": None, + "stop_reason": "end_turn", + "stop_sequence": None, + "stop_details": None, + "usage": { + "input_tokens": 23, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + "output_tokens": 6, + "output_tokens_details": {"thinking_tokens": 0}, + }, + }, + expected_litellm_status_code=200, + expected_litellm_response={ + "id": ANY, + "created": ANY, + "model": "vertex_ai/claude-opus-5-5@default", + "object": "chat.completion", + "choices": [ + { + "finish_reason": "stop", + "index": 0, + "message": { + "content": "Hello.", + "role": "assistant", + "provider_specific_fields": {"citations": None, "thinking_blocks": None}, + }, + } + ], + "usage": { + "completion_tokens": 6, + "prompt_tokens": 23, + "total_tokens": 29, + "completion_tokens_details": {"reasoning_tokens": 0, "text_tokens": 6}, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 23, + "cache_write_tokens": 0, + "cache_creation_tokens": 0, + "cache_creation_token_details": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + }, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + }, + }, +) + +GEMINI_3_5_FLASH_TEST_CASE: Final = TranslationTestCase( + scenario="basic", + litellm_endpoint="/v1/chat/completions", + litellm_request={ + "model": "vertex_ai/gemini-3.5-flash", + "max_tokens": 1024, + "messages": [ + {"role": "system", "content": "You are a terse assistant."}, + {"role": "user", "content": "Say hello."}, + ], + "cache": {"no-cache": True}, + }, + expected_provider_endpoint="/v1/projects/synthetic-vertex-project/locations/global/publishers/google/models/gemini-3.5-flash:generateContent", + expected_provider_headers={"content-type": "application/json", "authorization": "Bearer scripted-token"}, + expected_provider_request={ + "contents": [{"role": "user", "parts": [{"text": "Say hello."}]}], + "system_instruction": {"parts": [{"text": "You are a terse assistant."}]}, + "generationConfig": {"max_output_tokens": 1024, "temperature": 1.0}, + }, + mock_provider_response={ + "candidates": [ + { + "content": { + "role": "model", + "parts": [{"text": "Hello.", "thoughtSignature": GEMINI_3_5_FLASH_THOUGHT_SIGNATURE}], + }, + "finishReason": "STOP", + } + ], + "usageMetadata": { + "promptTokenCount": 9, + "candidatesTokenCount": 2, + "totalTokenCount": 95, + "trafficType": "ON_DEMAND", + "promptTokensDetails": [{"modality": "TEXT", "tokenCount": 9}], + "candidatesTokensDetails": [{"modality": "TEXT", "tokenCount": 2}], + "thoughtsTokenCount": 84, + }, + "modelVersion": "gemini-3.5-flash", + "createTime": "2026-10-05T22:54:50.809002Z", + "responseId": "uirEaqqwMc-U9LsPjJTYuQU", + }, + expected_litellm_status_code=200, + expected_litellm_response={ + "id": "uirEaqqwMc-U9LsPjJTYuQU", + "created": ANY, + "model": "vertex_ai/gemini-3.5-flash", + "object": "chat.completion", + "choices": [ + { + "finish_reason": "stop", + "index": 0, + "message": { + "content": "Hello.", + "role": "assistant", + "images": [], + "thinking_blocks": [], + "provider_specific_fields": {"thought_signatures": [GEMINI_3_5_FLASH_THOUGHT_SIGNATURE]}, + }, + "provider_specific_fields": {"native_finish_reason": "STOP"}, + } + ], + "usage": { + "completion_tokens": 86, + "prompt_tokens": 9, + "total_tokens": 95, + "completion_tokens_details": {"reasoning_tokens": 84, "text_tokens": 2}, + "prompt_tokens_details": {"text_tokens": 9}, + }, + "vertex_ai_grounding_metadata": [], + "vertex_ai_url_context_metadata": [], + "vertex_ai_safety_results": [], + "vertex_ai_citation_metadata": [], + }, +) + +GEMINI_3_8_FLASH_TEST_CASE: Final = TranslationTestCase( + scenario="basic", + litellm_endpoint="/v1/chat/completions", + litellm_request={ + "model": "vertex_ai/gemini-3.8-flash", + "max_tokens": 1024, + "messages": [ + {"role": "system", "content": "You are a terse assistant."}, + {"role": "user", "content": "Say hello."}, + ], + "cache": {"no-cache": True}, + }, + expected_provider_endpoint="/v1/projects/synthetic-vertex-project/locations/global/publishers/google/models/gemini-3.8-flash:generateContent", + expected_provider_headers={"content-type": "application/json", "authorization": "Bearer scripted-token"}, + expected_provider_request={ + "contents": [{"role": "user", "parts": [{"text": "Say hello."}]}], + "system_instruction": {"parts": [{"text": "You are a terse assistant."}]}, + "generationConfig": {"max_output_tokens": 1024, "temperature": 1.0}, + }, + mock_provider_response={ + "candidates": [ + { + "content": { + "role": "model", + "parts": [{"text": "Hello.", "thoughtSignature": GEMINI_3_8_FLASH_THOUGHT_SIGNATURE}], + }, + "finishReason": "STOP", + } + ], + "usageMetadata": { + "promptTokenCount": 9, + "candidatesTokenCount": 2, + "totalTokenCount": 118, + "trafficType": "ON_DEMAND", + "promptTokensDetails": [{"modality": "TEXT", "tokenCount": 9}], + "candidatesTokensDetails": [{"modality": "TEXT", "tokenCount": 2}], + "thoughtsTokenCount": 107, + }, + "modelVersion": "gemini-3.8-flash", + "createTime": "2026-10-05T22:55:12.739855Z", + "responseId": "0CrEao-ULbuU9LsPk-mQ2A8", + }, + expected_litellm_status_code=200, + expected_litellm_response={ + "id": "0CrEao-ULbuU9LsPk-mQ2A8", + "created": ANY, + "model": "vertex_ai/gemini-3.8-flash", + "object": "chat.completion", + "choices": [ + { + "finish_reason": "stop", + "index": 0, + "message": { + "content": "Hello.", + "role": "assistant", + "images": [], + "thinking_blocks": [], + "provider_specific_fields": {"thought_signatures": [GEMINI_3_8_FLASH_THOUGHT_SIGNATURE]}, + }, + "provider_specific_fields": {"native_finish_reason": "STOP"}, + } + ], + "usage": { + "completion_tokens": 109, + "prompt_tokens": 9, + "total_tokens": 118, + "completion_tokens_details": {"reasoning_tokens": 107, "text_tokens": 2}, + "prompt_tokens_details": {"text_tokens": 9}, + }, + "vertex_ai_grounding_metadata": [], + "vertex_ai_url_context_metadata": [], + "vertex_ai_safety_results": [], + "vertex_ai_citation_metadata": [], + }, +) + +GEMINI_3_1_PRO_PREVIEW_TEST_CASE: Final = TranslationTestCase( + scenario="basic", + litellm_endpoint="/v1/chat/completions", + litellm_request={ + "model": "vertex_ai/gemini-3.1-pro-preview", + "max_tokens": 1024, + "messages": [ + {"role": "system", "content": "You are a terse assistant."}, + {"role": "user", "content": "Say hello."}, + ], + "cache": {"no-cache": True}, + }, + expected_provider_endpoint="/v1/projects/synthetic-vertex-project/locations/global/publishers/google/models/gemini-3.1-pro-preview:generateContent", + expected_provider_headers={"content-type": "application/json", "authorization": "Bearer scripted-token"}, + expected_provider_request={ + "contents": [{"role": "user", "parts": [{"text": "Say hello."}]}], + "system_instruction": {"parts": [{"text": "You are a terse assistant."}]}, + "generationConfig": {"max_output_tokens": 1024, "temperature": 1.0}, + }, + mock_provider_response={ + "candidates": [ + { + "content": { + "role": "model", + "parts": [{"text": "Hello.", "thoughtSignature": GEMINI_3_1_PRO_PREVIEW_THOUGHT_SIGNATURE}], + }, + "finishReason": "STOP", + } + ], + "usageMetadata": { + "promptTokenCount": 9, + "candidatesTokenCount": 1, + "totalTokenCount": 99, + "trafficType": "ON_DEMAND", + "promptTokensDetails": [{"modality": "TEXT", "tokenCount": 9}], + "candidatesTokensDetails": [{"modality": "TEXT", "tokenCount": 1}], + "thoughtsTokenCount": 89, + }, + "modelVersion": "gemini-3.1-pro-preview", + "createTime": "2026-10-05T22:55:14.874538Z", + "responseId": "0irEaqqwNbCdq8YP3L2mqQ4", + }, + expected_litellm_status_code=200, + expected_litellm_response={ + "id": "0irEaqqwNbCdq8YP3L2mqQ4", + "created": ANY, + "model": "vertex_ai/gemini-3.1-pro-preview", + "object": "chat.completion", + "choices": [ + { + "finish_reason": "stop", + "index": 0, + "message": { + "content": "Hello.", + "role": "assistant", + "images": [], + "thinking_blocks": [], + "provider_specific_fields": {"thought_signatures": [GEMINI_3_1_PRO_PREVIEW_THOUGHT_SIGNATURE]}, + }, + "provider_specific_fields": {"native_finish_reason": "STOP"}, + } + ], + "usage": { + "completion_tokens": 90, + "prompt_tokens": 9, + "total_tokens": 99, + "completion_tokens_details": {"reasoning_tokens": 89, "text_tokens": 1}, + "prompt_tokens_details": {"text_tokens": 9}, + }, + "vertex_ai_grounding_metadata": [], + "vertex_ai_url_context_metadata": [], + "vertex_ai_safety_results": [], + "vertex_ai_citation_metadata": [], + }, +) diff --git a/tests/integration/translation/chat_completions/basic/test_chat_completions_basic_vertex_ai.py b/tests/integration/translation/chat_completions/basic/test_chat_completions_basic_vertex_ai.py new file mode 100644 index 00000000000..c151bce4886 --- /dev/null +++ b/tests/integration/translation/chat_completions/basic/test_chat_completions_basic_vertex_ai.py @@ -0,0 +1,40 @@ +import pytest + +from integration._support.client import Gateway +from integration._support.provider import SharedProvider +from integration.translation.case import TranslationTestCase +from integration.translation.chat_completions.bases.vertex_ai import ( + CLAUDE_HAIKU_4_5_TEST_CASE, + CLAUDE_SONNET_4_6_TEST_CASE, + CLAUDE_SONNET_5_TEST_CASE, + CLAUDE_OPUS_4_8_TEST_CASE, + CLAUDE_SONNET_5_5_TEST_CASE, + CLAUDE_OPUS_5_5_TEST_CASE, + GEMINI_3_5_FLASH_TEST_CASE, + GEMINI_3_8_FLASH_TEST_CASE, + GEMINI_3_1_PRO_PREVIEW_TEST_CASE, +) +from integration.translation.runner import assert_translation + + +@pytest.mark.parametrize( + "case", + [ + CLAUDE_HAIKU_4_5_TEST_CASE, + CLAUDE_SONNET_4_6_TEST_CASE, + CLAUDE_SONNET_5_TEST_CASE, + CLAUDE_OPUS_4_8_TEST_CASE, + CLAUDE_SONNET_5_5_TEST_CASE, + CLAUDE_OPUS_5_5_TEST_CASE, + GEMINI_3_5_FLASH_TEST_CASE, + GEMINI_3_8_FLASH_TEST_CASE, + GEMINI_3_1_PRO_PREVIEW_TEST_CASE, + ], + ids=lambda case: case.id, +) +def test_chat_completions_basic_vertex_ai( + case: TranslationTestCase, + gateway: Gateway, + provider: SharedProvider, +) -> None: + assert_translation(case, gateway, provider) diff --git a/tests/integration/translation/messages/bases/vertex_ai.py b/tests/integration/translation/messages/bases/vertex_ai.py new file mode 100644 index 00000000000..97d48bdc871 --- /dev/null +++ b/tests/integration/translation/messages/bases/vertex_ai.py @@ -0,0 +1,527 @@ +from typing import Final + +from integration.translation.case import TranslationTestCase + +GEMINI_3_5_FLASH_THOUGHT_SIGNATURE: Final = "AY89a1+kriNs9YsPegJQxgjTWsMlxsqPU/UkHDyz4EK+3FRrE52ygqPIHTvkblgKKiO5Fn8R9D3iu//i+YnuuZ+e7W0yxUoFScas89b5rn3vZHsihnUA7VTe3b7beth1IftteoTGZ3F8OSLrSIP8HKn4xTIx35eeNEt5oiD3K5rTQrdklqO277HvTDhu/LuZhZ0pzaWciGQEc61epIBBpC2ygu8V4L8TekUVZnz6qzSzxipdiQStQ8dZou43334hMnxOR6JjJCGxqt/1K+C0vXX6YP6f+Bph5VhPSnvy2Z1uApi3TUxAcsDDyVewIr+ha2G6DPhN1Oi5uZZNhmbvH01Uq5OOquF1uEICq2m/35LhJIbDNM9N7oI1Z6L94P9xdugeFXM2S7wdJfc8sdg09vEzNeA++GK25zRuB4hU7XyaG4XnWjmu2kswi4sf+EyrO/K0P2tb/ZCJu7kJTtWTiD1wgSIl1eqS8ORKKsXDN2hIbFrtsNR50WIvVzd6" + +GEMINI_3_8_FLASH_THOUGHT_SIGNATURE: Final = "AY89a1+gmp4bVJhwGOCtbUalmyfhwvgE4EsZOwS4bNR02vurTt4AZC72ypTNLJDRwK8I518tGYBiDnFqD6jTxkjWTQTPGwUZ5IOYr1IskTb3pPKfvZWZ/XunV7kfb9VmEU0TBlgwjVqxXziOxvjediJyb2gbFnCVjGTSVsq0ipmpFznF22lV1I6N9GI7RKBFgYXaSzGU1+fGbLLBigaUD0qbgquAnFv6R3JGpaLcW7EUrhKDinHoFukmUwBLx07S7SfuWBT8hMLMs32vEVzwpns9JmbVtuQG24zs5qQx9sJqtO9E07k6SFL3f1tvaKZsQ6Yjb08Xcuvnh9Pcz4ChCdRJ4vRkmtRW3AgOli8/43rrrAMmvIOqEdAOOvZW3W9D8lnOC+Nfq7S0FTNWPYkIJhrtskWoQkUwDrY6TOzMjIA6Jblt2f9kKU3VJzq7O1hPjbHsJKmLBYP+De8rKx7Ufq9Shs5qLLu4aBBgHleiyFiHGQSD18RDjnyv0DLRyrp7LOkjIYWBYoIA2+BSb0ySMyeLhk8B2U8XZ6AXiw3aHoNUG/wwuE0EhXBhOUBM14FmchGfptXUr+Dn9kZvPAy8mfbq3Pk=" + +GEMINI_3_1_PRO_PREVIEW_THOUGHT_SIGNATURE: Final = "AY89a198sdR5Cy3qgtvOq/o5xsmH1g3WymzyZyB/bv3XeyqNZAgXbCocCAqUS913U8FvVUodNrm3y2B5P/mDsn3A3pINhbEiwJcWA/o8bNJq+Z/1O2UYdViO0FMhOOS1aRaCbza+6aSBLPVQ81QnyxrwH6rdDCuQlwyZ+5/Evc/EOXLVxdUF9PYwTwNJcZPREl4UMcarKvtKsNofkSfa09EWQNVVMDkasNl3YHtQJqYLu5rL7vSUrbMtLLA0sMEMh2/4zMj4gIvtjoOwiI4CBdLLH0Xp8SGGco8UljxikaIp6YvQW4cHsfkaqTBQmLgREudbnKKTZ0MvMR52G/++7r8Iv6BL29+73igMMyHoQad40OM8RY3E6akpSE5uPXH/CgYTmSLXQONtRMXzrXm996JYLaHLOo2dP1RtaJANcjNoDB4eN5vj4GOpTUYqZqRRaB5jMX2od8QcAnaXk2AYs/2rNMaLV4zgraz45gbA0It465nN5hb4JbRH2toJ9K/TvsZY/YyoLmV9RjyAmIhbq6bG0fGatsXgmDaXj/vEsBmqfRyBN31k" + +CLAUDE_HAIKU_4_5_TEST_CASE: Final = TranslationTestCase( + scenario="basic", + litellm_endpoint="/v1/messages", + litellm_request={ + "model": "vertex_ai/claude-haiku-4-5@20251001", + "max_tokens": 64, + "system": "You are a terse assistant.", + "messages": [{"role": "user", "content": "Say hello."}], + "cache": {"no-cache": True}, + }, + expected_provider_endpoint="/v1/projects/synthetic-vertex-project/locations/global/publishers/anthropic/models/claude-haiku-4-5@20251001:rawPredict", + expected_provider_headers={"authorization": "Bearer scripted-token", "content-type": "application/json"}, + expected_provider_request={ + "messages": [{"role": "user", "content": "Say hello."}], + "max_tokens": 64, + "stream": False, + "system": [{"type": "text", "text": "You are a terse assistant."}], + "anthropic_version": "vertex-2023-10-16", + }, + mock_provider_response={ + "model": "claude-haiku-4-5-20251001", + "id": "msg_vrtx_011CfjuyLkoJCtCANzSshqj7", + "type": "message", + "role": "assistant", + "content": [{"type": "text", "text": "Hello."}], + "container": None, + "stop_reason": "end_turn", + "stop_sequence": None, + "stop_details": None, + "usage": { + "input_tokens": 17, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + "output_tokens": 5, + }, + }, + expected_litellm_status_code=200, + expected_litellm_response={ + "model": "vertex_ai/claude-haiku-4-5@20251001", + "id": "msg_vrtx_011CfjuyLkoJCtCANzSshqj7", + "type": "message", + "role": "assistant", + "content": [{"type": "text", "text": "Hello."}], + "container": None, + "stop_reason": "end_turn", + "stop_sequence": None, + "stop_details": None, + "usage": { + "input_tokens": 17, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + "output_tokens": 5, + }, + }, +) + +CLAUDE_SONNET_4_6_TEST_CASE: Final = TranslationTestCase( + scenario="basic", + litellm_endpoint="/v1/messages", + litellm_request={ + "model": "vertex_ai/claude-sonnet-4-6@default", + "max_tokens": 64, + "system": "You are a terse assistant.", + "messages": [{"role": "user", "content": "Say hello."}], + "cache": {"no-cache": True}, + }, + expected_provider_endpoint="/v1/projects/synthetic-vertex-project/locations/global/publishers/anthropic/models/claude-sonnet-4-6@default:rawPredict", + expected_provider_headers={"authorization": "Bearer scripted-token", "content-type": "application/json"}, + expected_provider_request={ + "messages": [{"role": "user", "content": "Say hello."}], + "max_tokens": 64, + "stream": False, + "system": [{"type": "text", "text": "You are a terse assistant."}], + "anthropic_version": "vertex-2023-10-16", + }, + mock_provider_response={ + "model": "claude-sonnet-4-6", + "id": "msg_vrtx_011CfjuxNMG2o9mmi6TvgC48", + "type": "message", + "role": "assistant", + "content": [{"type": "text", "text": "Hello!"}], + "container": None, + "stop_reason": "end_turn", + "stop_sequence": None, + "stop_details": None, + "usage": { + "input_tokens": 18, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + "output_tokens": 5, + }, + }, + expected_litellm_status_code=200, + expected_litellm_response={ + "model": "vertex_ai/claude-sonnet-4-6@default", + "id": "msg_vrtx_011CfjuxNMG2o9mmi6TvgC48", + "type": "message", + "role": "assistant", + "content": [{"type": "text", "text": "Hello!"}], + "container": None, + "stop_reason": "end_turn", + "stop_sequence": None, + "stop_details": None, + "usage": { + "input_tokens": 18, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + "output_tokens": 5, + }, + }, +) + +CLAUDE_SONNET_5_TEST_CASE: Final = TranslationTestCase( + scenario="basic", + litellm_endpoint="/v1/messages", + litellm_request={ + "model": "vertex_ai/claude-sonnet-5@default", + "max_tokens": 64, + "system": "You are a terse assistant.", + "messages": [{"role": "user", "content": "Say hello."}], + "cache": {"no-cache": True}, + }, + expected_provider_endpoint="/v1/projects/synthetic-vertex-project/locations/global/publishers/anthropic/models/claude-sonnet-5@default:rawPredict", + expected_provider_headers={"authorization": "Bearer scripted-token", "content-type": "application/json"}, + expected_provider_request={ + "messages": [{"role": "user", "content": "Say hello."}], + "max_tokens": 64, + "stream": False, + "system": [{"type": "text", "text": "You are a terse assistant."}], + "anthropic_version": "vertex-2023-10-16", + }, + mock_provider_response={ + "model": "claude-sonnet-5", + "id": "msg_vrtx_011CfjuyZQJsY8CfdZxY6C9E", + "type": "message", + "role": "assistant", + "content": [{"type": "text", "text": "Hello."}], + "container": None, + "stop_reason": "end_turn", + "stop_sequence": None, + "stop_details": None, + "usage": { + "input_tokens": 21, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + "output_tokens": 6, + "output_tokens_details": {"thinking_tokens": 0}, + }, + }, + expected_litellm_status_code=200, + expected_litellm_response={ + "model": "vertex_ai/claude-sonnet-5@default", + "id": "msg_vrtx_011CfjuyZQJsY8CfdZxY6C9E", + "type": "message", + "role": "assistant", + "content": [{"type": "text", "text": "Hello."}], + "container": None, + "stop_reason": "end_turn", + "stop_sequence": None, + "stop_details": None, + "usage": { + "input_tokens": 21, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + "output_tokens": 6, + "output_tokens_details": {"thinking_tokens": 0}, + }, + }, +) + +CLAUDE_OPUS_4_8_TEST_CASE: Final = TranslationTestCase( + scenario="basic", + litellm_endpoint="/v1/messages", + litellm_request={ + "model": "vertex_ai/claude-opus-4-8@default", + "max_tokens": 64, + "system": "You are a terse assistant.", + "messages": [{"role": "user", "content": "Say hello."}], + "cache": {"no-cache": True}, + }, + expected_provider_endpoint="/v1/projects/synthetic-vertex-project/locations/global/publishers/anthropic/models/claude-opus-4-8@default:rawPredict", + expected_provider_headers={"authorization": "Bearer scripted-token", "content-type": "application/json"}, + expected_provider_request={ + "messages": [{"role": "user", "content": "Say hello."}], + "max_tokens": 64, + "stream": False, + "system": [{"type": "text", "text": "You are a terse assistant."}], + "anthropic_version": "vertex-2023-10-16", + }, + mock_provider_response={ + "model": "claude-opus-4-8", + "id": "msg_vrtx_011Cfjuyk5joSiAW8h6nktJh", + "type": "message", + "role": "assistant", + "content": [{"type": "text", "text": "Hello."}], + "container": None, + "stop_reason": "end_turn", + "stop_sequence": None, + "stop_details": None, + "usage": { + "input_tokens": 21, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + "output_tokens": 6, + "output_tokens_details": {"thinking_tokens": 0}, + }, + }, + expected_litellm_status_code=200, + expected_litellm_response={ + "model": "vertex_ai/claude-opus-4-8@default", + "id": "msg_vrtx_011Cfjuyk5joSiAW8h6nktJh", + "type": "message", + "role": "assistant", + "content": [{"type": "text", "text": "Hello."}], + "container": None, + "stop_reason": "end_turn", + "stop_sequence": None, + "stop_details": None, + "usage": { + "input_tokens": 21, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + "output_tokens": 6, + "output_tokens_details": {"thinking_tokens": 0}, + }, + }, +) + +CLAUDE_SONNET_5_5_TEST_CASE: Final = TranslationTestCase( + scenario="basic", + litellm_endpoint="/v1/messages", + litellm_request={ + "model": "vertex_ai/claude-sonnet-5-5@default", + "max_tokens": 64, + "system": "You are a terse assistant.", + "messages": [{"role": "user", "content": "Say hello."}], + "cache": {"no-cache": True}, + }, + expected_provider_endpoint="/v1/projects/synthetic-vertex-project/locations/global/publishers/anthropic/models/claude-sonnet-5-5@default:rawPredict", + expected_provider_headers={"authorization": "Bearer scripted-token", "content-type": "application/json"}, + expected_provider_request={ + "messages": [{"role": "user", "content": "Say hello."}], + "max_tokens": 64, + "stream": False, + "system": [{"type": "text", "text": "You are a terse assistant."}], + "anthropic_version": "vertex-2023-10-16", + }, + mock_provider_response={ + "model": "claude-sonnet-5-5", + "id": "msg_vrtx_011CfjuysbBy7Cc5quJ5tS6c", + "type": "message", + "role": "assistant", + "content": [{"type": "text", "text": "Hello."}], + "container": None, + "stop_reason": "end_turn", + "stop_sequence": None, + "stop_details": None, + "usage": { + "input_tokens": 23, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + "output_tokens": 6, + "output_tokens_details": {"thinking_tokens": 0}, + }, + }, + expected_litellm_status_code=200, + expected_litellm_response={ + "model": "vertex_ai/claude-sonnet-5-5@default", + "id": "msg_vrtx_011CfjuysbBy7Cc5quJ5tS6c", + "type": "message", + "role": "assistant", + "content": [{"type": "text", "text": "Hello."}], + "container": None, + "stop_reason": "end_turn", + "stop_sequence": None, + "stop_details": None, + "usage": { + "input_tokens": 23, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + "output_tokens": 6, + "output_tokens_details": {"thinking_tokens": 0}, + }, + }, +) + +CLAUDE_OPUS_5_5_TEST_CASE: Final = TranslationTestCase( + scenario="basic", + litellm_endpoint="/v1/messages", + litellm_request={ + "model": "vertex_ai/claude-opus-5-5@default", + "max_tokens": 64, + "system": "You are a terse assistant.", + "messages": [{"role": "user", "content": "Say hello."}], + "cache": {"no-cache": True}, + }, + expected_provider_endpoint="/v1/projects/synthetic-vertex-project/locations/global/publishers/anthropic/models/claude-opus-5-5@default:rawPredict", + expected_provider_headers={"authorization": "Bearer scripted-token", "content-type": "application/json"}, + expected_provider_request={ + "messages": [{"role": "user", "content": "Say hello."}], + "max_tokens": 64, + "stream": False, + "system": [{"type": "text", "text": "You are a terse assistant."}], + "anthropic_version": "vertex-2023-10-16", + }, + mock_provider_response={ + "model": "claude-opus-5-5", + "id": "msg_vrtx_011Cfjuz1zxAEM1YiVFYdaJB", + "type": "message", + "role": "assistant", + "content": [{"type": "text", "text": "Hello."}], + "container": None, + "stop_reason": "end_turn", + "stop_sequence": None, + "stop_details": None, + "usage": { + "input_tokens": 23, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + "output_tokens": 6, + "output_tokens_details": {"thinking_tokens": 0}, + }, + }, + expected_litellm_status_code=200, + expected_litellm_response={ + "model": "vertex_ai/claude-opus-5-5@default", + "id": "msg_vrtx_011Cfjuz1zxAEM1YiVFYdaJB", + "type": "message", + "role": "assistant", + "content": [{"type": "text", "text": "Hello."}], + "container": None, + "stop_reason": "end_turn", + "stop_sequence": None, + "stop_details": None, + "usage": { + "input_tokens": 23, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + "output_tokens": 6, + "output_tokens_details": {"thinking_tokens": 0}, + }, + }, +) + +GEMINI_3_5_FLASH_TEST_CASE: Final = TranslationTestCase( + scenario="basic", + litellm_endpoint="/v1/messages", + litellm_request={ + "model": "vertex_ai/gemini-3.5-flash", + "max_tokens": 1024, + "system": "You are a terse assistant.", + "messages": [{"role": "user", "content": "Say hello."}], + "cache": {"no-cache": True}, + }, + expected_provider_endpoint="/v1/projects/synthetic-vertex-project/locations/global/publishers/google/models/gemini-3.5-flash:generateContent", + expected_provider_headers={"content-type": "application/json", "authorization": "Bearer scripted-token"}, + expected_provider_request={ + "contents": [{"role": "user", "parts": [{"text": "Say hello."}]}], + "system_instruction": {"parts": [{"text": "You are a terse assistant."}]}, + "generationConfig": {"max_output_tokens": 1024, "temperature": 1.0}, + }, + mock_provider_response={ + "candidates": [ + { + "content": { + "role": "model", + "parts": [{"text": "Hello.", "thoughtSignature": GEMINI_3_5_FLASH_THOUGHT_SIGNATURE}], + }, + "finishReason": "STOP", + } + ], + "usageMetadata": { + "promptTokenCount": 9, + "candidatesTokenCount": 2, + "totalTokenCount": 95, + "trafficType": "ON_DEMAND", + "promptTokensDetails": [{"modality": "TEXT", "tokenCount": 9}], + "candidatesTokensDetails": [{"modality": "TEXT", "tokenCount": 2}], + "thoughtsTokenCount": 84, + }, + "modelVersion": "gemini-3.5-flash", + "createTime": "2026-10-05T22:54:50.809002Z", + "responseId": "uirEaqqwMc-U9LsPjJTYuQU", + }, + expected_litellm_status_code=200, + expected_litellm_response={ + "id": "uirEaqqwMc-U9LsPjJTYuQU", + "type": "message", + "role": "assistant", + "model": "vertex_ai/gemini-3.5-flash", + "stop_sequence": None, + "usage": {"input_tokens": 9, "output_tokens": 86}, + "content": [{"type": "text", "text": "Hello."}], + "stop_reason": "end_turn", + "stop_details": None, + }, +) + +GEMINI_3_8_FLASH_TEST_CASE: Final = TranslationTestCase( + scenario="basic", + litellm_endpoint="/v1/messages", + litellm_request={ + "model": "vertex_ai/gemini-3.8-flash", + "max_tokens": 1024, + "system": "You are a terse assistant.", + "messages": [{"role": "user", "content": "Say hello."}], + "cache": {"no-cache": True}, + }, + expected_provider_endpoint="/v1/projects/synthetic-vertex-project/locations/global/publishers/google/models/gemini-3.8-flash:generateContent", + expected_provider_headers={"content-type": "application/json", "authorization": "Bearer scripted-token"}, + expected_provider_request={ + "contents": [{"role": "user", "parts": [{"text": "Say hello."}]}], + "system_instruction": {"parts": [{"text": "You are a terse assistant."}]}, + "generationConfig": {"max_output_tokens": 1024, "temperature": 1.0}, + }, + mock_provider_response={ + "candidates": [ + { + "content": { + "role": "model", + "parts": [{"text": "Hello.", "thoughtSignature": GEMINI_3_8_FLASH_THOUGHT_SIGNATURE}], + }, + "finishReason": "STOP", + } + ], + "usageMetadata": { + "promptTokenCount": 9, + "candidatesTokenCount": 2, + "totalTokenCount": 118, + "trafficType": "ON_DEMAND", + "promptTokensDetails": [{"modality": "TEXT", "tokenCount": 9}], + "candidatesTokensDetails": [{"modality": "TEXT", "tokenCount": 2}], + "thoughtsTokenCount": 107, + }, + "modelVersion": "gemini-3.8-flash", + "createTime": "2026-10-05T22:55:12.739855Z", + "responseId": "0CrEao-ULbuU9LsPk-mQ2A8", + }, + expected_litellm_status_code=200, + expected_litellm_response={ + "id": "0CrEao-ULbuU9LsPk-mQ2A8", + "type": "message", + "role": "assistant", + "model": "vertex_ai/gemini-3.8-flash", + "stop_sequence": None, + "usage": {"input_tokens": 9, "output_tokens": 109}, + "content": [{"type": "text", "text": "Hello."}], + "stop_reason": "end_turn", + "stop_details": None, + }, +) + +GEMINI_3_1_PRO_PREVIEW_TEST_CASE: Final = TranslationTestCase( + scenario="basic", + litellm_endpoint="/v1/messages", + litellm_request={ + "model": "vertex_ai/gemini-3.1-pro-preview", + "max_tokens": 1024, + "system": "You are a terse assistant.", + "messages": [{"role": "user", "content": "Say hello."}], + "cache": {"no-cache": True}, + }, + expected_provider_endpoint="/v1/projects/synthetic-vertex-project/locations/global/publishers/google/models/gemini-3.1-pro-preview:generateContent", + expected_provider_headers={"content-type": "application/json", "authorization": "Bearer scripted-token"}, + expected_provider_request={ + "contents": [{"role": "user", "parts": [{"text": "Say hello."}]}], + "system_instruction": {"parts": [{"text": "You are a terse assistant."}]}, + "generationConfig": {"max_output_tokens": 1024, "temperature": 1.0}, + }, + mock_provider_response={ + "candidates": [ + { + "content": { + "role": "model", + "parts": [{"text": "Hello.", "thoughtSignature": GEMINI_3_1_PRO_PREVIEW_THOUGHT_SIGNATURE}], + }, + "finishReason": "STOP", + } + ], + "usageMetadata": { + "promptTokenCount": 9, + "candidatesTokenCount": 1, + "totalTokenCount": 99, + "trafficType": "ON_DEMAND", + "promptTokensDetails": [{"modality": "TEXT", "tokenCount": 9}], + "candidatesTokensDetails": [{"modality": "TEXT", "tokenCount": 1}], + "thoughtsTokenCount": 89, + }, + "modelVersion": "gemini-3.1-pro-preview", + "createTime": "2026-10-05T22:55:14.874538Z", + "responseId": "0irEaqqwNbCdq8YP3L2mqQ4", + }, + expected_litellm_status_code=200, + expected_litellm_response={ + "id": "0irEaqqwNbCdq8YP3L2mqQ4", + "type": "message", + "role": "assistant", + "model": "vertex_ai/gemini-3.1-pro-preview", + "stop_sequence": None, + "usage": {"input_tokens": 9, "output_tokens": 90}, + "content": [{"type": "text", "text": "Hello."}], + "stop_reason": "end_turn", + "stop_details": None, + }, +) diff --git a/tests/integration/translation/messages/basic/test_messages_basic_vertex_ai.py b/tests/integration/translation/messages/basic/test_messages_basic_vertex_ai.py new file mode 100644 index 00000000000..24e20780909 --- /dev/null +++ b/tests/integration/translation/messages/basic/test_messages_basic_vertex_ai.py @@ -0,0 +1,40 @@ +import pytest + +from integration._support.client import Gateway +from integration._support.provider import SharedProvider +from integration.translation.case import TranslationTestCase +from integration.translation.messages.bases.vertex_ai import ( + CLAUDE_HAIKU_4_5_TEST_CASE, + CLAUDE_SONNET_4_6_TEST_CASE, + CLAUDE_SONNET_5_TEST_CASE, + CLAUDE_OPUS_4_8_TEST_CASE, + CLAUDE_SONNET_5_5_TEST_CASE, + CLAUDE_OPUS_5_5_TEST_CASE, + GEMINI_3_5_FLASH_TEST_CASE, + GEMINI_3_8_FLASH_TEST_CASE, + GEMINI_3_1_PRO_PREVIEW_TEST_CASE, +) +from integration.translation.runner import assert_translation + + +@pytest.mark.parametrize( + "case", + [ + CLAUDE_HAIKU_4_5_TEST_CASE, + CLAUDE_SONNET_4_6_TEST_CASE, + CLAUDE_SONNET_5_TEST_CASE, + CLAUDE_OPUS_4_8_TEST_CASE, + CLAUDE_SONNET_5_5_TEST_CASE, + CLAUDE_OPUS_5_5_TEST_CASE, + GEMINI_3_5_FLASH_TEST_CASE, + GEMINI_3_8_FLASH_TEST_CASE, + GEMINI_3_1_PRO_PREVIEW_TEST_CASE, + ], + ids=lambda case: case.id, +) +def test_messages_basic_vertex_ai( + case: TranslationTestCase, + gateway: Gateway, + provider: SharedProvider, +) -> None: + assert_translation(case, gateway, provider) diff --git a/tests/integration/translation/responses/bases/vertex_ai.py b/tests/integration/translation/responses/bases/vertex_ai.py new file mode 100644 index 00000000000..eb0bf46e9db --- /dev/null +++ b/tests/integration/translation/responses/bases/vertex_ai.py @@ -0,0 +1,854 @@ +from typing import Final +from unittest.mock import ANY + +from integration.translation.case import TranslationTestCase + +GEMINI_3_5_FLASH_THOUGHT_SIGNATURE: Final = "AY89a1+kriNs9YsPegJQxgjTWsMlxsqPU/UkHDyz4EK+3FRrE52ygqPIHTvkblgKKiO5Fn8R9D3iu//i+YnuuZ+e7W0yxUoFScas89b5rn3vZHsihnUA7VTe3b7beth1IftteoTGZ3F8OSLrSIP8HKn4xTIx35eeNEt5oiD3K5rTQrdklqO277HvTDhu/LuZhZ0pzaWciGQEc61epIBBpC2ygu8V4L8TekUVZnz6qzSzxipdiQStQ8dZou43334hMnxOR6JjJCGxqt/1K+C0vXX6YP6f+Bph5VhPSnvy2Z1uApi3TUxAcsDDyVewIr+ha2G6DPhN1Oi5uZZNhmbvH01Uq5OOquF1uEICq2m/35LhJIbDNM9N7oI1Z6L94P9xdugeFXM2S7wdJfc8sdg09vEzNeA++GK25zRuB4hU7XyaG4XnWjmu2kswi4sf+EyrO/K0P2tb/ZCJu7kJTtWTiD1wgSIl1eqS8ORKKsXDN2hIbFrtsNR50WIvVzd6" + +GEMINI_3_8_FLASH_THOUGHT_SIGNATURE: Final = "AY89a1+gmp4bVJhwGOCtbUalmyfhwvgE4EsZOwS4bNR02vurTt4AZC72ypTNLJDRwK8I518tGYBiDnFqD6jTxkjWTQTPGwUZ5IOYr1IskTb3pPKfvZWZ/XunV7kfb9VmEU0TBlgwjVqxXziOxvjediJyb2gbFnCVjGTSVsq0ipmpFznF22lV1I6N9GI7RKBFgYXaSzGU1+fGbLLBigaUD0qbgquAnFv6R3JGpaLcW7EUrhKDinHoFukmUwBLx07S7SfuWBT8hMLMs32vEVzwpns9JmbVtuQG24zs5qQx9sJqtO9E07k6SFL3f1tvaKZsQ6Yjb08Xcuvnh9Pcz4ChCdRJ4vRkmtRW3AgOli8/43rrrAMmvIOqEdAOOvZW3W9D8lnOC+Nfq7S0FTNWPYkIJhrtskWoQkUwDrY6TOzMjIA6Jblt2f9kKU3VJzq7O1hPjbHsJKmLBYP+De8rKx7Ufq9Shs5qLLu4aBBgHleiyFiHGQSD18RDjnyv0DLRyrp7LOkjIYWBYoIA2+BSb0ySMyeLhk8B2U8XZ6AXiw3aHoNUG/wwuE0EhXBhOUBM14FmchGfptXUr+Dn9kZvPAy8mfbq3Pk=" + +GEMINI_3_1_PRO_PREVIEW_THOUGHT_SIGNATURE: Final = "AY89a198sdR5Cy3qgtvOq/o5xsmH1g3WymzyZyB/bv3XeyqNZAgXbCocCAqUS913U8FvVUodNrm3y2B5P/mDsn3A3pINhbEiwJcWA/o8bNJq+Z/1O2UYdViO0FMhOOS1aRaCbza+6aSBLPVQ81QnyxrwH6rdDCuQlwyZ+5/Evc/EOXLVxdUF9PYwTwNJcZPREl4UMcarKvtKsNofkSfa09EWQNVVMDkasNl3YHtQJqYLu5rL7vSUrbMtLLA0sMEMh2/4zMj4gIvtjoOwiI4CBdLLH0Xp8SGGco8UljxikaIp6YvQW4cHsfkaqTBQmLgREudbnKKTZ0MvMR52G/++7r8Iv6BL29+73igMMyHoQad40OM8RY3E6akpSE5uPXH/CgYTmSLXQONtRMXzrXm996JYLaHLOo2dP1RtaJANcjNoDB4eN5vj4GOpTUYqZqRRaB5jMX2od8QcAnaXk2AYs/2rNMaLV4zgraz45gbA0It465nN5hb4JbRH2toJ9K/TvsZY/YyoLmV9RjyAmIhbq6bG0fGatsXgmDaXj/vEsBmqfRyBN31k" + +CLAUDE_HAIKU_4_5_TEST_CASE: Final = TranslationTestCase( + scenario="basic", + litellm_endpoint="/v1/responses", + litellm_request={ + "model": "vertex_ai/claude-haiku-4-5@20251001", + "max_output_tokens": 64, + "instructions": "You are a terse assistant.", + "input": "Say hello.", + "cache": {"no-cache": True}, + }, + expected_provider_endpoint="/v1/projects/synthetic-vertex-project/locations/global/publishers/anthropic/models/claude-haiku-4-5@20251001:rawPredict", + expected_provider_headers={ + "authorization": "Bearer scripted-token", + "anthropic-version": "2023-06-01", + "content-type": "application/json", + "x-api-key": "scripted-token", + }, + expected_provider_request={ + "messages": [{"role": "user", "content": [{"type": "text", "text": "Say hello."}]}], + "max_tokens": 64, + "anthropic_version": "vertex-2023-10-16", + "system": [{"type": "text", "text": "You are a terse assistant."}], + }, + mock_provider_response={ + "model": "claude-haiku-4-5-20251001", + "id": "msg_vrtx_011CfjuySsPewSSfasuPiaHg", + "type": "message", + "role": "assistant", + "content": [{"type": "text", "text": "Hello."}], + "container": None, + "stop_reason": "end_turn", + "stop_sequence": None, + "stop_details": None, + "usage": { + "input_tokens": 17, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + "output_tokens": 5, + }, + }, + expected_litellm_status_code=200, + expected_litellm_response={ + "id": ANY, + "created_at": ANY, + "error": None, + "incomplete_details": None, + "instructions": "You are a terse assistant.", + "metadata": {}, + "model": "vertex_ai/claude-haiku-4-5@20251001", + "object": "response", + "output": [ + { + "type": "message", + "id": ANY, + "status": "completed", + "role": "assistant", + "content": [{"type": "output_text", "text": "Hello.", "annotations": []}], + "phase": None, + } + ], + "parallel_tool_calls": False, + "temperature": None, + "tool_choice": "auto", + "tools": [], + "top_p": None, + "max_output_tokens": 64, + "previous_response_id": None, + "reasoning": None, + "status": "completed", + "text": {}, + "truncation": None, + "usage": { + "input_tokens": 17, + "input_tokens_details": { + "audio_tokens": None, + "cached_tokens": 0, + "cached_tokens_details": None, + "image_tokens": None, + "text_tokens": 17, + "video_tokens": None, + "cache_write_tokens": 0, + }, + "output_tokens": 5, + "output_tokens_details": {"audio_tokens": None, "reasoning_tokens": 0, "text_tokens": 5}, + "total_tokens": 22, + "cost": None, + }, + "user": None, + "store": None, + "provider_specific_fields": {"citations": None, "thinking_blocks": None}, + }, +) + +CLAUDE_SONNET_4_6_TEST_CASE: Final = TranslationTestCase( + scenario="basic", + litellm_endpoint="/v1/responses", + litellm_request={ + "model": "vertex_ai/claude-sonnet-4-6@default", + "max_output_tokens": 64, + "instructions": "You are a terse assistant.", + "input": "Say hello.", + "cache": {"no-cache": True}, + }, + expected_provider_endpoint="/v1/projects/synthetic-vertex-project/locations/global/publishers/anthropic/models/claude-sonnet-4-6@default:rawPredict", + expected_provider_headers={ + "authorization": "Bearer scripted-token", + "anthropic-version": "2023-06-01", + "content-type": "application/json", + "x-api-key": "scripted-token", + }, + expected_provider_request={ + "messages": [{"role": "user", "content": [{"type": "text", "text": "Say hello."}]}], + "max_tokens": 64, + "anthropic_version": "vertex-2023-10-16", + "system": [{"type": "text", "text": "You are a terse assistant."}], + }, + mock_provider_response={ + "model": "claude-sonnet-4-6", + "id": "msg_vrtx_011CfjuxUbJsRvQZCN21qNQq", + "type": "message", + "role": "assistant", + "content": [{"type": "text", "text": "Hello!"}], + "container": None, + "stop_reason": "end_turn", + "stop_sequence": None, + "stop_details": None, + "usage": { + "input_tokens": 18, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + "output_tokens": 5, + }, + }, + expected_litellm_status_code=200, + expected_litellm_response={ + "id": ANY, + "created_at": ANY, + "error": None, + "incomplete_details": None, + "instructions": "You are a terse assistant.", + "metadata": {}, + "model": "vertex_ai/claude-sonnet-4-6@default", + "object": "response", + "output": [ + { + "type": "message", + "id": ANY, + "status": "completed", + "role": "assistant", + "content": [{"type": "output_text", "text": "Hello!", "annotations": []}], + "phase": None, + } + ], + "parallel_tool_calls": False, + "temperature": None, + "tool_choice": "auto", + "tools": [], + "top_p": None, + "max_output_tokens": 64, + "previous_response_id": None, + "reasoning": None, + "status": "completed", + "text": {}, + "truncation": None, + "usage": { + "input_tokens": 18, + "input_tokens_details": { + "audio_tokens": None, + "cached_tokens": 0, + "cached_tokens_details": None, + "image_tokens": None, + "text_tokens": 18, + "video_tokens": None, + "cache_write_tokens": 0, + }, + "output_tokens": 5, + "output_tokens_details": {"audio_tokens": None, "reasoning_tokens": 0, "text_tokens": 5}, + "total_tokens": 23, + "cost": None, + }, + "user": None, + "store": None, + "provider_specific_fields": {"citations": None, "thinking_blocks": None}, + }, +) + +CLAUDE_SONNET_5_TEST_CASE: Final = TranslationTestCase( + scenario="basic", + litellm_endpoint="/v1/responses", + litellm_request={ + "model": "vertex_ai/claude-sonnet-5@default", + "max_output_tokens": 64, + "instructions": "You are a terse assistant.", + "input": "Say hello.", + "cache": {"no-cache": True}, + }, + expected_provider_endpoint="/v1/projects/synthetic-vertex-project/locations/global/publishers/anthropic/models/claude-sonnet-5@default:rawPredict", + expected_provider_headers={ + "authorization": "Bearer scripted-token", + "anthropic-version": "2023-06-01", + "content-type": "application/json", + "x-api-key": "scripted-token", + }, + expected_provider_request={ + "messages": [{"role": "user", "content": [{"type": "text", "text": "Say hello."}]}], + "max_tokens": 64, + "anthropic_version": "vertex-2023-10-16", + "system": [{"type": "text", "text": "You are a terse assistant."}], + }, + mock_provider_response={ + "model": "claude-sonnet-5", + "id": "msg_vrtx_011CfjuygPVDgzuMXdyyW9UP", + "type": "message", + "role": "assistant", + "content": [{"type": "text", "text": "Hello."}], + "container": None, + "stop_reason": "end_turn", + "stop_sequence": None, + "stop_details": None, + "usage": { + "input_tokens": 21, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + "output_tokens": 6, + "output_tokens_details": {"thinking_tokens": 0}, + }, + }, + expected_litellm_status_code=200, + expected_litellm_response={ + "id": ANY, + "created_at": ANY, + "error": None, + "incomplete_details": None, + "instructions": "You are a terse assistant.", + "metadata": {}, + "model": "vertex_ai/claude-sonnet-5@default", + "object": "response", + "output": [ + { + "type": "message", + "id": ANY, + "status": "completed", + "role": "assistant", + "content": [{"type": "output_text", "text": "Hello.", "annotations": []}], + "phase": None, + } + ], + "parallel_tool_calls": False, + "temperature": None, + "tool_choice": "auto", + "tools": [], + "top_p": None, + "max_output_tokens": 64, + "previous_response_id": None, + "reasoning": None, + "status": "completed", + "text": {}, + "truncation": None, + "usage": { + "input_tokens": 21, + "input_tokens_details": { + "audio_tokens": None, + "cached_tokens": 0, + "cached_tokens_details": None, + "image_tokens": None, + "text_tokens": 21, + "video_tokens": None, + "cache_write_tokens": 0, + }, + "output_tokens": 6, + "output_tokens_details": {"audio_tokens": None, "reasoning_tokens": 0, "text_tokens": 6}, + "total_tokens": 27, + "cost": None, + }, + "user": None, + "store": None, + "provider_specific_fields": {"citations": None, "thinking_blocks": None}, + }, +) + +CLAUDE_OPUS_4_8_TEST_CASE: Final = TranslationTestCase( + scenario="basic", + litellm_endpoint="/v1/responses", + litellm_request={ + "model": "vertex_ai/claude-opus-4-8@default", + "max_output_tokens": 64, + "instructions": "You are a terse assistant.", + "input": "Say hello.", + "cache": {"no-cache": True}, + }, + expected_provider_endpoint="/v1/projects/synthetic-vertex-project/locations/global/publishers/anthropic/models/claude-opus-4-8@default:rawPredict", + expected_provider_headers={ + "authorization": "Bearer scripted-token", + "anthropic-version": "2023-06-01", + "content-type": "application/json", + "x-api-key": "scripted-token", + }, + expected_provider_request={ + "messages": [{"role": "user", "content": [{"type": "text", "text": "Say hello."}]}], + "max_tokens": 64, + "anthropic_version": "vertex-2023-10-16", + "system": [{"type": "text", "text": "You are a terse assistant."}], + }, + mock_provider_response={ + "model": "claude-opus-4-8", + "id": "msg_vrtx_011CfjuyofoMgSaV8Pbt16N9", + "type": "message", + "role": "assistant", + "content": [{"type": "text", "text": "Hello."}], + "container": None, + "stop_reason": "end_turn", + "stop_sequence": None, + "stop_details": None, + "usage": { + "input_tokens": 21, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + "output_tokens": 6, + "output_tokens_details": {"thinking_tokens": 0}, + }, + }, + expected_litellm_status_code=200, + expected_litellm_response={ + "id": ANY, + "created_at": ANY, + "error": None, + "incomplete_details": None, + "instructions": "You are a terse assistant.", + "metadata": {}, + "model": "vertex_ai/claude-opus-4-8@default", + "object": "response", + "output": [ + { + "type": "message", + "id": ANY, + "status": "completed", + "role": "assistant", + "content": [{"type": "output_text", "text": "Hello.", "annotations": []}], + "phase": None, + } + ], + "parallel_tool_calls": False, + "temperature": None, + "tool_choice": "auto", + "tools": [], + "top_p": None, + "max_output_tokens": 64, + "previous_response_id": None, + "reasoning": None, + "status": "completed", + "text": {}, + "truncation": None, + "usage": { + "input_tokens": 21, + "input_tokens_details": { + "audio_tokens": None, + "cached_tokens": 0, + "cached_tokens_details": None, + "image_tokens": None, + "text_tokens": 21, + "video_tokens": None, + "cache_write_tokens": 0, + }, + "output_tokens": 6, + "output_tokens_details": {"audio_tokens": None, "reasoning_tokens": 0, "text_tokens": 6}, + "total_tokens": 27, + "cost": None, + }, + "user": None, + "store": None, + "provider_specific_fields": {"citations": None, "thinking_blocks": None}, + }, +) + +CLAUDE_SONNET_5_5_TEST_CASE: Final = TranslationTestCase( + scenario="basic", + litellm_endpoint="/v1/responses", + litellm_request={ + "model": "vertex_ai/claude-sonnet-5-5@default", + "max_output_tokens": 64, + "instructions": "You are a terse assistant.", + "input": "Say hello.", + "cache": {"no-cache": True}, + }, + expected_provider_endpoint="/v1/projects/synthetic-vertex-project/locations/global/publishers/anthropic/models/claude-sonnet-5-5@default:rawPredict", + expected_provider_headers={ + "authorization": "Bearer scripted-token", + "anthropic-version": "2023-06-01", + "content-type": "application/json", + "x-api-key": "scripted-token", + }, + expected_provider_request={ + "messages": [{"role": "user", "content": [{"type": "text", "text": "Say hello."}]}], + "max_tokens": 64, + "anthropic_version": "vertex-2023-10-16", + "system": [{"type": "text", "text": "You are a terse assistant."}], + }, + mock_provider_response={ + "model": "claude-sonnet-5-5", + "id": "msg_vrtx_011CfjuywExcu3BdZ3x1KzVJ", + "type": "message", + "role": "assistant", + "content": [{"type": "text", "text": "Hello."}], + "container": None, + "stop_reason": "end_turn", + "stop_sequence": None, + "stop_details": None, + "usage": { + "input_tokens": 23, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + "output_tokens": 6, + "output_tokens_details": {"thinking_tokens": 0}, + }, + }, + expected_litellm_status_code=200, + expected_litellm_response={ + "id": ANY, + "created_at": ANY, + "error": None, + "incomplete_details": None, + "instructions": "You are a terse assistant.", + "metadata": {}, + "model": "vertex_ai/claude-sonnet-5-5@default", + "object": "response", + "output": [ + { + "type": "message", + "id": ANY, + "status": "completed", + "role": "assistant", + "content": [{"type": "output_text", "text": "Hello.", "annotations": []}], + "phase": None, + } + ], + "parallel_tool_calls": False, + "temperature": None, + "tool_choice": "auto", + "tools": [], + "top_p": None, + "max_output_tokens": 64, + "previous_response_id": None, + "reasoning": None, + "status": "completed", + "text": {}, + "truncation": None, + "usage": { + "input_tokens": 23, + "input_tokens_details": { + "audio_tokens": None, + "cached_tokens": 0, + "cached_tokens_details": None, + "image_tokens": None, + "text_tokens": 23, + "video_tokens": None, + "cache_write_tokens": 0, + }, + "output_tokens": 6, + "output_tokens_details": {"audio_tokens": None, "reasoning_tokens": 0, "text_tokens": 6}, + "total_tokens": 29, + "cost": None, + }, + "user": None, + "store": None, + "provider_specific_fields": {"citations": None, "thinking_blocks": None}, + }, +) + +CLAUDE_OPUS_5_5_TEST_CASE: Final = TranslationTestCase( + scenario="basic", + litellm_endpoint="/v1/responses", + litellm_request={ + "model": "vertex_ai/claude-opus-5-5@default", + "max_output_tokens": 64, + "instructions": "You are a terse assistant.", + "input": "Say hello.", + "cache": {"no-cache": True}, + }, + expected_provider_endpoint="/v1/projects/synthetic-vertex-project/locations/global/publishers/anthropic/models/claude-opus-5-5@default:rawPredict", + expected_provider_headers={ + "authorization": "Bearer scripted-token", + "anthropic-version": "2023-06-01", + "content-type": "application/json", + "x-api-key": "scripted-token", + }, + expected_provider_request={ + "messages": [{"role": "user", "content": [{"type": "text", "text": "Say hello."}]}], + "max_tokens": 64, + "anthropic_version": "vertex-2023-10-16", + "system": [{"type": "text", "text": "You are a terse assistant."}], + }, + mock_provider_response={ + "model": "claude-opus-5-5", + "id": "msg_vrtx_011Cfjuz6qugj8FbC3fcXdun", + "type": "message", + "role": "assistant", + "content": [{"type": "text", "text": "Hello."}], + "container": None, + "stop_reason": "end_turn", + "stop_sequence": None, + "stop_details": None, + "usage": { + "input_tokens": 23, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, + "output_tokens": 6, + "output_tokens_details": {"thinking_tokens": 0}, + }, + }, + expected_litellm_status_code=200, + expected_litellm_response={ + "id": ANY, + "created_at": ANY, + "error": None, + "incomplete_details": None, + "instructions": "You are a terse assistant.", + "metadata": {}, + "model": "vertex_ai/claude-opus-5-5@default", + "object": "response", + "output": [ + { + "type": "message", + "id": ANY, + "status": "completed", + "role": "assistant", + "content": [{"type": "output_text", "text": "Hello.", "annotations": []}], + "phase": None, + } + ], + "parallel_tool_calls": False, + "temperature": None, + "tool_choice": "auto", + "tools": [], + "top_p": None, + "max_output_tokens": 64, + "previous_response_id": None, + "reasoning": None, + "status": "completed", + "text": {}, + "truncation": None, + "usage": { + "input_tokens": 23, + "input_tokens_details": { + "audio_tokens": None, + "cached_tokens": 0, + "cached_tokens_details": None, + "image_tokens": None, + "text_tokens": 23, + "video_tokens": None, + "cache_write_tokens": 0, + }, + "output_tokens": 6, + "output_tokens_details": {"audio_tokens": None, "reasoning_tokens": 0, "text_tokens": 6}, + "total_tokens": 29, + "cost": None, + }, + "user": None, + "store": None, + "provider_specific_fields": {"citations": None, "thinking_blocks": None}, + }, +) + +GEMINI_3_5_FLASH_TEST_CASE: Final = TranslationTestCase( + scenario="basic", + litellm_endpoint="/v1/responses", + litellm_request={ + "model": "vertex_ai/gemini-3.5-flash", + "max_output_tokens": 1024, + "instructions": "You are a terse assistant.", + "input": "Say hello.", + "cache": {"no-cache": True}, + }, + expected_provider_endpoint="/v1/projects/synthetic-vertex-project/locations/global/publishers/google/models/gemini-3.5-flash:generateContent", + expected_provider_headers={"content-type": "application/json", "authorization": "Bearer scripted-token"}, + expected_provider_request={ + "contents": [{"role": "user", "parts": [{"text": "Say hello."}]}], + "system_instruction": {"parts": [{"text": "You are a terse assistant."}]}, + "generationConfig": {"max_output_tokens": 1024, "temperature": 1.0}, + }, + mock_provider_response={ + "candidates": [ + { + "content": { + "role": "model", + "parts": [{"text": "Hello.", "thoughtSignature": GEMINI_3_5_FLASH_THOUGHT_SIGNATURE}], + }, + "finishReason": "STOP", + } + ], + "usageMetadata": { + "promptTokenCount": 9, + "candidatesTokenCount": 2, + "totalTokenCount": 95, + "trafficType": "ON_DEMAND", + "promptTokensDetails": [{"modality": "TEXT", "tokenCount": 9}], + "candidatesTokensDetails": [{"modality": "TEXT", "tokenCount": 2}], + "thoughtsTokenCount": 84, + }, + "modelVersion": "gemini-3.5-flash", + "createTime": "2026-10-05T22:54:50.809002Z", + "responseId": "uirEaqqwMc-U9LsPjJTYuQU", + }, + expected_litellm_status_code=200, + expected_litellm_response={ + "id": ANY, + "created_at": ANY, + "error": None, + "incomplete_details": None, + "instructions": "You are a terse assistant.", + "metadata": {}, + "model": "vertex_ai/gemini-3.5-flash", + "object": "response", + "output": [ + { + "type": "message", + "id": ANY, + "status": "completed", + "role": "assistant", + "content": [{"type": "output_text", "text": "Hello.", "annotations": []}], + "phase": None, + } + ], + "parallel_tool_calls": False, + "temperature": None, + "tool_choice": "auto", + "tools": [], + "top_p": None, + "max_output_tokens": 1024, + "previous_response_id": None, + "reasoning": None, + "status": "completed", + "text": {}, + "truncation": None, + "usage": { + "input_tokens": 9, + "input_tokens_details": { + "audio_tokens": None, + "cached_tokens": 0, + "cached_tokens_details": None, + "image_tokens": None, + "text_tokens": 9, + "video_tokens": None, + }, + "output_tokens": 86, + "output_tokens_details": {"audio_tokens": None, "reasoning_tokens": 84, "text_tokens": 2}, + "total_tokens": 95, + "cost": None, + }, + "user": None, + "store": None, + "provider_specific_fields": {"traffic_type": "ON_DEMAND"}, + }, +) + +GEMINI_3_8_FLASH_TEST_CASE: Final = TranslationTestCase( + scenario="basic", + litellm_endpoint="/v1/responses", + litellm_request={ + "model": "vertex_ai/gemini-3.8-flash", + "max_output_tokens": 1024, + "instructions": "You are a terse assistant.", + "input": "Say hello.", + "cache": {"no-cache": True}, + }, + expected_provider_endpoint="/v1/projects/synthetic-vertex-project/locations/global/publishers/google/models/gemini-3.8-flash:generateContent", + expected_provider_headers={"content-type": "application/json", "authorization": "Bearer scripted-token"}, + expected_provider_request={ + "contents": [{"role": "user", "parts": [{"text": "Say hello."}]}], + "system_instruction": {"parts": [{"text": "You are a terse assistant."}]}, + "generationConfig": {"max_output_tokens": 1024, "temperature": 1.0}, + }, + mock_provider_response={ + "candidates": [ + { + "content": { + "role": "model", + "parts": [{"text": "Hello.", "thoughtSignature": GEMINI_3_8_FLASH_THOUGHT_SIGNATURE}], + }, + "finishReason": "STOP", + } + ], + "usageMetadata": { + "promptTokenCount": 9, + "candidatesTokenCount": 2, + "totalTokenCount": 118, + "trafficType": "ON_DEMAND", + "promptTokensDetails": [{"modality": "TEXT", "tokenCount": 9}], + "candidatesTokensDetails": [{"modality": "TEXT", "tokenCount": 2}], + "thoughtsTokenCount": 107, + }, + "modelVersion": "gemini-3.8-flash", + "createTime": "2026-10-05T22:55:12.739855Z", + "responseId": "0CrEao-ULbuU9LsPk-mQ2A8", + }, + expected_litellm_status_code=200, + expected_litellm_response={ + "id": ANY, + "created_at": ANY, + "error": None, + "incomplete_details": None, + "instructions": "You are a terse assistant.", + "metadata": {}, + "model": "vertex_ai/gemini-3.8-flash", + "object": "response", + "output": [ + { + "type": "message", + "id": ANY, + "status": "completed", + "role": "assistant", + "content": [{"type": "output_text", "text": "Hello.", "annotations": []}], + "phase": None, + } + ], + "parallel_tool_calls": False, + "temperature": None, + "tool_choice": "auto", + "tools": [], + "top_p": None, + "max_output_tokens": 1024, + "previous_response_id": None, + "reasoning": None, + "status": "completed", + "text": {}, + "truncation": None, + "usage": { + "input_tokens": 9, + "input_tokens_details": { + "audio_tokens": None, + "cached_tokens": 0, + "cached_tokens_details": None, + "image_tokens": None, + "text_tokens": 9, + "video_tokens": None, + }, + "output_tokens": 109, + "output_tokens_details": {"audio_tokens": None, "reasoning_tokens": 107, "text_tokens": 2}, + "total_tokens": 118, + "cost": None, + }, + "user": None, + "store": None, + "provider_specific_fields": {"traffic_type": "ON_DEMAND"}, + }, +) + +GEMINI_3_1_PRO_PREVIEW_TEST_CASE: Final = TranslationTestCase( + scenario="basic", + litellm_endpoint="/v1/responses", + litellm_request={ + "model": "vertex_ai/gemini-3.1-pro-preview", + "max_output_tokens": 1024, + "instructions": "You are a terse assistant.", + "input": "Say hello.", + "cache": {"no-cache": True}, + }, + expected_provider_endpoint="/v1/projects/synthetic-vertex-project/locations/global/publishers/google/models/gemini-3.1-pro-preview:generateContent", + expected_provider_headers={"content-type": "application/json", "authorization": "Bearer scripted-token"}, + expected_provider_request={ + "contents": [{"role": "user", "parts": [{"text": "Say hello."}]}], + "system_instruction": {"parts": [{"text": "You are a terse assistant."}]}, + "generationConfig": {"max_output_tokens": 1024, "temperature": 1.0}, + }, + mock_provider_response={ + "candidates": [ + { + "content": { + "role": "model", + "parts": [{"text": "Hello.", "thoughtSignature": GEMINI_3_1_PRO_PREVIEW_THOUGHT_SIGNATURE}], + }, + "finishReason": "STOP", + } + ], + "usageMetadata": { + "promptTokenCount": 9, + "candidatesTokenCount": 1, + "totalTokenCount": 99, + "trafficType": "ON_DEMAND", + "promptTokensDetails": [{"modality": "TEXT", "tokenCount": 9}], + "candidatesTokensDetails": [{"modality": "TEXT", "tokenCount": 1}], + "thoughtsTokenCount": 89, + }, + "modelVersion": "gemini-3.1-pro-preview", + "createTime": "2026-10-05T22:55:14.874538Z", + "responseId": "0irEaqqwNbCdq8YP3L2mqQ4", + }, + expected_litellm_status_code=200, + expected_litellm_response={ + "id": ANY, + "created_at": ANY, + "error": None, + "incomplete_details": None, + "instructions": "You are a terse assistant.", + "metadata": {}, + "model": "vertex_ai/gemini-3.1-pro-preview", + "object": "response", + "output": [ + { + "type": "message", + "id": ANY, + "status": "completed", + "role": "assistant", + "content": [{"type": "output_text", "text": "Hello.", "annotations": []}], + "phase": None, + } + ], + "parallel_tool_calls": False, + "temperature": None, + "tool_choice": "auto", + "tools": [], + "top_p": None, + "max_output_tokens": 1024, + "previous_response_id": None, + "reasoning": None, + "status": "completed", + "text": {}, + "truncation": None, + "usage": { + "input_tokens": 9, + "input_tokens_details": { + "audio_tokens": None, + "cached_tokens": 0, + "cached_tokens_details": None, + "image_tokens": None, + "text_tokens": 9, + "video_tokens": None, + }, + "output_tokens": 90, + "output_tokens_details": {"audio_tokens": None, "reasoning_tokens": 89, "text_tokens": 1}, + "total_tokens": 99, + "cost": None, + }, + "user": None, + "store": None, + "provider_specific_fields": {"traffic_type": "ON_DEMAND"}, + }, +) diff --git a/tests/integration/translation/responses/basic/test_responses_basic_vertex_ai.py b/tests/integration/translation/responses/basic/test_responses_basic_vertex_ai.py new file mode 100644 index 00000000000..9deace0e2d8 --- /dev/null +++ b/tests/integration/translation/responses/basic/test_responses_basic_vertex_ai.py @@ -0,0 +1,40 @@ +import pytest + +from integration._support.client import Gateway +from integration._support.provider import SharedProvider +from integration.translation.case import TranslationTestCase +from integration.translation.responses.bases.vertex_ai import ( + CLAUDE_HAIKU_4_5_TEST_CASE, + CLAUDE_SONNET_4_6_TEST_CASE, + CLAUDE_SONNET_5_TEST_CASE, + CLAUDE_OPUS_4_8_TEST_CASE, + CLAUDE_SONNET_5_5_TEST_CASE, + CLAUDE_OPUS_5_5_TEST_CASE, + GEMINI_3_5_FLASH_TEST_CASE, + GEMINI_3_8_FLASH_TEST_CASE, + GEMINI_3_1_PRO_PREVIEW_TEST_CASE, +) +from integration.translation.runner import assert_translation + + +@pytest.mark.parametrize( + "case", + [ + CLAUDE_HAIKU_4_5_TEST_CASE, + CLAUDE_SONNET_4_6_TEST_CASE, + CLAUDE_SONNET_5_TEST_CASE, + CLAUDE_OPUS_4_8_TEST_CASE, + CLAUDE_SONNET_5_5_TEST_CASE, + CLAUDE_OPUS_5_5_TEST_CASE, + GEMINI_3_5_FLASH_TEST_CASE, + GEMINI_3_8_FLASH_TEST_CASE, + GEMINI_3_1_PRO_PREVIEW_TEST_CASE, + ], + ids=lambda case: case.id, +) +def test_responses_basic_vertex_ai( + case: TranslationTestCase, + gateway: Gateway, + provider: SharedProvider, +) -> None: + assert_translation(case, gateway, provider)