Commit Graph
45 Commits
Author SHA1 Message Date
3cba8a7e4c feat: add an AI decision flow step (TypeSafe's Jev, Cloudflare's Clef) (#11474)
* feat: add an AI decision flow step on TypeSafe's Jev

An `aidecision` step asks TypeSafe's Jev typed questions (choice, score,
yes/no) about a state and returns calibrated answers. It is a plain step:
a flow routes on the answers with a Branch to one after it, which the
editor builds and keeps in step with the questions. It can also be an AI
agent tool, where the calling model supplies the state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: read single-quoted branch conditions and flag branches on a removed question

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: route decision tools by their tag and keep structured questions as JSON

The questions editor writes only on an edit, so opening a step leaves its
value untouched, and questions the cards cannot hold stay in a JSON editor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: re-pick the questions editor mode when the value is replaced from elsewhere

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: keep the questions JSON editor's text in step with the value

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: recreate the questions JSON editor when the value is replaced from elsewhere

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: run AI decisions on Cloudflare's Clef as well as TypeSafe's Jev

A `cloudflare` provider kind reads the hub's `cloudflare` resource (token and
account id), calls the model's Workers AI URL, and unwraps Workers AI's
`result` envelope. Like TypeSafe, it serves decision steps only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019sEUVZVYBjkzkaVkjbFeAL

* feat: branch from each AI decision question, and align its editor with the brand guidelines

"Branch on this question" now sits in each choice question's card, which also
links to the Branch to one that routes it and flags options it misses or
branches it no longer matches. Labels, errors, cards and icons follow the form
guidelines and the Branch to one's cards.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019sEUVZVYBjkzkaVkjbFeAL

* feat: branch on an AI decision from its graph node

A decision with a choice question nothing branches on yet shows a button beside
its node that inserts the Branch to one, as its question card does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019sEUVZVYBjkzkaVkjbFeAL

* fix: resolve Cloudflare resources in the AI proxy, and use the design-system button on the graph

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019sEUVZVYBjkzkaVkjbFeAL

* fix: put the decision_inputs test's doc comment back on it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019sEUVZVYBjkzkaVkjbFeAL

* chore: regenerate the flow schema in prompts and the chat after the AiDecision description edit

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019sEUVZVYBjkzkaVkjbFeAL

* feat: switch AI decision questions between the form and JSON

JSON is always available; the form is offered whenever its cards can hold the
questions, and a value they cannot hold still opens in JSON.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019sEUVZVYBjkzkaVkjbFeAL

* fix: show step ids as badges and use the JSON toggle of the run forms

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019sEUVZVYBjkzkaVkjbFeAL

* fix: quote option names in the AI decision branch alerts

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019sEUVZVYBjkzkaVkjbFeAL

* feat: remove stale branches from the AI decision question card

An empty branch is removed directly; one with steps asks first, naming how many
steps go with it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019sEUVZVYBjkzkaVkjbFeAL

* feat: branch on yes/no AI decision questions, and test a decision step from the AI chat

A yes/no question branches like a choice: "Branch on this question" writes one
"yes" branch on `noul >= 0.5`, with the default as "no", and the helper still
reads it once the threshold is tuned. The AI chat's step test runs a decision
alone as a one-step preview, taking the state the chat passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019sEUVZVYBjkzkaVkjbFeAL

* fix: record removing several stale branches as one undo step

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019sEUVZVYBjkzkaVkjbFeAL

* fix: name the typesafe resource type TypeSafe

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019sEUVZVYBjkzkaVkjbFeAL

* fix: store TypeSafe as the typesafe resource type's display name, not in the word map

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019sEUVZVYBjkzkaVkjbFeAL

* fix: leave the typesafe and cloudflare resource types to the hub instead of seeding them

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019sEUVZVYBjkzkaVkjbFeAL

* docs(AGENTS): resource types ship through the hub, not migrations

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019sEUVZVYBjkzkaVkjbFeAL

* fix: show the questions JSON format beside the JSON toggle

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019sEUVZVYBjkzkaVkjbFeAL

* fix: give the questions JSON format in the field's description

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019sEUVZVYBjkzkaVkjbFeAL

* fix: style the decision's graph branch button like Add branch

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019sEUVZVYBjkzkaVkjbFeAL

* fix: show the questions JSON format only in JSON mode

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019sEUVZVYBjkzkaVkjbFeAL

* feat: show each question's branching under the questions JSON editor

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019sEUVZVYBjkzkaVkjbFeAL

* fix: drop the redundant In JSON prefix from the questions format

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019sEUVZVYBjkzkaVkjbFeAL

* fix: shorten the decision state's description and show an example as its placeholder

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019sEUVZVYBjkzkaVkjbFeAL

* fix: escape a schema placeholder before the JSON editor renders it as HTML

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019sEUVZVYBjkzkaVkjbFeAL

* fix: give the decision state's placeholder a text and an object example

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019sEUVZVYBjkzkaVkjbFeAL

* chore: update ee-repo-ref to 154cbccb6b8d47bf662b5507882917b5933d5a45

This commit updates the EE repository reference after PR #839 was merged in windmill-ee-private.

Previous ee-repo-ref: eaf38c1388f3cc5bece163404fffa959ede8cae0

