Skip to content

Commit e7504ba

Browse files
authored
release: v2.1.0
See CHANGELOG.md [2.1.0].
1 parent ff167b3 commit e7504ba

43 files changed

Lines changed: 3124 additions & 1355 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎CHANGELOG.md‎

Lines changed: 161 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,165 @@ target.
1111

1212
## [Unreleased]
1313

14+
## [2.1.0] — 2026-09-30
15+
16+
Amazon Bedrock becomes a provider you can run from the console. It
17+
authenticates with a Bedrock API key as well as access keys or the
18+
instance role, lists its models for import and for the route editor,
19+
and has a working Test Connection. Requests converted for Bedrock now
20+
keep their prompt-cache breakpoints and Claude's thinking settings. The
21+
route editor takes any upstream model name, and a route the provider
22+
refused can be created anyway. ThinkWatch-Core moves to v0.55.0, whose
23+
Bedrock layer this edition now shares with the desktop gateway. No
24+
database, setting, environment variable or Helm value changes.
25+
26+
### Read before upgrading
27+
28+
- **Test Connection with a saved provider's secrets needs
29+
`providers:update`.** A test that names a saved provider
30+
(`provider_id`, which is what the Edit dialog sends) used to take only
31+
`providers:create`. A custom role that has `providers:create` without
32+
`providers:update` can no longer test existing providers; the built-in
33+
admin role has both. A test with values typed into the request still
34+
takes `providers:create`. See *Security* below.
35+
- **Refusing a route the provider does not serve answers
36+
`model_not_served`.** `POST /api/admin/models/{model_id}/routes` still
37+
answers `400` when the import probe found no API the provider serves
38+
the model on, but `error.type` is now `model_not_served`, where it was
39+
`bad_request`. Scripts that matched on `bad_request` for this case need
40+
the new value. The request takes a new optional `"force": true` to
41+
create the route anyway.
42+
- **`error_type` in `gateway_logs` for failed requests is always the
43+
error's tag.** An upstream HTTP error used to log its debug text, such
44+
as `ProviderHttpError { status: 502, message: "…" }`, and an upstream
45+
rate limit a cut-off `UpstreamRateLimited { retry_after_secs: Some`.
46+
They now log `ProviderHttpError` and `UpstreamRateLimited`, as the
47+
metric labels and streamed requests already did. Dashboards or log
48+
forwarder queries that grouped by those strings see them merge into
49+
one value each.
50+
- **The server log now carries the upstream's reply to a 401 or 403**,
51+
as it already did for other upstream errors. For Bedrock that reply
52+
names the AWS account and the IAM principal. Callers still see only
53+
"Authentication failed with upstream", and failover, breakers and
54+
`gateway_logs` treat these errors as before.
55+
- **Prompt caching now works on Bedrock routes, and is billed at cache
56+
prices.** Requests converted for Bedrock used to lose their
57+
`cache_control` breakpoints, so Claude on Bedrock re-read the whole
58+
prompt at full price every turn. Breakpoints now go out as Converse
59+
`cachePoint` blocks, to Claude and Nova models only (others reject
60+
them), and the cache reads and writes Bedrock reports come back into
61+
usage, one-hour writes included. Traffic with repeated prompts, such as
62+
Claude Code, costs much less on Bedrock routes than it did; writes cost
63+
a little more, at `cache_write_weight` / `cache_write_1h_weight`.
64+
- **Claude's thinking reaches Bedrock.** A request converted for Claude
65+
on Bedrock now carries its thinking and effort settings, in the form
66+
Anthropic's API uses, along with the sampling limits thinking imposes
67+
(`temperature` 1, no `top_k`, `top_p` at least 0.95). Thinking used to
68+
be dropped on the way to Bedrock. Other Bedrock models still get no
69+
thinking.
70+
71+
### Added
72+
73+
- **Bedrock providers can authenticate with a Bedrock API key.** Pick
74+
*Bedrock API Key* as the authentication mode and paste the key AWS
75+
generated. It is kept like any other provider's API key, as an
76+
encrypted `Authorization: Bearer` header, and replaced in the
77+
provider's Edit dialog. A Bedrock provider that sends an
78+
`Authorization` header is not SigV4-signed; one without it is signed
79+
as before, with access keys or the instance role. Use a long-term key:
80+
a short-term one expires within 12 hours.
81+
- **Bedrock models can be imported, and are suggested in the route
82+
editor.** A Bedrock provider now lists what it can be routed to: the
83+
region's foundation models that can be invoked on demand and answer in
84+
text, and the inference profiles AWS defines, such as
85+
`us.anthropic.claude-sonnet-4-5-20250929-v1:0`. A model that is only
86+
served through an inference profile, as most current models are, is
87+
listed under its profiles' ids and not its own. Whether the account
88+
may call a model is still checked one model at a time when it is
89+
imported. The provider's credential needs
90+
`bedrock:ListFoundationModels` and `bedrock:ListInferenceProfiles`;
91+
the `AmazonBedrockLimitedAccess` policy a long-term API key is created
92+
with allows both.
93+
- **Test Connection for Bedrock providers**, however they authenticate:
94+
with an API key, access keys or the instance role. It lists the models
95+
above, so a wrong credential or a missing permission shows up before
96+
any traffic does.
97+
- **Create anyway, for a route the provider refused.** When the route
98+
editor's save is refused because the provider does not serve the
99+
model, the error now offers *Create anyway*: the refusal can be
100+
stale, or be about the probe's request rather than the model. The
101+
route is created, and its `model_route.created` audit row records the
102+
refusal it overrode in a new `refusal_overridden` key.
103+
104+
### Changed
105+
106+
- **The route editor's upstream model field takes any model name.** For
107+
a provider that lists its models, the field used to turn into a list
108+
to pick from, so a model the listing leaves out could not be routed to
109+
from the console. It now suggests the provider's models as you type,
110+
and takes whatever is typed.
111+
- **Bedrock instance-role credentials are cached.** They took three
112+
IMDSv2 round trips per request; they are now kept until five minutes
113+
before they expire, and a burst of requests at expiry makes one trip.
114+
- **Chat Completions requests can switch reasoning with `thinking`.**
115+
When a request is converted for another format, `thinking.type`
116+
(`enabled` / `disabled`, as DeepSeek, GLM and Kimi write it) now turns
117+
reasoning on or off; `disabled` wins over `reasoning_effort`.
118+
- **Core crates at ThinkWatch-Core v0.55.0.** `tw-dialect`, `tw-guard`
119+
and `tw-breaker` move from v0.43.0, and `tw-bedrock` joins them:
120+
SigV4 signing, eventstream unframing, Bedrock's addresses, region
121+
checks and model catalog now come from core. Where credentials come
122+
from (provider keys, the instance role) stays in this repository.
123+
`tw-breaker` is unchanged.
124+
125+
### Fixed
126+
127+
- **Bedrock providers could not be created or edited in the console.**
128+
The region was checked as a URL, so saving failed with
129+
`400 Invalid URL`. A Bedrock provider's `base_url` is now checked as an
130+
AWS region such as `us-east-1`, and anything else is refused, since the
131+
host is built from it. A provider saved with a URL there never reached
132+
Bedrock; set its region in the Edit dialog.
133+
- **Bedrock models the account may not call were imported anyway.**
134+
Bedrock refuses such a model (model access not granted, or an IAM or
135+
organization policy that denies it) with a 403, and the import probe
136+
read every 403 as a credential problem that says nothing about the
137+
model. The route was created and failed on first use. Now, when the
138+
region's control plane accepts the same credential, the refusal is
139+
recorded as the model's, with AWS's reason, and the model is skipped
140+
on import like any other refused one. Once access is granted,
141+
re-check the provider's models.
142+
- **A Bedrock provider whose stored secret key would not decrypt signed
143+
with an empty secret**, and every request failed with AWS's
144+
`SignatureDoesNotMatch`. It now refuses its requests with the reason
145+
until the keys are saved again, rather than falling back to the
146+
instance role, which would call AWS as a different identity. An
147+
access key ID saved without a secret is refused the same way.
148+
- **A Bedrock model id that is an ARN could not be routed.** Its `/`
149+
went into the request path unescaped, adding a path segment AWS could
150+
not route. It is now escaped.
151+
- **A base URL ending in its version segment doubled it.** A provider
152+
written as `https://api.openai.com/v1` sent requests, and the model
153+
listing, to `/v1/v1/…` and got 404s. A trailing version segment
154+
(`v1`, `v1beta`, …) that the request path starts with is now written
155+
once. Base URLs without one are unchanged.
156+
- **Typing into a provider's API key field and clearing it again wiped
157+
the saved key on save.** The field sent `Bearer ` with nothing after
158+
it. A cleared field now keeps the saved key, as a blank one always did.
159+
160+
### Security
161+
162+
- **Testing a connection with a saved provider's secrets takes
163+
`providers:update`.** The test fills each header left blank with the
164+
saved provider's secret and sends it to the URL in the request, and
165+
`providers:create` was enough to ask for it. So a user allowed only to
166+
create providers could send any saved API key to a server of their
167+
own. A test that names a saved provider (`provider_id`) now takes
168+
`providers:update`, which lets a user point that provider elsewhere
169+
anyway; a test without one still takes `providers:create`. A user with
170+
`providers:update` alone can now use Test Connection in the Edit
171+
dialog, which used to answer `403`.
172+
14173
## [2.0.0] — 2026-09-24
15174

