Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ User-visible changes to Imp are recorded here.

## Unreleased

## 0.16.1 — 2026-10-17

### Fixed

- ReActV2 under `finish: :submit` or `finish: :tools` replays a stored turn
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ large-scale benchmarking. Bug reports and pull requests are welcome.
- [Getting started](docs/getting-started/index.md) builds one program step by
step, from the first call to a supervised server, with real scores.
- [Coming from DSPy](docs/coming-from-dspy.md) maps DSPy's names to Imp's.
- [Tutorials](https://github.com/deepfates/imp/tree/v0.16.0/livebooks) are Livebook notebooks
- [Tutorials](https://github.com/deepfates/imp/tree/v0.16.1/livebooks) are Livebook notebooks
you can run offline or with a key.
- The [cheatsheet](docs/cheatsheet.cheatmd) has the common calls on one page.

Expand Down
71 changes: 28 additions & 43 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Imp v0.16.0
# Imp v0.16.1

Imp is a framework for typed, optimizable language-model programs on the BEAM.
This release adds `request_retry:` to `Imp.Run.start/3`, which sends a model
request the provider failed in a way that may pass again in the same run, after
a wait. It also changes two things a caller may rely on: an LM client other
than ReqLLM reports its charge as metadata `:cost`, because `Imp.Core` no longer
reads it from the usage map; and a ReActV2 forced `submit` with invalid
arguments goes to the extractor. A new option and changed behaviour make this
a minor release under Imp's versioning rule.
This release fixes how ReActV2 replays a history turn that a host stored with
the task's inputs and its answer, and no step of the loop's own. Under
`finish: :submit` and `finish: :tools`, since 0.14.0, such a turn replayed only
its inputs: the model read the event and not the answer stored with it. It now
replays as the model's message holding that answer, as it always has under
`finish: :auto`. It adds no options and changes no public API, so under Imp's
versioning rule this is a patch release.

## Install

Expand All @@ -29,44 +29,29 @@ in its release definition; see [releases that use MCP or
ACP](docs/production.md#releases-that-use-mcp-or-acp).
Ordinary Imp startup starts no protocol endpoint.

## Changes and upgrading from 0.15
## Changes and upgrading from 0.16.0

Change the Imp dependency to `{:imp, "~> 0.16.0"}`, run `mix deps.update imp`,
and commit `mix.lock`. No data migration is needed. Check the second item if
you use an LM client of your own, or build response envelopes by hand.
Under `{:imp, "~> 0.16.0"}`, run `mix deps.update imp` and commit `mix.lock`.
No code or data migration is needed.

- **Added: `request_retry:` on `Imp.Run.start/3`.** `request_retry: [waits:
[ms, ...], reserve: ms]` sends a model request that failed with an error
`Imp.Errors.retryable?/1` accepts (408, 425, 429, 5xx, a failed connection)
again inside `Imp.LM.request/2`, after each wait in turn. Under a deadline
the last wait repeats while it leaves `reserve` (60 s by default) of the
deadline; without one, the list is the bound. A provider's `retry-after`
longer than the wait is waited instead. Each attempt is its own
`:model_request` and `:model_response`, and a failed response followed by
another attempt carries `retry_in_ms`. Only the outermost request in a
process retries, so an LM that wraps an LM does not multiply attempts.
Streamed requests are not sent again. `waits` must not be empty. Off unless
given. A timed-out attempt may have been finished and billed by the
provider without a response to record, so a retry can be billed twice;
under a deadline each attempt is capped to the time left.
- **Changed: where a call's charge is read.** OpenRouter bring-your-own-key
billing moved into `Imp.Clients.ReqLLM`, which records the charge it
resolves as `:charge` in its `req_llm` response metadata. `Imp.Core` reads
`LMResponse.cost` from that `:charge` (final when present, `nil` included),
and otherwise from metadata `:cost`; it no longer reads usage `"cost"` or
`"is_byok"`. An LM client other than ReqLLM, or a hand-built response
envelope, must report its charge as `:cost` in its metadata (or
`req_llm.charge`), or its cost is `nil`.
- **Fixed: a forced `submit` with invalid arguments is extracted.** A ReActV2
forced `submit` whose arguments fail validation now goes to the typed
extractor and ends `:extracted`, where it ended `:incomplete` with no
outputs. If that extraction request fails with an LM error, the call
returns `{:error, %Imp.Predict.ReActV2.StepError{}}`, where it returned
`{:ok, prediction}` ending `:incomplete`. A forced `submit` the tool policy
refuses still ends `:incomplete`.
- **Fixed: a stored answer turn replays its answer under every ending.**
ReActV2 reads a stored turn that carries the inputs and the one output of
the task, and neither `next_thought` nor `tool_calls`, as a step that
answered in text. Since 0.14.0 it did so only when the program ended in
plain text (`finish: :auto`); under `finish: :submit` and `:tools`, with
either `history_replay`, the request held the turn's inputs as a user
message and no assistant message after them. A host that writes such a
turn, for example a recap standing in for the earlier conversation, had
its content left out of every request. The turn now replays as one
assistant message holding the answer, with no "not sent" line after it,
since it carries the task's output. Turns the loop writes itself are
unchanged, and the history the caller holds is unchanged. Under
`finish: :auto`, a one-output signature whose output is constrained or not
typed string gets the same replay when the stored value is a string.

Rollback means restoring `0.15.0` in the lock and removing `request_retry:`.
See the [changelog](CHANGELOG.md) for every change.
Rollback means restoring `0.16.0` in the lock; such turns then replay their
inputs alone again under `finish: :submit` and `:tools`. See the
[changelog](CHANGELOG.md) for every change.

## Known limits

Expand Down
2 changes: 1 addition & 1 deletion docs/coming-from-dspy.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ error row too. Each model request has a receive timeout, and
has no time limit of its own (an MCP tool call does, 30 seconds by default),
so to bound an agent, start it with `Imp.start_run/3` and cancel it when you
choose. The
[deployment example](https://github.com/deepfates/imp/blob/v0.16.0/examples/deployment/README.md)
[deployment example](https://github.com/deepfates/imp/blob/v0.16.1/examples/deployment/README.md)
is a complete OTP application.

**Optimizers see what you name.** DSPy finds predictors by walking a module's
Expand Down
2 changes: 1 addition & 1 deletion docs/diving-deeper/modules-and-composition.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ A custom module is your code, so it is not saved as a whole. What an optimizer
chose for it is data: `Imp.ProgramParameters.values/1` reads it,
`Imp.ProgramParameters.apply_values/2` puts it on a freshly built program, and
`Imp.Optimizer.Artifact` writes it to a checksummed file. The
[deployment example](https://github.com/deepfates/imp/blob/v0.16.0/examples/deployment/README.md)
[deployment example](https://github.com/deepfates/imp/blob/v0.16.1/examples/deployment/README.md)
does this in a supervised application.

### Built-in variants
Expand Down
2 changes: 1 addition & 1 deletion docs/diving-deeper/saving-and-artifacts.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Save the whole program when it is made of Imp's modules: `Imp.predict/2`,
when the program is your own struct implementing `Imp.Module`, or when you
want the program's code to live in your release and only its tuned text to
cross the persistence boundary. The
[deployment example](https://github.com/deepfates/imp/tree/v0.16.0/examples/deployment)
[deployment example](https://github.com/deepfates/imp/tree/v0.16.1/examples/deployment)
does the second.

### 2. JSON with a checksum
Expand Down
2 changes: 1 addition & 1 deletion docs/getting-started/running-in-your-application.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ Changing models is then a configuration change, and rotating a key is a
restart.

The
[deployment example](https://github.com/deepfates/imp/blob/v0.16.0/examples/deployment/README.md)
[deployment example](https://github.com/deepfates/imp/blob/v0.16.1/examples/deployment/README.md)
is the complete version of this page: a two-stage program like `TicketTriage`,
learned parameters loaded and reloaded as an `Imp.Optimizer.Artifact`, crashed
and timed-out calls contained, and the whole thing restarted in a fresh OS
Expand Down
2 changes: 1 addition & 1 deletion docs/getting-started/where-to-go-next.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ The module documentation is the reference for every function.
[Running Imp in production](../production.md) covers what the last page began:
supervision, provider failures, runs you can observe and cancel, and
telemetry. The
[deployment example](https://github.com/deepfates/imp/blob/v0.16.0/examples/deployment/README.md)
[deployment example](https://github.com/deepfates/imp/blob/v0.16.1/examples/deployment/README.md)
is a complete application to copy from.

## Try it in a notebook
Expand Down
4 changes: 2 additions & 2 deletions docs/production.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ where its credentials come from, how long a call may take and what it may
cost. This page covers those decisions in the order you meet them: starting
Imp, credentials, concurrency, timeouts, cost and caching, persistence and
telemetry. The
[deployment example](https://github.com/deepfates/imp/tree/v0.16.0/examples/deployment)
[deployment example](https://github.com/deepfates/imp/tree/v0.16.1/examples/deployment)
is a complete OTP application that puts them together.

## Imp in your supervision tree
Expand Down Expand Up @@ -263,7 +263,7 @@ standard error.

## The reference application

[`examples/deployment`](https://github.com/deepfates/imp/tree/v0.16.0/examples/deployment)
[`examples/deployment`](https://github.com/deepfates/imp/tree/v0.16.1/examples/deployment)
is a small OTP application that loads a checksummed artifact at startup, reads
its key from the environment, serves calls from bounded supervised tasks,
answers overload and timeouts with errors instead of blocking, and reloads
Expand Down
2 changes: 1 addition & 1 deletion livebooks/05_operating_imp.livemd
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ Enum.each(busy, &Task.await/1)
refused
```

The [deployment example](https://github.com/deepfates/imp/tree/v0.16.0/examples/deployment)
The [deployment example](https://github.com/deepfates/imp/tree/v0.16.1/examples/deployment)
wraps the same pattern in a `ProgramServer` that also reloads parameters while
serving.

Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule Imp.MixProject do
use Mix.Project

@version "0.16.0"
@version "0.16.1"

def project do
[
Expand Down
2 changes: 1 addition & 1 deletion priv/public_api.json
Original file line number Diff line number Diff line change
Expand Up @@ -10359,7 +10359,7 @@
"types": []
}
],
"package_version": "0.16.0",
"package_version": "0.16.1",
"schema_version": 3,
"scope": "Curated Imp API compiled from package-shipped sources. Exports come from non-hidden documentation entries; callbacks, types, and struct fields are category- and policy-gated."
}
2 changes: 1 addition & 1 deletion test/package_contract_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ defmodule PackageContractTest do
version = Mix.Project.config()[:version]
dependency = hex_dependency(version)

assert version == "0.16.0"
assert version == "0.16.1"
assert File.read!("RELEASE_NOTES.md") =~ "# Imp v#{version}"
assert File.read!("CHANGELOG.md") =~ "## #{version}"
assert File.read!("examples/deployment/mix.exs") =~ dependency
Expand Down
Loading