Build the upstream URL for a provider route. `stream_response` selects between the unary and streaming Vertex AI Anthropic endpoint suffixes. Pass the same value used for the enclosing [`prepare_backend_request`] call. See that function's documentation for the full caller table. Behavior matrix (`request_path_override`, `model_in_path`): - `(Some(suffix), true)`: `{endpoint}/{model_id}{suffix}`
(
route: &ResolvedRoute,
model_id: &str,
protocol_path: &str,
stream_response: bool,
)
| 762 | /// Model embedded before protocol path. |
| 763 | /// - `(None, false)`: delegates to `build_backend_url` (default, with /v1 dedup). |
| 764 | fn build_provider_url( |
| 765 | route: &ResolvedRoute, |
| 766 | model_id: &str, |
| 767 | protocol_path: &str, |
| 768 | stream_response: bool, |
| 769 | ) -> String { |
| 770 | let base = route.endpoint.trim_end_matches('/'); |
| 771 | match (&route.request_path_override, route.model_in_path) { |
| 772 | // Vertex AI publisher endpoint: model in URL path with suffix |
| 773 | // e.g. .../publishers/anthropic/models/claude-3-5-sonnet@20241022:rawPredict |
| 774 | (Some(suffix), true) => { |
| 775 | // suffix is appended directly after model_id (e.g. ":rawPredict"). |
| 776 | // It must not start with '/' — use the (Some, false) arm for path overrides. |
| 777 | debug_assert!( |
| 778 | !suffix.starts_with('/'), |
| 779 | "suffix in model_in_path branch must not start with '/'; got: {suffix:?}" |
| 780 | ); |
| 781 | let suffix = if stream_response |
| 782 | && suffix == ":rawPredict" |
| 783 | && is_vertex_anthropic_rawpredict_route(route) |
| 784 | { |
| 785 | ":streamRawPredict" |
| 786 | } else { |
| 787 | suffix.as_str() |
| 788 | }; |
| 789 | format!("{base}/{model_id}{suffix}") |
| 790 | } |
| 791 | // Explicit path override, model NOT in URL. |
| 792 | // Normalize: ensure override_path begins with '/' so the concatenation |
| 793 | // never produces a broken URL like `https://host.compath`. |
| 794 | (Some(override_path), false) => { |
| 795 | if override_path.starts_with('/') || override_path.is_empty() { |
| 796 | format!("{base}{override_path}") |
| 797 | } else { |
| 798 | format!("{base}/{override_path}") |
| 799 | } |
| 800 | } |
| 801 | // Model in path, no override — append model then protocol-derived path |
| 802 | (None, true) => { |
| 803 | let path = protocol_path.trim_start_matches('/'); |
| 804 | format!("{base}/{model_id}/{path}") |
| 805 | } |
| 806 | // Default: existing behavior (includes /v1 deduplication) |
| 807 | (None, false) => build_backend_url(&route.endpoint, protocol_path), |
| 808 | } |
| 809 | } |
| 810 | |
| 811 | fn build_backend_url(endpoint: &str, path: &str) -> String { |
| 812 | let base = endpoint.trim_end_matches('/'); |