Fix chat CLI management URLs with /v1 base
Author: VectorPeak
Pull request: yes

﻿<!-- ci-dashboard-badge:start -->
[![CI](https://transformers-ci.lor-e.huggingface.cool/badge/pr?pr=47140)](https://transformers-ci.lor-e.huggingface.cool/d/pytest-observability-by-pr/pytest-observability-branch?var-pr=47140)
<!-- ci-dashboard-badge:end -->

# What does this PR do?

Fixes #47139

This PR fixes a `transformers chat` routing mismatch when the CLI is given an OpenAI-compatible API base URL with a root `/v1` path, for example:

```bash
transformers chat HuggingFaceTB/SmolLM3-3B http://localhost:8000/v1
```

## What Problem This Solves

`transformers chat` accepts a `base_url` that can point at the OpenAI-compatible inference API. In that setup, `/v1` belongs to the inference surface: it is the prefix used for endpoints such as `/v1/chat/completions`.

The issue is that the chat CLI also used the same user-provided `base_url` to build local `transformers serve` management requests. When `base_url` was `http://localhost:8000/v1`, those management calls could be constructed as:

```text
GET  http://localhost:8000/v1/health
POST http://localhost:8000/v1/load_model
```

Issue #47139 reports that these management endpoints are expected at the service root instead:

```text
GET  http://localhost:8000/health
POST http://localhost:8000/load_model
```

So the failure mode is not that `/v1` is wrong in general. `/v1` is still the right API prefix for the OpenAI-compatible inference client. The bug is that the CLI was treating one URL as if it could serve both roles: the public inference API base and the local management endpoint base.

## Changes

This PR separates those two URL roles inside `transformers chat`:

- keeps the original user-provided `base_url` unchanged for `AsyncInferenceClient`
- derives a separate management base URL for health checks and model-loading calls
- strips only an exact root `/v1` path when building management endpoint URLs
- preserves unrelated paths such as `/proxy/v1`, so proxy-mounted or non-root paths are not rewritten broadly

The fix is intentionally client-side and narrow. It does not add `/v1/health` or `/v1/load_model` aliases to the server, and it does not change the OpenAI-compatible inference route layout.

## Evidence

Before this change, a `/v1` API base could leak into management endpoint construction:

```text
base_url = http://localhost:8000/v1
health   = http://localhost:8000/v1/health
load     = http://localhost:8000/v1/load_model
```

After this change, the CLI preserves `/v1` for inference while deriving root management URLs for the affected local management calls:

```text
base_url            = http://localhost:8000/v1
management_base_url = http://localhost:8000
health              = http://localhost:8000/health
load                = http://localhost:8000/load_model
```

The added tests cover the normalization helper and both affected call sites:

- `_get_management_base_url("http://localhost:8000/v1")` returns `http://localhost:8000`
- `_get_management_base_url("http://localhost:8000/v1/")` returns `http://localhost:8000`
- `_get_management_base_url("http://localhost:8000/proxy/v1")` remains `http://localhost:8000/proxy/v1`
- `Chat.check_health("http://localhost:8000/v1")` calls `http://localhost:8000/health`
- `RichInterface(..., base_url="http://localhost:8000/v1").print_model_load(...)` posts to `http://localhost:8000/load_model`

## Possible call chain / impact

The affected CLI path is:

```text
transformers chat <model_id> http://localhost:8000/v1
  -> Chat.__init__(..., base_url="http://localhost:8000/v1")
    -> health check management request
  -> Chat._inner_run()
    -> RichInterface(...).print_model_load(...)
      -> model-loading management request
  -> AsyncInferenceClient(base_url=self.base_url)
    -> OpenAI-compatible inference request under /v1
```

This PR changes only the management URL derivation used by `transformers chat`. It does not change chat history handling, generation settings, the inference client base URL, server route registration, or normal behavior for base URLs that do not use an exact root `/v1` path.

AI assistance was used to help diagnose the issue and draft the implementation. This is not a pure code-agent PR: the human submitter reviewed the issue, requested the fix, and is responsible for the submitted change.

Duplicate-work check: I searched issues and PRs for `chat base_url v1 health load_model` and did not find an obvious existing issue or PR for this specific CLI management endpoint routing problem.
## Code Agent Policy

The Transformers repo is currently being overwhelmed by a large number of PRs and issue comments written by
code agents. We are currently bottlenecked by our ability to review and respond to them. As a result, 
**we ask that new users do not submit pure code agent PRs** at this time. 
You may use code agents in drafting or to help you diagnose issues. We'd also ask autonomous "OpenClaw"-like agents
not to open any PRs or issues for the moment.

PRs that appear to be fully agent-written will probably be closed without review, and we may block users who do this
repeatedly or maliciously. 

This is a rapidly-evolving situation that's causing significant shockwaves in the open-source community. As a result, 
this policy is likely to be updated regularly in the near future. For more information, please read [`CONTRIBUTING.md`](https://github.com/huggingface/transformers/blob/main/CONTRIBUTING.md).

- [x] I confirm that this is not a pure code agent PR.

## Before submitting
- [ ] This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case).
- [x] Did you read the [contributor guideline](https://huggingface.co/docs/transformers/contributing) and the
      [Pull Request](https://huggingface.co/docs/transformers/pr_checks) checks?
- [x] Was this discussed/approved via a Github issue or the [forum](https://discuss.huggingface.co/)? Please add a link
      to it if that's the case. Issue: #47139.
- [x] Did you make sure to update the documentation with your changes according to the [guidelines](https://github.com/huggingface/transformers/tree/main/docs)?
      No documentation update needed; this is a narrow CLI routing fix that makes the documented `/v1` base URL work with root-level management endpoints.
- [x] Did you write any new necessary [tests](https://huggingface.co/docs/transformers/testing)?

## Evidence

A user following the CLI help can pass:

```bash
transformers chat HuggingFaceTB/SmolLM3-3B http://localhost:8000/v1
```

Before this change, the CLI could use that API base URL for management endpoints as well:

```text
GET  http://localhost:8000/v1/health
POST http://localhost:8000/v1/load_model
```

Those routes do not match the serve app's management endpoint layout. This PR routes those management calls to the service root instead:

```text
GET  http://localhost:8000/health
POST http://localhost:8000/load_model
```

while preserving the original `/v1` base URL for the OpenAI-compatible inference client.

## Validation

- `python -m pytest tests/cli/test_chat.py -q` - passed, 7 tests
- `python -m ruff check src/transformers/cli/chat.py tests/cli/test_chat.py` - passed
- `python -m ruff format --check src/transformers/cli/chat.py tests/cli/test_chat.py` - passed
- `git diff --check` - passed

## Who can review?

Anyone in the community is free to review the PR once the tests have passed.


Comments:
<!-- ci-dashboard-recap:start -->

---

### CI recap

**Dashboard:** [View test results in Grafana](https://transformers-ci.lor-e.huggingface.cool/d/pytest-observability-by-pr/pytest-observability-branch?var-pr=47140)
**Latest run:** [28866176546:2](https://github.com/huggingface/transformers/actions/runs/28866176546)
**Result:** `success` | **Jobs:** 3 | **Tests:** 14 | **Failures:** 0 | **Duration:** 29s

<!-- ci-dashboard-recap:end -->

﻿Just to clarify the scope of this PR:

This PR fixes a small URL-responsibility mismatch inside `transformers chat`. When a user passes:

```bash
transformers chat HuggingFaceTB/SmolLM3-3B http://localhost:8000/v1
```

that `/v1` path is meant for the OpenAI-compatible inference API. Previously, the CLI could also reuse that same `/v1` base URL when constructing local management endpoints, which could produce:

```text
/v1/health
/v1/load_model
```

This PR changes that split as follows:

- `AsyncInferenceClient` keeps using the original `/v1` base URL for inference.
- `/health` and `/load_model` use a derived management root URL.
- Only the exact root `/v1` path is stripped for management requests.
- Paths such as `/proxy/v1` are not rewritten.

The direct impact is limited to the `transformers chat` startup path: health checking and model loading. It does not change server route registration, `/v1/chat/completions`, generation config, chat history, or interactive commands such as `!save` / `!set`.

The main compatibility risk I can see is a non-standard deployment that intentionally exposes management endpoints under something like `/v1/load_model`. In that case this PR would change the management request URL. For the official `transformers serve` route layout, though, keeping `/v1` for inference and root URLs for management seems like the cleaner behavior.

That said, the actual behavior change is fairly small in scope, so I think either accepting this split or deciding to keep the current behavior would be reasonable.