16175
Callers now get errors in their own API's format, and an upstream that
@@ -557,7 +716,8 @@ unreleased builds should: stop the gateway, run `db/schema.sql`
557716
against PostgreSQL, restart against this tag. The schema is
558717
idempotent end-to-end, so the apply is safe to repeat.
559718

560-
[Unreleased]: https://github.com/ThinkWatchProject/ThinkWatch/compare/v2.0.0...HEAD
719+
[Unreleased]: https://github.com/ThinkWatchProject/ThinkWatch/compare/v2.1.0...HEAD
720+
[2.1.0]: https://github.com/ThinkWatchProject/ThinkWatch/releases/tag/v2.1.0
561721
[2.0.0]: https://github.com/ThinkWatchProject/ThinkWatch/releases/tag/v2.0.0
562722
[1.1.0]: https://github.com/ThinkWatchProject/ThinkWatch/releases/tag/v1.1.0
563723
[1.0.2]: https://github.com/ThinkWatchProject/ThinkWatch/releases/tag/v1.0.2

‎CONTRIBUTING.md‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ should say how you convinced yourself it doesn't leak a credential or
6161
weaken an existing check. "The tests pass" doesn't answer that — the
6262
tests didn't know about the hole either.
6363

64-
Please don't open a public PR or issue for a vulnerability. See
65-
[SECURITY.md](SECURITY.md) if present, or contact the maintainer
66-
directly.
64+
Please don't describe a vulnerability in a public issue, PR, commit or
65+
comment. The organization's
66+
[security policy](https://github.com/ThinkWatchProject/.github/blob/main/SECURITY.md)
67+
explains how to report one privately.

‎Cargo.lock‎

Lines changed: 34 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)