New ee-repo-ref: 154cbccb6b8d47bf662b5507882917b5933d5a45

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-10-05 14:23:34 +00:00
hugocasaandClaude Opus 5.5 e117871bec weekly ai evals on current models, and claude 5.5/gpt-6 support (#11409)
* feat: run ai evals weekly on current models and post results to a dashboard

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: add current flagship models, a reasoning flag and claude 5.5 defaults

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: never send a reasoning disable claude 5.5 or gpt-6-astra reject, and treat gpt-6 as a reasoning model

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: address review on gpt-6 support, chat completions tools and model metadata

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: leave tiered gpt-6 unpriced and drop the off sentinel on gpt-5 and o-series

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: point the ai_evals readme at the model registry instead of copying it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor: encode the reasoning rules as per-family maps with a shared parity fixture

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: keep the chat completions tools rule open-ended past gpt-5.6 and scope the parity fixture

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 13:10:24 +02:00
c1b59f70dd feat(ai-agent): add compaction memory that summarizes older context (#10928)
* feat(ai-agent): add autocompacted memory that summarizes older context

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* fix(ai-agent): compact on final-answer turns and count what a turn appended

Address the pre-push review findings on the compaction path:

- A turn the model answers without a tool call left the agent loop on its first
  iteration, so a chat-shaped step never compacted and reloaded the whole
  conversation on every later turn. Compaction now also runs after the loop.
- The trigger measured only the last request, so a single large tool result
  could carry the next one past the window without ever crossing 80%.
- The summarization call re-sent the usage-tracking request shape on endpoints
  the loop had already learned to drop it for.
- The flat 8000-token summary reserve swallowed the whole target on a small
  context window, leaving one message in the tail and summarizing the rest.
- A response cut off inside the <analysis> scratchpad was accepted as a summary.
- The chat-mode memory default was a shared object the step form edited in place.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* fix(ai-agent): keep Anthropic prompt counts and compact once per response

Address the first CI review round on the compaction path:

- Anthropic's streaming parser dropped `message_start`, the only event carrying
  the prompt-side counts, so a native Anthropic run reported no input tokens at
  all and compaction fell back to a character estimate.
- A loop that exits without issuing another request — a structured-output turn
  does — reached the post-loop pass still holding the previous measurement and
  compacted a second time, or retried a failure with nothing changed.
- The summarization call inherited the step's `max_completion_tokens`; a low one
  truncates the summary inside its scratchpad, which counts as a failure and
  disables compaction after three of them.
- A fired trigger that found nothing to summarize said nothing.
- Memory already over the window — a lowered `context_window`, or a step moved
  over from `auto` — had no way back, since compaction only ran after an
  accepted request. It now also runs once before the first one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* fix(ai-agent): state the summary's own completion cap and drop the pre-flight pass

- The summarization call asked for no completion cap at all, which is "uncapped"
  only on the OpenAI-shaped providers: Anthropic substitutes 64000, over several
  Claude models' output ceiling, and Bedrock leaves the model's own small default,
  short enough to cut the response off inside its scratchpad. It now asks for the
  reserve the split already set aside, raised to the step's cap when that is larger.
- Compaction no longer runs before the first request. The fallbacks the loop learns
  from a rejection are not known that early, so on exactly the endpoints that need
  them the summarization was malformed by construction: it failed, spent a strike,
  and the first agent request still carried the oversized conversation. A memory
  already past the window is repaired on the turn after a request the endpoint
  accepts, rather than by a pass that cannot succeed there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* fix(ai-agent): ask the summary for exactly the room the split reserved

The split scales its reserve down on a small window while the request asked for
a flat 8000, so the two diverged below an 80k window: on a 4k/8k model the cap
alone exceeded the window and every summarization was refused, and on a 20k one
a full-length summary could land the conversation back over the trigger and
compact its own previous summary on the next response. Both now read one
`summary_reserve_tokens`.

The call also no longer inherits the step's reasoning effort. Every provider
counts thinking against that same budget, so a high-effort model could spend the
whole reserve before writing anything and return a summary cut off inside its
scratchpad; the compaction prompt asks for an `<analysis>` block, which is the
reasoning this call needs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* fix(ai-agent): charge the compaction budget for tools and the system prompt

The tail budget was the whole target, but a request also carries the system
prompt compaction keeps and the tool definitions, which are not in the message
list at all. On a small window those are most of it: a tail sized to the full
target left the next request back over the trigger, compacting again every
response, and the no-usage estimate missed the tool schemas entirely so it could
fail to trigger at all. Both now account for them.

The reserve also gains a floor. It is the summary's output cap as well as the
room the split leaves, and scaled down without one a small window gave a
structured nine-section summary a few hundred tokens — truncated inside its
scratchpad every time, which is discarded, which switches the mode off after
three.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* fix(ai-agent): count Gemini's tool-use prompt tokens in an agent step's usage

Gemini splits a tool-using turn's input across `promptTokenCount` and a disjoint
`toolUsePromptTokenCount`, and its thinking apart from `candidatesTokenCount`.
The agent step's parser read only the headline fields, so every tool-using turn
under-reported both — and the compaction trigger, which runs off the reported
prompt, could not see the tool results that grew it. It now goes through the same
helpers the proxy path already used.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* fix(ai-agent): calibrate the compaction estimate against the measured prompt

Two rounds running, the finding was "the character estimate cannot see input X"
— tool schemas, then S3 attachments, which are short paths in the message list
and whole images by the time a provider counts them. Enumerating those is a list
that only grows, so the estimate is now scaled to the one number that is ground
truth: what the provider charged for the last request. Attachments, tokenizer
drift and whatever comes next fall out of that, because the estimate is only
ever used relative to itself.

Also stop the Gemini helpers turning an absent count into `Some(0)`. Downstream,
absent means "fall back to estimating the conversation" while zero reads as an
empty prompt and would hold the trigger below its threshold for the whole run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* fix(ai-agent): charge attachments what they cost and let a heavy short prefix compact

The calibration conserved the conversation's total cost but spread it by
character count, so an attachment — a short S3 path in the message list, a whole
image or PDF once a provider expands it — was charged to the text messages around
it and stayed nearly free in the split. It now carries a nominal cost of its own,
which the calibration corrects a residual on rather than the whole gap.

The four-message minimum also refused exactly the case that fix is for: an
attachment arriving on the first or second turn can pass the trigger before four
removable messages exist, and summarizing even one of them saves most of the
prompt. A prefix worth a quarter of the window is now enough on its own.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* fix(ai-agent): never summarize a prefix holding only a previous summary

The message-count floor was carrying a second job: a fresh summary sits in a one
or two message prefix, so requiring four declined it. The share threshold added
last commit admits it, and a summary is reserve-sized by construction — so the
post-compaction shape could spend one summarization per response swapping a
summary for another the same size, shrinking nothing and losing fidelity each
time. A previous summary no longer counts towards that threshold.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* fix(ai-agent): take the context window from the model and drop the estimate calibration

Brings compaction in line with how the AI session does the same job, which had
already answered these three questions.

- The window is looked up from the model. `MODEL_CONTEXT_WINDOWS` in
  `windmill-ai/src/model_context.rs` mirrors the session's table in
  `copilot/modelConfig.ts`, entry for entry and with the same matching rules;
  each side points at the other, since a model added to one and not the other
  compacts at two different sizes. A step's `context_window` becomes the
  override for what the lookup cannot serve, and chat mode writes none.
- Provider usage is normalized where the provider's quirk is, not at the
  consumer. `TokenUsage::with_cache_beside_input` raises `input_tokens` to the
  whole prompt for Anthropic and Bedrock, which report their cached prefix
  beside it; the OpenAI shape already counts it inside. `prompt_tokens()` is
  then just `input_tokens`, rather than inferring the shape from whether a
  write count is present.
- The estimator is no longer calibrated against the measured prompt. The
  session uses the provider's count when it has one and a chars/4 estimate
  otherwise, with nothing in between, and a tail sized a little wrong only
  compacts again a turn later.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* feat(ai-agent): summarize memory down to what the database can store

Without an instance object store, memory is a 100KB database row cut
from its oldest message, the summary included, so compaction on a
mainstream model never got to keep anything across runs. A step that
persists there now runs its post-loop compaction pass against the
smaller of the model's window and the cap at chars/4, about 25k tokens:
the loop keeps the whole window, and what is written is a summary plus a
tail that fits. The run logs when that pass summarizes, and how many
messages the write dropped when one still overshoots.

The editor's storage warning on the option is removed: nothing exposes
the instance storage to it, so it keyed on the workspace S3 setting,
which is unrelated to where memory goes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* fix(ai-agent): get a complete, billed summary out of every provider

Compaction against the real providers turned up four things the stub
could not: Gemini and OpenAI's reasoning models think by default and
bill it against the same cap the summary must fit in, so the
summarization request now asks them for their least (none, low); an
OpenAI Responses call that hits max_output_tokens ends in
response.incomplete, whose usage the parser dropped, so that
summarization went unbilled; a summary that quotes </summary> when it
describes its own instruction was cut off at the quote, on the agent
step and the AI session alike; and the prefix could end on an unanswered
user message, after which the instruction reads as part of that turn
(Anthropic merges the two outright). The tail now starts on a user
message, and both prompts tell the model the instruction is not part of
the conversation.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* fix(ai-agent): compact down to half the window, on the agent step and the AI session

The gap between the 80% trigger and the target is what one compaction
buys, and every summarization request carries most of the window. At a
70% target a 128k model summarized about 13k tokens of prefix for a
summary of up to 8k, so each ~100k-token request bought a few turns of
room before the next one re-summarized the previous summary. At 50% the
same request frees about 30k.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* fix(ai-agent): drop the workspace-S3 memory hint and state the database bound in the tooltip

The memory field warned that memory is kept in the database whenever the
workspace had no S3 storage. That setting has no bearing on where memory
goes: the instance object store decides, and nothing exposes it to the
editor. The field's tooltip now describes both memory kinds and states
the database bound unconditionally; the run log says what happened.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* fix(ai-agent): send the summarizer its tool history as text

The summarization request carries no tool definitions, and Bedrock's
Converse API rejects toolUse/toolResult blocks that arrive without them,
so on Bedrock every summarization of a prefix holding a tool call failed
silently until the breaker tripped. The prefix's tool calls and results
now reach the summarizer rendered as text, on the agent step and in the
AI session's compaction, which goes through the same proxy.

Also drops the TokenUsage::prompt_tokens accessor, which had become a
plain read of the normalized input_tokens, and shortens the context
window field's description.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* fix(ai-agent): price attachments from the provider count, bound storage in bytes, effort per pro model

Addresses two Codex rounds and a leftovers audit.

- Attachments were priced at a flat 1500 tokens in the split, so a
  multi-page PDF (tens of thousands of tokens to the provider, a short
  S3 path in the message list) could be kept in the tail or leave no
  prefix worth summarizing. They are now priced from the provider's
  count for the request that carried them, less that request's text,
  with the 1500 floor where nothing was counted.
- The database storage bound measured the provider's token count, but
  the 100KB cap is bytes and repetitive text packs several characters
  per token. The persist pass now measures the serialized conversation.
- The summarizer forced `low` on every reasoning model, which the pro
  variants reject (gpt-5-pro takes only high, gpt-5.2-pro starts at
  medium); they now get no effort.
- Dropped the unused prompt_tokens accessor and its orphaned assert, an
  unused PartialEq, a needlessly public lookup, and fully-qualified
  Gemini calls; refreshed stale comments and the memory_id schema doc;
  regenerated the flow schema artifacts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* fix(ai-agent): evict a heavy attachment into the summarized prefix, not the tail

Pricing attachments from the provider count was not enough on its own: a
leading attachment is a user message, and the boundary rule pulled the
last unanswered user turn back into the kept tail to keep it with its
answer. For a heavy attachment that dragged it into the tail — or, at
the front, emptied the prefix — so it was never summarized and rode
every request. The boundary now moves forward instead, keeping that
user turn and its answer in the summarized prefix. Verified on the
running instance: a 25k-token PDF on a 30k window is summarized out on
the turn it overflows, and later turns drop from 26k to ~1.5k tokens.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* fix(ai-agent): keep the forward boundary move off tool results and the prefix start

The forward move that keeps an unanswered user turn out of the tail had
two edges the third Codex round found: advancing past the user could
land the boundary on a tool result (its tool_calls then summarized away,
orphaning it), and with no system prompt the summarizable prefix starts
at 0, so a trigger firing while the tail estimate fit everything indexed
below the start and panicked the task. The forward scan now skips
tool-opening boundaries, and the move is guarded above the prefix start.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* fix(ai-agent): drop the step temperature from the summary request

OpenAI's reasoning models (gpt-5-mini, gpt-5.1, gpt-5.2) reject
`temperature` alongside any reasoning effort but their own default, so a
step configured with a temperature made every summarization fail once
the summarizer forced a low effort — history then grew unchecked. The
internal summary call now omits the step's temperature: a structured
extraction does not need a set one, and omitting it sidesteps each
provider's temperature-versus-reasoning rules. Confirmed against the API
that low + temperature is refused on those models.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* fix(ai-agent): compact an oversized loaded memory before the first request

Compaction was reactive, taken only after a request the endpoint
accepted, so the fallbacks the loop learns from a rejection are known
first. But a memory loaded from an earlier run can already exceed this
run's window — the step was switched to a smaller model, or a run under
a wider one persisted more than fits — and that first request then
overflows and fails the run, with every retry reloading the same
history and failing again. A pass is now taken up front, off the
character estimate, before the first request. It uses the default
request shape; an endpoint needing a fallback may reject this one
summary, which is non-fatal, and mainstream providers need none.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* fix(ai-agent): under the storage bound, trigger on the max of bytes and model tokens

The storage-bound pass measured only the serialized row size, so an
attachment — a few bytes as an S3 path but nearly the whole model
context — read as tiny and the pass skipped a compaction the model
needed. It now takes the larger of the byte measure and the model's
token count, since repetitive text is few tokens but many bytes and an
attachment is the reverse; either being over must fire a pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* fix(ai-agent): drop oldest turns when a summary cannot fit the window, as the AI session does

An oversized loaded memory (a step switched to a smaller model, or an
object-store run that persisted more than a later model's window holds)
left a prefix larger than the summarizer's own window, so the summary
request overflowed and failed, the memory was untouched, and every
retry failed the same way. The AI session handles this by falling back
from summarization to dropping the oldest turns down to the target;
compaction here now does the same. When a summary cannot run — it
failed, the breaker is tripped, or nothing is worth folding — the oldest
turns are dropped until the conversation fits and opens on a user
message, keeping the newest turn. The next request then always fits.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* fix(ai-agent): drop whole turns only, keep the storage pass to bytes, refresh the count after a rewrite

Three edges the seventh Codex round found, all in the drop-oldest
fallback and the storage-bound measure:

- drop_oldest_to_fit dropped to any point that freed enough, which could
  strand a tool result whose tool_calls went with the messages before
  it. It now drops whole turns only, always landing the boundary on a
  user message and never splitting the newest turn; a lone turn too big
  for the window is left whole rather than broken.
- The storage-bound pass measured the whole model prompt against the
  shrunk 25k window, so a large tool roster and the system prompt —
  neither written to the row — tripped it on a conversation the row
  easily held. It measures the serialized bytes alone now; the model's
  own window is enforced by the in-loop passes and the pre-first-request
  pass, so the persisted size is all this pass is for.
- A compaction rewrites the message list, so the provider's count for
  the request that produced it no longer lines up. The count is now
  cleared after any pass that rewrites the conversation, so a later pass
  measures the estimate over the actual messages instead of a stale,
  larger prompt (which could decline a summary that already fit and then
  drop it). The step temperature, no longer sent to the summarizer on
  any path, is dropped from the request struct.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* fix(ai-agent): measure only the persisted messages against the storage cap

Persistence strips the system prompt before writing the memory row, but
the storage pass was serializing every message including it, so a large
system prompt with a tiny conversation reported far over the storage
trigger, and the fallback dropped the one real turn, run after run. The
storage measure now serializes only the non-system messages, matching
what the row actually holds.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* fix(ai-agent): run the model-window pass before the storage-bytes pass post-loop

A turn the model answered without a tool call broke before the in-loop
compaction check, so on database-backed memory its only pass was the
storage one, which measures bytes. An attachment fills the model context
but is a few bytes in the row, so that turn never compacted and a
follow-up could overflow the model. The post-loop now runs a
model-window pass first, off the provider's count, then the
storage-bytes pass when the row is smaller than the model — both limits
enforced for a chat-shaped step, not just the one that happens to bind.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* fix: simplify agent compaction and preserve execution history

* fix: remove unused compaction history setting

* fix: preserve answers and recover rejected agent context

* refactor: make agent compaction transactional

* fix: skip agent summaries that cannot fit retained context

* fix: explain skipped agent context compaction

* fix: retain recent agent memory when storage compaction cannot fit

* fix: start retained agent memory at a user turn

* fix: reject unsafe agent memory truncation on storage fallback

* docs: clarify agent context window override scope

* fix: keep recent turns verbatim when compaction memory outgrows storage

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* fix: keep the compaction summary out of the agent's answers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* fix: shorten the agent context window help text

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH

* chore: update ee-repo-ref to 942d4013f36edac1fc9a9addbdb02198db1c7a05

This commit updates the EE repository reference after PR #812 was merged in windmill-ee-private.

Previous ee-repo-ref: 8ca1682ce6106ba6ea96894fbe606dac64102eb6

New ee-repo-ref: 942d4013f36edac1fc9a9addbdb02198db1c7a05

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2026-09-29 19:01:53 +02:00
e14da5c6bc feat(bedrock): add OIDC role assumption as a fourth auth mode (#10936)
* feat(bedrock): add OIDC role assumption as a fourth auth mode

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YCVb91fZp3dqM14KRPzoEn

* fix(bedrock): gate the OIDC cache correctly and assume the role once per job

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YCVb91fZp3dqM14KRPzoEn

* refactor(bedrock): check the OIDC region before minting a token

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YCVb91fZp3dqM14KRPzoEn

* fix(bedrock): keep OIDC session names collision-resistant, gate the copy on EE

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YCVb91fZp3dqM14KRPzoEn

* fix(bedrock): check the OIDC region before reusing cached credentials

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YCVb91fZp3dqM14KRPzoEn

* fix(bedrock): clear assumed-role sessions when AI settings change

invalidate_ai_request_cache_for_workspace cleared AI_REQUEST_CACHE only, so a
workspace's AI settings edit reset one cache and left the assumed-role sessions
keyed on the old config in place until STS expired them.

Also name the region requirement in the credentials-check hint, so following it
does not land on the OIDC path's region guard.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YCVb91fZp3dqM14KRPzoEn

* chore: update ee-repo-ref to de73db2bacfdc3eaa2e63b1827178bc198d54e5c

This commit updates the EE repository reference after PR #770 was merged in windmill-ee-private.

Previous ee-repo-ref: c43dab1e69b1cb3f685e6df07bff634dc2a0b734

New ee-repo-ref: de73db2bacfdc3eaa2e63b1827178bc198d54e5c

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-09-25 18:19:38 +02:00
c637b06a54 feat: add per-model context window override in workspace AI settings (#11232)
* feat: add per-model context window override in workspace AI settings

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test: pin the instance ai_config context window validation

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* refactor: name the table-only context window lookup for what it is

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix: drop the per-row assumed-window hint from the model limits card

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix: fold the model limit default into an inline reset button

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix: give the per-model settings cards one reset control

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix: enforce the documented output limit bounds on the AI config

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix: show inherited model limits as placeholders, like model pricing

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: hugocasa <hugo@casademont.ch>
2026-09-21 14:44:03 +00:00
GuilhemandClaude Opus 5 a571117f3f feat: store mcp tool call, result and reasoning on flow conversation rows (#11176)
* feat: store mcp tool call, result and reasoning on flow conversation rows

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: keep the failure reason and web search citations on tool rows

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: persist a structured answer as its own row and keep reasoning-only rows from closing a turn

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: keep a failed Windmill tool's error on its conversation row

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: let a structured answer row claim its streamed thinking

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: scope a structured answer's claim to its own turn and document the row fields as stored

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: claim a textless assistant row only within the newest turn

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* refactor: store the thinking that led to a tool call on the tool row

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: keep tool calls in stream order and scope a structured answer to its turn

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat: store the files a user message carried as object-storage references on its row

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat(chat-sdk): read a message's attachments and build their download URL

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat(chat-sdk): carry a loaded user message's attachments in AI SDK metadata

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat: store the model's call and what it got back on every tool row

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: pass no extras in the orphaned-conversation test's message insert

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(chat-sdk): keep a stored JSON null tool result instead of the row text

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: label a tool call the turn finished without as not finished

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: keep a structured answer's streamed call out of the did-not-finish label

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: write a turn's conversation rows in order and describe stored tool calls

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: await the image answer row like the agent loop's other rows

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(chat-sdk): place a row nothing streamed by its sequence, not at the end

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(chat-sdk): place only tool rows by sequence, keep a closing row last

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(flow chat): show a failed tool's error, and no Retry on a running or stopped turn

A failed tool card showed the row label in place of the error the row now
stores. The live tool result now reports failures, so a failed call briefly
flagged a running turn as failed; a stopped turn, whose last row is the
failed tool or the cancelled flow's failure, offered Retry too.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 15:35:18 +02:00
hugocasaandClaude Opus 5 c297ed0052 feat: managed memory with an inherited or custom memory id per step (#11118)
* feat: split ai agent memory into agent policy, run memory id and step history

* fix: scope string memory ids to workspace and flow, keep nested tool history inputs

* chore: update sqlx cache for the flow context query

* docs: describe memory id scoping as collision-free rather than isolated

* chore: regenerate openflow json after merging main

* fix: offer no memory id for legacy manual memory, document linked history inputs

* fix: seed provided messages from legacy manual memory and hide its note once set

* fix: bypass memory when a provided messages expression evaluates to null

* fix: require a user message when provided messages are empty

* chore: keep the empty messages comment within the line width

* docs: name the history inputs wherever linked steps list their flow-local inputs

* docs: keep the memory storage path on one line

* feat: managed memory with an inherited or custom memory id per step

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix: list a custom memory id in the test run form and name where an inherited one comes from

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix: keep memory id out of the add-field menu and drop the memory id telemetry

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix: keep legacy auto memory without an id working after an untouched redeploy

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix: rename step messages to previous_messages and address review

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* style: rewrap comments and docs lines lengthened by the previous_messages rename

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* refactor: read agent memory as either a legacy shape or the current one

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix: name the memory setting in ignored-input notes and keep conversions honest

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix: keep a legacy memory count unset on open and read a cleared count as off

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: address review on cleared test history and zero-count memory

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: drop flow-local keys from a linked agent resource before interpolating it

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: keep a linked resource's own inputs as fallbacks and note ignored history on image runs

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: restore the linked agent draft tests and log ignored history on every image run

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: resolve the one-of variant from the value when the selected one leaves the list

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: treat zero-count managed memory as off when enabling chat mode and shorten comments

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: stop requiring user_message in the openflow agent contract when previous messages are the prompt

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 11:55:43 +02:00
hugocasaandClaude Opus 5 b51c0eabbe feat: stream reasoning summaries in AI agent Responses API steps (#11124)
* feat: stream reasoning summaries in AI agent Responses API steps

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix: retry without a reasoning summary a strict gateway rejects

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix: request the reasoning summary whether or not the step streams

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* feat: return the OpenAI reasoning summary in the agent step's reasoning

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix: retry without a reasoning summary a gateway rejects with 422

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 10:46:15 +02:00
GuilhemandClaude Fable 5.1 c4e878e831 feat: return an ai agent step's thinking in its job result (#11140)
* feat: return an ai agent step's thinking in its job result

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs: say when an agent result carries no reasoning

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 14:28:48 +02:00
a78beff743 feat: dynamic AI agent toolsets (#11050)
* feat: dynamic ai agent toolsets, and memory as a step input

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: address review round 1 on dynamic ai agent toolsets

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* refactor: tag enabled_tools and drop the memory step input

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: let an mcp server entry be named by the path the roster shows

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: keep $res: out of the tool names the enabled tools picker offers

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: name an mcp server by its bare path on the one side that can hold it

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: count the enabled tool names that matched nothing instead of logging them

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* refactor: narrow an agent's roster in one pass, by whole entries

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test: pin that an mcp summary is rejected against a name that is not

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* chore: regenerate the copilot flow schema after the merge

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs: shorten the enabled tools list hint

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* refactor: take enabled_tools back to a plain list of tool names

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs: keep the enabled tools add-menu hint describing the unset field

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: name a websearch tool that carries no summary of its own

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: reserve the name web search is enabled by so no tool can share it

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* refactor: spell the reserved web search name with a hyphen

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* refactor: reserve __wm_web_search as the name web search is enabled by

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* chore: advance ee-repo-ref past the git sync ci check work

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs: shorten the enabled tools description the run form shows

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref to e4c1b794d6c5e6e390987341b2840587bbb40348

This commit updates the EE repository reference after PR #785 was merged in windmill-ee-private.

Previous ee-repo-ref: af668462f0f06b02a5f4e0c22e6156858487a518

New ee-repo-ref: e4c1b794d6c5e6e390987341b2840587bbb40348

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-09-15 11:34:30 +02:00
hugocasaandClaude Opus 5 320f400512 feat: enable Anthropic prompt caching on Vertex AI agent steps (#10876)
* feat: enable Anthropic prompt caching on Vertex AI agent steps

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Mfioobw3Wp8qAmPE82aM9

* fix: add an escape hatch for Vertex projects with prompt caching disabled

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Mfioobw3Wp8qAmPE82aM9

* docs: state that the caching flag spans every Anthropic platform

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Mfioobw3Wp8qAmPE82aM9

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 08:44:08 +02:00
hugocasaandClaude Opus 5 b6e059116a feat: track token cost in AI sessions and chats (#10688)
* feat: track token cost in AI sessions and chats

* fix: address review findings on AI cost tracking

* fix: price inherited and overridden models at their real rates

* fix: stop newer model revisions inheriting an older price

* fix: stop a sub-model inheriting its family's price

* fix: keep alias suffixes resolving to their model's price

* fix: count OpenRouter cache writes and drop unverifiable rates

* refactor: move AI spend out of the chat into workspace and user settings

* fix: pin the usage workspace per turn and stop inventing cache rates

* fix: leave Sonnet 5 unpriced while its promotional rate runs

* docs: record the new table in the schema summary and tighten comments

* fix: mark estimated AI costs with ~ and drop session grouping

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: name the workspace in the self-scoped AI usage title

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs: state that overrides never replace a provider-returned cost

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: let a cleared cache rate inherit again and flag partial totals

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: clear a refused rate's error when the input snaps back

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: stop a revision variant inheriting its base family's rate

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: report AI usage before tools run and price self usage consistently

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: key pricing rows on the model id usage is reported under

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: surface Bedrock and Gemini usage the chat proxy was dropping

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: count Gemini tool-use prompt tokens as input

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat: price flat-rate Gemini Flash models

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs: state the tool-use token invariant once

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 11:08:13 +02:00
hugocasaandClaude Opus 5 3468cb68b1 fix: drop sampling params on Claude models that reject them (#10708)
* fix: drop sampling params on Claude models that reject them

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs: scope the sampling-param claim to what was probed and split the bedrock test

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test: build the disable body through the resolver instead of asserting a rejected shape

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: use the Gemini 3.1 Pro id that actually resolves

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: Bedrock Sonnet 5 cannot disable thinking, unlike the native API

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 21:15:56 +02:00
hugocasaandClaude Opus 5 68fc7825bb fix: refresh AI provider model defaults and capability metadata (#10690)
* fix: refresh AI provider model defaults and capability metadata

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: send explicit thinking disable for Claude and cap Opus 4.1 output

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: resolve mistral-medium-latest window and OpenRouter Claude 5 off

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: cover au. bedrock geo and Fable 5 caching, revert unverified mistral ladder

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: scope the Anthropic explicit disable to models that think by default

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: translate the reasoning off sentinel on the backend Anthropic path

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: translate the reasoning off sentinel on the Bedrock Converse path

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* refactor: share the reasoning off sentinel and make its translation testable

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 18:41:40 +02:00
Ruben Fiszel 8364dd96ef feat: send prompt_cache_key on the openai responses api (#10507)
* feat: send prompt_cache_key on the openai responses api

* fix: bound prompt_cache_key to the provider limit and scope it to retryable paths

* fix: keep a digest suffix when bounding long frontend cache keys

* docs: attach the cache-key doc block to the function it describes
2026-08-04 12:28:09 +00:00
Ruben Fiszel 94bcc00554 fix(ai): route Azure OpenAI agent steps through the Responses API (#10404)
* fix(ai): route Azure OpenAI agent steps through the Responses API

* docs: note why azure responses routing is per-provider

* fix(ai): fall back to chat/completions when an azure endpoint rejects responses

* refactor(ai): retry endpoint and stream_options rejections in one loop

* fix(ai): keep a rejected request shape dropped across agent iterations

* perf(ai): remember which deployments reject the responses route

* fix(ai): only remember a route rejection the fallback resolved

* fix(ai): keep web search on azure and require the deployment be named to reroute

* fix(ai): remember an unserved route only on a 404

* docs: correct the reroute-flag and fallback-hook comments
2026-07-30 09:56:37 +02:00
Ruben FiszelandClaude Opus 5 621718b32f fix(ai): resolve deployment-pinned Azure base URLs to the v1 surface (#10362)
An Azure OpenAI base URL naming a deployment, such as the
`https://<res>.openai.azure.com/openai/deployments/<id>` format that
`openai_azure_base_path` documents, was appended to as-is. That names the legacy
surface, which serves only with an `api-version` query and answers 404 without
one, so both the proxy and the AI agent step reached a route that does not exist.

Such a base now resolves to the resource root and the v1 surface, like every
other Azure shape. The deployment in the URL is redundant there: the v1 surface
takes it from the request body. `azure_foundry_root` recovers the root the same
way, so a Foundry resource on such a base builds its Claude URL from the root too.

The instance-settings help text promised the URL pins the model for every
workspace, which that surface never delivered; it now says where the model comes
from.

Verified against a live Azure OpenAI resource: the previous URLs 404 and the ones
built now return 200.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 22:31:36 +02:00
Ruben FiszelandClaude Opus 5 6e56ce11db fix(ai): make the proxy and the AI agent step read a resource the same way (#10359)
The two paths derive the endpoint and the credential header independently, so a
resource could authenticate in one and 401 in the other. A parity test pins them
together across the provider/platform matrix and fails on each divergence below.

- Anthropic base URLs were read differently: the proxy trimmed and re-appended
  `/v1` while the agent step appended `/messages` to the stored value, so a
  `.../anthropic` base worked in workspace settings and 404'd in an agent step.
  `build_anthropic_api_url` accepts both forms for both paths, and the URL no
  longer depends on the client-supplied `X-Anthropic-SDK` header, which is gone.
- A base URL stored with a trailing slash doubled it in an agent step.
- An OpenAI resource pointed at Azure got Azure's URL layout and `api-key`
  header from the proxy but bearer auth and the plain path from the agent step,
  where `OpenAIQueryBuilder` ignored `is_azure`.
- The agent step sent an empty credential when the resource had no api key,
  where the proxy sends none at all. `retain_effective_credentials` gives both
  the same rule, so an endpoint that authenticates another way still works.
- An OAuth resource cannot resolve to a token in a worker: there is no client
  credentials exchange there, so it now fails with that reason unless it carries
  the credential header its provider reads.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 16:38:13 +02:00
Ruben FiszelandClaude Opus 5 ebf68d7970 fix(ai): support OpenAI/Anthropic-compatible gateways in workspace AI settings (#10356)
* fix(ai): stop breaking OpenAI-compatible gateways in workspace AI settings

The workspace/instance AI proxy sent credentials in a shape that
OpenAI-compatible and Anthropic-compatible gateways reject, while the same
resource worked in an AI agent step:

- `is_azure` treated *any* OpenAI base URL other than api.openai.com as Azure,
  so a gateway got the Azure `api-key` header instead of `Authorization: Bearer`
  and an `/openai/v1/`-rewritten path. Match on the host instead.
- The Anthropic proxy sent both `authorization: Bearer` and `X-API-Key`.
  Gateways reject ambiguous credentials; send only the header the endpoint
  expects, matching `get_auth_headers` and the agent-step path.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(ai): match Azure on the endpoint and let resource headers own auth

Review follow-ups:
- Azure OpenAI reached through a custom domain keeps its `/openai/deployments`
  path, which `openai_azure_base_path` documents; match on it so those
  instance-wide settings are not reclassified as plain OpenAI-compatible.
- Cover the sovereign-cloud API Management suffixes and FQDNs with a trailing
  dot.
- A resource that supplies its own `authorization`/`x-api-key` header now
  suppresses the built-in one. Outgoing headers are appended rather than
  replaced, so both credentials used to travel, which is exactly what gateways
  reject; this is the escape hatch for endpoints wanting bearer auth.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(ai): give the agent step the same resource-header credential rule

Review round follow-ups:
- `ai_executor` appended the query builder's credential header alongside the
  resource's, so an AI agent step still sent two credentials where the proxy now
  sends one. Both paths share `resource_owns_credentials`/`CREDENTIAL_HEADERS`;
  non-credential headers such as `anthropic-version` are kept.
- Cover the OpenAI-compatible proxy's suppression branch with a test.
- Match the Azure deployments path case-insensitively, like the host.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(ai): scope the credential override to the header the provider uses

A resource header now replaces the built-in credential only when it is the same
header the provider authenticates with, or an `authorization` one (which every
endpoint reads as the credential). Matching any credential-shaped header let an
OpenAI-compatible resource's `x-api-key` routing header suppress the bearer
token. Google AI's `x-goog-api-key` joins the list so the override reaches that
provider too.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* refactor(ai): share credential and trailing header assembly across AI paths

The proxy and the AI agent step each assembled outbound headers themselves, so
this fix had to be applied at three sites and the rules could drift apart
silently. Two pieces move into `proxy`:

- `credential_header` picks the credential to send, applying the resource
  override. `authorization` carries a bearer token and every other credential
  header carries the raw key, which holds for every provider.
- `common_outbound_headers` yields Windmill's own headers then the resource's,
  the tail every outbound request shares.

A resource resolves to an api key or an OAuth token, never both, so selecting
one drops the branch that could emit two `authorization` headers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(ai): keep OAuth tokens on the bearer header

An OAuth resource resolves to an access token, which every provider reads from
`authorization` — Azure OpenAI accepts api keys in `api-key` but Entra ID tokens
only as a bearer. Sending it in the provider's key header left Azure OpenAI and
Foundry Claude OAuth resources unauthenticated.

Also covers the credential-override narrowing: a credential-shaped header the
provider does not authenticate with is an ordinary header and must not suppress
the built-in credential.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 15:23:49 +02:00
3cf7a390a3 fix: pin validated DNS address to close SSRF DNS-rebinding TOCTOU (#10303)
* [ee] fix: pin validated DNS address to close SSRF DNS-rebinding TOCTOU

validate_url_for_ssrf resolved the host, checked every address was
public, then discarded them. Callers re-used the hostname and let stock
reqwest re-resolve at connect time, so a TTL-0 DNS rebinder that answered
a public IP at check-time and an internal one (e.g. 169.254.169.254) at
connect-time slipped straight through the guard.

Return the resolved addresses as a ValidatedTarget and pin them onto the
client that connects, so validate-time and connect-time target the same
address. Covers the AI proxy and worker AI-agent base_url (the primary
readable-SSRF sink), AI OAuth token_url, MCP server + OAuth
registration/discovery/token endpoints, SAML metadata, and the WebSocket
trigger connect.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref to 22abd6d4e229f1206a13ebee8a6a9b808cd82a0d

This commit updates the EE repository reference after PR #684 was merged in windmill-ee-private.

Previous ee-repo-ref: 700feb02ef1b96758ba9425358dbebc83bc02c61

New ee-repo-ref: 22abd6d4e229f1206a13ebee8a6a9b808cd82a0d

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-07-24 15:04:40 +02:00
hugocasaandClaude Opus 4.8 2b58df57fc feat(ai): extract prompt cache token usage from OpenAI and Azure providers (#10214)
* feat(ai): extract prompt cache token usage from OpenAI and Azure providers

Parse the nested cache token details OpenAI returns and thread them into
TokenUsage, matching the Anthropic and Bedrock providers.

- sse.rs: add OpenAIPromptTokensDetails / OpenAIInputTokensDetails and the
  optional prompt_tokens_details / input_tokens_details fields.
- other.rs (Chat Completions) and openai.rs (Responses): populate
  cache_read via .with_cache(cached_tokens, None).

OpenAI's prompt_tokens/input_tokens already include cached tokens (cached is
a subset), so total/prompt are unchanged; cache_read is recorded separately
for reporting. For the same reason the frontend token-usage conversions are
left as-is (adding cached would double-count); optional cache fields and a
clarifying comment are added to prevent a future incorrect Anthropic-style fix.

Fixes WIN-2207

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(ai): pin OpenAI/Azure cache-token deserialization paths

Add regression tests deserializing the real Chat Completions and Responses
usage payloads, guarding the prompt_tokens_details.cached_tokens /
input_tokens_details.cached_tokens paths against a silent rename.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(ai): extract to_token_usage() and test the cache mapping

Address review nit: move the usage->TokenUsage conversion into
OpenAIChatUsage::to_token_usage / OpenAIResponsesUsage::to_token_usage so
the providers call one method and the tests exercise the real mapping.
Tests now assert cache_read is populated while input/total are unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 18:47:25 +02:00
hugocasaandClaude Opus 4.8 8bfe5c9340 fix(ai): stop sending the AI agent system prompt twice for OpenAI (#10126)
* fix(ai): stop sending the AI agent system prompt twice for OpenAI

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ai): document collect_system_prompt precedence and trim duplicate comments

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ai): hoist only the leading system prompt for OpenAI

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 17:53:09 +02:00
Ruben FiszelandClaude Opus 4.8 27ead8d084 fix(ai): disable redirects on worker AI provider client (GHSA-5q4v) (#10122)
* fix(ai): disable redirects on worker AI provider client (GHSA-5q4v)

The worker AI request path issued provider requests with the shared
HTTP_CLIENT, which follows up to 10 redirects without revalidating each
hop. SSRF validation on the provider base_url is single-shot, so a public
base_url could 3xx the worker into a private/internal host (e.g. cloud
metadata), bypassing the private-endpoint protection. The API proxy was
already hardened in #9370; the worker path was missed.

Add a dedicated AI_HTTP_CLIENT with redirects disabled and use it for the
user-controlled provider endpoint, mirroring the API proxy client.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ai): add ALLOW_AI_BASE_URL_REDIRECTS debug escape hatch

Off by default (redirects stay disabled). When set, restores redirect
following on the AI HTTP client for debugging non-standard/self-hosted
gateways, with a startup warning that it weakens SSRF protection.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(ai): correct redirect comment for the escape hatch override

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(ai): condense redirect invariant comments per review

Anchor the SSRF rationale to ALLOW_AI_BASE_URL_REDIRECTS (the knob that
would break it) and shorten the AI_HTTP_CLIENT and call-site comments to
avoid restating it at multiple sites (AGENTS.md).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 11:24:26 +02:00
hugocasaandClaude Opus 4.8 a368d49bd8 feat(ai-agent): support reasoning effort in AI agent workflow steps (#9886)
* feat(ai-agent): support reasoning effort in AI agent workflow steps

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ai-agent): round-trip native Anthropic thinking blocks and fix DeepSeek/Mistral reasoning

Address review: native Anthropic now captures the signed thinking block during streaming and replays it before tool_use across iterations (prevents a 400 on multi-turn tool use). DeepSeek 'off' sends thinking:{type:disabled} instead of the rejected reasoning_effort:none, and Mistral drops temperature when reasoning is on.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(ai-agent): move reasoning effort into the provider/model selector

Store reasoning_effort on ProviderConfig (next to the model) instead of a separate flow arg, and render the selector inside AIProviderPicker under the model dropdown. Add an explicit 'off' option on models that disable reasoning by omission (e.g. Claude), so reasoning can always be turned off from the UI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(ai-agent): use DropdownV2 for reasoning effort, matching copilot chat

Replace the Select combobox with the same DropdownV2 action-menu the copilot chat reasoning selector uses. Each option carries an action instead of a bound value, so click selection is unambiguous and there is no typeahead/sentinel-value mismatch on the off/default entries.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(ai-agent): regenerate system prompts for ProviderConfig.reasoning_effort

Refresh system_prompts/auto-generated and cli skills.gen after adding reasoning_effort to the OpenFlow ProviderConfig schema (check-freshness).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ai-agent): clear stale reasoning effort on model change; dedup bedrock reasoning folding

Address cubic review: (P1) the reasoning picker now clears the stored effort when the newly selected model doesn't accept it (e.g. carrying 'xhigh' from Opus onto a model that tops out at 'high'), not only when the model can't reason at all. (P3) the proxy's accumulate_reasoning_delta now delegates to the shared bedrock_stream_event_to_reasoning_delta so worker and proxy folding can't drift.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(ai-agent): stream reasoning summary and show a thinking affordance in flow chat

Add StreamingEvent::ReasoningTokenDelta, emitted from every worker reasoning path (Anthropic native thinking deltas, Bedrock, Gemini thought parts, OpenAI-compatible reasoning_content, OpenAI Responses reasoning_summary_text with summary:auto). The flow chat parses it and renders a collapsible 'Thinking' affordance on the assistant message (thinking tokens bill regardless of display, so surfacing the summary is billing-neutral).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ai-agent): persist streamed reasoning onto the finished chat message

Reasoning isn't stored server-side, so the completion re-poll (which swaps temp messages for the persisted ones) was dropping the streamed thinking summary. Carry it onto the final assistant message so the 'Thought process' affordance survives the run.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(ai-agent): match flow-chat thinking box to the copilot chat reasoning UI

Replace the plain <details> thinking affordance with the same bordered, collapsible reasoning box the copilot chat uses (chevron + Brain/spinner + 'Thinking' header, markdown body, expand-while-streaming/collapse-on-answer).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ai-agent): attribute streamed reasoning per turn by content; drop duplicated comment

Address review: the completion-poll carry-over now matches each temp assistant turn's thinking summary to its persisted message by content, so a multi-turn response (reasoning -> tool call -> final answer) no longer misattributes an earlier turn's thinking to the final answer or drops intermediate turns. Also removes a leftover duplicated comment block above the AIReasoningEffortPicker effect.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ai-agent): address review round 2 (carry-over edges, off-token validity, aria, test)

cubic round 2: (1) reasoning carry-over now consumes temp turns in order verifying content, so identical/empty-content multi-turn responses attribute thinking correctly and reasoning-only turns aren't dropped; (2) the picker's stale-value check only accepts the off token when the model can actually disable reasoning; (3) add aria-expanded to the Thinking toggle; (4) add a test for the failed tool_result path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ai-agent): add bottom margin below the flow-chat thinking box

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ai-agent): don't request OpenAI reasoning summary, matching the copilot chat

OpenAI gates reasoning summaries behind org verification, so requesting summary: auto would 400 for unverified orgs. The copilot chat requests effort only and never asks for a summary; align the worker with it (reasoning: { effort }) and drop the now-unreachable summary-delta parsing. OpenAI reasoning no longer streams a summary in flow chat (consistent with the copilot); Anthropic/Bedrock/Gemini/DeepSeek reasoning display is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ai-agent): scope reasoning carry-over to newly persisted messages

cubic round 3: matching by content over the full history could attach a new turn's reasoning to an older message with identical text. Restrict eligible targets to the messages just fetched for this response (via afterSeq), so historical turns are never touched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ai-agent): carry reasoning newest-first instead of gating on the final poll

cubic round 4: gating the carry-over on the final poll's filteredResponse dropped reasoning for messages already fetched by an earlier streaming poll (their id is excluded by afterSeq). Walk persisted newest-first and consume the newest matching pending summary, stopping once summaries run out. This response's turns are always at the end, so they claim their own reasoning (P1) before older history is reached (P2), regardless of which poll persisted them.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(ai-agent): drop flow-chat reasoning display, keep backend + effort picker

The chat-side thinking box relied on non-deterministically matching streamed (ephemeral) reasoning back onto persisted messages, which kept spawning edge cases. Remove the flow-chat display entirely (ChatMessage box, FlowChatManager carry-over/threading, parseStreamDeltas reasoning) and keep the sound backend: per-provider reasoning-effort requests, thinking-block round-trips for tool calls, and ReasoningTokenDelta streaming. A display can be built on top later, deterministically (e.g. once the stream carries the persisted message id).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ai-agent): include reasoning_effort in default-config compare; document reasoning_token_delta

Codex/Pi nits: isSameAsStoredConfig now compares reasoning_effort so the 'use as personal default' toggle reflects effort-only changes; openflow streaming-events doc lists the reasoning_token_delta event (regenerated auto prompts).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 17:16:07 +02:00
hugocasaandClaude Opus 4.8 d600c7ecfe fix(ai): route Azure Foundry Claude models via Anthropic Messages API (#9908)
* fix(ai): route Azure Foundry Claude models via Anthropic Messages API

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ai): keep explicit Azure OpenAI deployment base URLs intact

build_azure_openai_url only appends /openai/v1 for a bare resource root; any base with an explicit path (e.g. .../openai/deployments/<id>) is preserved. Adds a regression test and a unit test for usesAnthropicMessagesApi.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(ai): enable Claude extended thinking on Azure Foundry

Route azure_foundry+Claude through the Anthropic reasoning branch (adaptive thinking + output_config.effort) instead of the gpt/o gate, and recognize claude-sonnet-5. Live-verified: sonnet-5 and opus-4-8 on Foundry accept the low/medium/high/xhigh/max ladder and render summarized thinking.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 18:26:12 +02:00
hugocasaandClaude Opus 4.8 d9b080f57f feat(ai): add Azure AI Foundry as a native AI provider (#9879)
* feat(ai): add Azure AI Foundry as a native AI provider

Adds `azure_foundry` as a new AIProvider variant wired through the AI
chat (copilot) and AI agent flow steps. Foundry's chat completions API
is OpenAI-compatible and uses Azure conventions (api-key header, Azure
URL building), so it reuses the existing OpenAI-compatible query builder
and proxy path via the shared `is_azure` helper (renamed from
`is_azure_openai`).

Backend (windmill-ai):
- New `AzureFoundry` enum variant (serde `azure_foundry`)
- `get_base_url` requires a resource base URL (like Azure OpenAI / Custom)
- `is_azure()` covers Azure OpenAI + Foundry (api-key auth, Azure URL)
- Added to OpenAI-compatible proxy support and HttpForward proxy mode
- New proxy URL unit test

Frontend (copilot):
- New provider entry, completion config, model-token handling, streamed
  usage tracking, and reasoning registry (all model-id-gated, so a no-op
  for Foundry's non-OpenAI catalog)
- Treated as a chat-completions provider, not the OpenAI Responses API

OpenAPI:
- `azure_foundry` added to AIProvider (openapi.yaml) and AIProviderKind
  (openflow.openapi.yaml); regenerated CLI guidance

Note: the `azure_foundry` resource type (base_url + optional api_key) is
hub-managed and must be published to the Windmill Hub separately.

Fixes WIN-2122

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ai): add azure_foundry to copilot flow Zod provider enum

The tracked copilot flow schema (openFlowZod.gen.ts and its openFlow.json
source) still carried the old AIProvider enum, so validateFlowModules /
validateSpecialFlowModule rejected AI-generated flow edits that create or
update an aiagent module with provider kind "azure_foundry" before they
could be saved. Add the value to both (preserving the generated single-line
format) and a regression test over the flow-module validation path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(ai): lead provider list with OpenAI, Anthropic, Google AI

Reorder AI_PROVIDERS so the three primary direct providers come first. The
AIProviderPicker renders the first three entries as quick-access buttons, so
these become the defaults (previously OpenAI, Azure OpenAI, Azure Foundry);
Azure OpenAI / Azure Foundry stay adjacent right after. No logic depends on
provider order (only per-provider defaultModels[0] is read).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 23:42:44 +02:00
centdixandClaude Opus 4.8 0cc2257596 fix(ai): emit token usage in gemini proxy streaming translation (#9669)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 10:57:52 +00:00
centdixandClaude Opus 4.6 82e2197922 chore: remove deprecated enable_1m_context from AI providers (#9580)
* chore: remove deprecated enable_1m_context from AI provider code

1M context is now standard on Anthropic models — the beta header
`anthropic-beta: context-1m-2025-08-07` is no longer needed.

Remove the field from ProviderCredentials and AnthropicQueryBuilder,
and stop injecting the beta header in both the API proxy and worker
query builder paths.

The field is kept (as `_enable_1m_context`) on the ProviderResource
deserialization structs in both windmill-api and windmill-ai so
existing resources with the field still deserialize without error.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: drop vestigial _enable_1m_context field from AI resources

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test: assert legacy enable_1m_context keys still deserialize

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-15 18:58:19 +02:00
hugocasa aaf05635ce feat: wire chat reasoning effort through gemini and bedrock proxies (#9545)
* feat: map chat reasoning effort to gemini thinkingConfig in ai proxy

* feat: support claude adaptive thinking through the bedrock ai proxy

* feat: per-model gemini effort levels and thought summary display

* fix: exclude claude opus 4.5 from reasoning effort (rejects adaptive)

* fix: render markdown in thinking blocks and unstick reasoning spinner

* feat: model-aware reasoning effort options across ai chat providers

* fix: scope openrouter reasoning off to the underlying model family
2026-06-12 17:24:46 +02:00
centdix d3f5fe1c8c feat: enable native web search in copilot (#9522)
* feat: enable native web search in copilot

* fix: add web search fallback and settings

* test: use frontend uuid helper

* fix: tighten web search fallback

* fix: add web search error hint

* fix: classify web search fallback errors

* fix: avoid web search fallback tool error

* fix: handle anthropic web search enablement errors
2026-06-12 00:18:22 +02:00
Ruben Fiszel 4b06881918 fix(ai): validate token_url for SSRF in OAuth credentials flow (#9385)
get_token_using_oauth resolved the AI OAuth resource's token_url and
POSTed to it without any SSRF validation, while base_url is validated in
get_base_url. A workspace member with resources:write could point
token_url at an internal/metadata address (e.g. 169.254.169.254),
turning the server into an authenticated blind SSRF probe.

Validate the resolved token_url with validate_url_for_ssrf before the
request, gated behind the same ALLOW_PRIVATE_AI_BASE_URLS opt-in as
base_url so private AI deployments keep working consistently for both
URL fields. ALLOW_PRIVATE_AI_BASE_URLS is now pub so windmill-api can
reuse it instead of re-parsing the env var.
2026-05-30 09:33:27 +00:00
centdix 2553fbfe31 feat: add deepseek fim support (#9365) 2026-05-28 16:52:26 +00:00
centdix a7d85a39ff refactor: clean up ai provider proxy logic (#9360)
* refactor: clean up ai provider proxy logic

* docs: remove completed ai refactor plan

* fix: audit failed google global proxy calls
2026-05-28 16:05:04 +00:00
centdix 4be930f585 refactor: unify AI provider credentials (#9317)
* refactor: use provider credentials for worker builders

* refactor: resolve api proxy credentials directly

* fix: lazy load frontend eval modes
2026-05-26 05:51:32 +00:00
centdix c96be0e903 refactor: move bedrock proxy handling to windmill-ai (#9309)
* refactor: move bedrock proxy handling to windmill-ai

* docs: track ai refactor follow-ups
2026-05-25 14:22:10 +00:00
centdix 289549a048 refactor: move google ai proxy handling to windmill-ai (#9260)
* refactor: add ai proxy execution mode

* refactor: move google ai proxy handling

* refactor: share google ai request building
2026-05-20 15:24:07 +00:00
centdix 55dcee2424 refactor: move anthropic proxy building (#9238) 2026-05-19 14:23:31 +00:00
centdix bd32c5f951 refactor: move openai-compatible proxy building (#9133)
* refactor: introduce ai proxy request types

* refactor: move openai-compatible proxy building
2026-05-18 10:24:16 +00:00
Ruben FiszelandClaude Opus 4.7 4e25954722 fix: don't show ALLOW_PRIVATE_AI_BASE_URLS hint for malformed AI base URLs (#9188)
* fix: don't show ALLOW_PRIVATE_AI_BASE_URLS hint for malformed AI base URLs

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix: impl std::error::Error for SsrfValidationError for anyhow callers

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 07:39:47 +00:00
centdix 17cf538a2d refactor: move ai providers to windmill-ai (#9120) 2026-05-12 14:09:31 +00:00
Ruben FiszelandClaude Opus 4.7 01e21c7f91 fix: preserve negative integers in Bedrock tool schema conversion (#9116)
json_to_document cast any i64 to u64 via `as` and wrapped in PosInt.
Negative integers in a tool's JSON schema (e.g. "default": -1,
"minimum": -1) became huge unsigned values like 18446744073709551615,
which Bedrock rejected with `tools.N.custom.input_schema: int too big
to convert`.

Try as_u64 first to preserve full u64 range, fall back to as_i64 for
negatives via NegInt, then as_f64.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 07:00:58 +00:00
centdix 6f7d31e56b refactor: move ai image handling to windmill-ai (#9098) 2026-05-11 14:59:40 +00:00
centdixandwindmill-internal-app[bot] 27acbbf3d5 refactor: move ai sse plumbing to windmill-ai (#9059)
* docs: refine windmill ai refactor plan

* refactor: move ai sse plumbing to windmill-ai

* refactor: remove ai re-export shims

* fix: update ee ai memory ref

* chore: update ee-repo-ref to d3bc7fa85195b46b7a38d43c2f806520bf8b5454

This commit updates the EE repository reference after PR #560 was merged in windmill-ee-private.

Previous ee-repo-ref: ff35bf7cc198e13884b33654e1d6dbd8a8b314d3

New ee-repo-ref: d3bc7fa85195b46b7a38d43c2f806520bf8b5454

Automated by sync-ee-ref workflow.

---------

Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-05-11 10:01:45 +00:00
centdixandClaude Opus 4.5 77d9a53423 fix: strip additionalProperties from google schemas (#8964)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-28 19:59:45 +00:00
centdixandClaude Opus 4.6 26a6d1e4ce refactor: create windmill-ai crate (part 1 — types, traits, base modules) (#8530)
* refactor: create windmill-ai crate and move base AI types from windmill-common

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: move worker AI types to windmill-ai crate

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: move QueryBuilder trait and StreamEventSink abstraction to windmill-ai

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: add base64 dependency to windmill-ai for bedrock PDF support

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add windmill-ai refactor plan

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: address PR review — remove dead bedrock feature, add boxed_sink helper, move plan to docs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 11:20:27 +00:00