From 9d79352f663c404503d050575d5be1643521b0dc Mon Sep 17 00:00:00 2001 From: deepfates Date: Fri, 16 Oct 2026 01:17:25 -0700 Subject: [PATCH] Prepare Imp 0.14.1 release Version 0.14.1 with tag links for v0.14.1; install lines stay ~> 0.14.0, as for earlier patch releases. The changelog's Unreleased section goes under 0.14.1, and the release notes describe #349. A patch: #349 adds no option and no public API, and fixes how ReActV2 replays histories that cross 0.14.0's opt-in finish: :submit and the default :auto. A history written only in a plain-text loop replays byte-for-byte as in 0.14.0. --- CHANGELOG.md | 2 + README.md | 2 +- RELEASE_NOTES.md | 105 ++++++------------ docs/coming-from-dspy.md | 2 +- docs/diving-deeper/modules-and-composition.md | 2 +- docs/diving-deeper/saving-and-artifacts.md | 2 +- .../running-in-your-application.md | 2 +- docs/getting-started/where-to-go-next.md | 2 +- docs/production.md | 4 +- livebooks/05_operating_imp.livemd | 2 +- mix.exs | 2 +- priv/public_api.json | 2 +- test/package_contract_test.exs | 2 +- 13 files changed, 51 insertions(+), 80 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93adef33..e6bfcc50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ User-visible changes to Imp are recorded here. ## Unreleased +## 0.14.1 — 2026-10-16 + ### Fixed - Under ReActV2's `finish: :submit`, a step that answered in plain text diff --git a/README.md b/README.md index 0f1d300e..4ebf4db9 100644 --- a/README.md +++ b/README.md @@ -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.14.0/livebooks) are Livebook notebooks +- [Tutorials](https://github.com/deepfates/imp/tree/v0.14.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. diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 45a3892e..bd46f8b3 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,15 +1,16 @@ -# Imp v0.14.0 +# Imp v0.14.1 Imp is a framework for typed, optimizable language-model programs on the BEAM. -This release adds two ReActV2 options a caller opts into: `last_request_tools`, -which lets an interrupted turn's last request still run named tools, and -`finish: :submit`, which ends a plain-text turn through `submit` as DSPy's -ReActV2 does. It also changes requests every ReActV2 program with one plain -text output sends: the finish line is worded differently, a forced `submit` -request no longer ends on the model's own message, and an extracted turn's -history now holds its answer. New options and a change to what callers' -models read and what hosts store make this a minor release under Imp's -versioning rule. +This release fixes how ReActV2 replays a history that crosses the two finish +modes 0.14.0 introduced. Under `finish: :submit`, a plain-text answer recorded +under `finish: :auto` is no longer taken for an unsent thought. Under +`finish: :auto`, a thought-only step recorded under `finish: :submit` is +followed by "Your message above was not sent." instead of standing as a sent +message. It adds no options and changes no public API. A history written +only in a plain-text loop replays byte-for-byte as in 0.14.0, and under +`finish: :submit` only steps that carry the signature's outputs are treated +differently. These are fixes to the opt-in `finish: :submit` that 0.14.0 +added, so under Imp's versioning rule this is a patch release. ## Install @@ -31,67 +32,35 @@ 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.13 +## Changes and upgrading from 0.14.0 -Change the Imp dependency to `{:imp, "~> 0.14.0"}`, run `mix deps.update imp`, -and commit `mix.lock`. No data migration is needed: saved programs without -the new options load with their defaults. +Under `{:imp, "~> 0.14.0"}`, run `mix deps.update imp` and commit `mix.lock`. +No code or data migration is needed. -- **Added: `last_request_tools`.** ReActV2 takes a list of tools whose calls - run in the last request of an interrupted turn, for a signature with one - unconstrained text output. Those calls run as any step's do, under the same - tool policy and run authorization, and are recorded in the history; the - turn then ends with the request's text as the answer. Calls to other tools - are listed in `unexecuted_tool_calls` and not run. A program that acts - through a tool and reports in text what it did can still take that action - when a turn is interrupted. Each name must be a tool the program holds; a - signature with `submit`, or `finish: :submit`, refuses the option. The - default, `[]`, changes nothing. A call may give its own list, which replaces - the program's for that call. A one-request call (`max_iters: 0`) is a last - request, so a host asking for a summary gives `last_request_tools: []`. -- **Added: `finish: :submit`.** A one-unconstrained-text-output ReActV2 turn - can end through `submit`, as DSPy's ReActV2 ends every turn, so text the - model did not mean as its answer is not published as one. A step that - writes a thought and calls no tool is followed by an ordinary step in which - the thought stays the model's own message and a user message after it says - "Your message above was not sent. To reply, call `submit` with `answer`; to - keep working, call a tool." (naming the signature's outputs). A second - thought-only step in a row, an empty step, or `max_iters` forces `submit`. - The default, `finish: :auto`, keeps the current rule. -- **Changed: the plain-text finish line.** A plain-text ReActV2 step's system - message now reads "When the final answer is ready, write it as plain text - without calling a tool." where it read "... reply without calling a - tool.", which a program holding a `reply` tool read as being about that - tool. A provider's prompt cache for those programs misses once. Migration: - a test that compares that system message exactly expects the new text. -- **Changed: a forced `submit` request no longer ends on the model's - message.** When the step before it wrote a thought and called nothing, the - request ended on an assistant turn, which Mistral and Gemini refuse. The - request now shows that thought as its last user message under its field's - marker; the history still records it as the model's step. A provider's - refusal of the named `tool_choice`, including one OpenRouter relays, now - gets the `"required"` retry and then the extractor instead of failing the - call. -- **Changed: an extracted turn's history holds its answer.** An `:extracted` - ReActV2 turn's history ends with the extracted outputs, recorded as a step - that called no tool. Replaying such a history shows the model nothing for - that step. A host that stores histories stores one more step for such - turns. -- **Changed: no requests after the deadline.** Once the process's - `Imp.Deadline` has passed, ReActV2 sends no forced `submit`, no - `"required"` retry and no extraction request; the turn is `:incomplete` - with `termination_cause: :deadline_exceeded`. -- **Fixed: a resume with no new inputs.** A ReActV2 call that resumes a held - history with no new inputs, such as `max_iters: 0` with a - `last_request_note`, and whose forced `submit` gets no `submit` back, now - gets its answer from the extractor instead of failing with - `{:missing_input_fields, [...]}`. +- **Fixed: an answer step is never a thought.** Under `finish: :submit`, a + step recorded under `finish: :auto` as the turn's plain-text answer (text + and the signature's outputs both) was treated as a thought-only step when + its history was passed on: the "Your message above was not sent." line + followed each answer that had been delivered, and models sent those + answers again. A step that carries the signature's outputs is an answer: + it gets no line, and the forced `submit` never moves it to the user side. + Only a step with text, no calls and no outputs is a thought. +- **Fixed: a plain-text loop keeps roles alternating.** In a plain-text + ReActV2 loop (`finish: :auto`), a history written under `finish: :submit` + replayed each thought-only step as an assistant message, which there reads + as a message that was sent, often right before the answer's: two assistant + messages in a row. Each such step is now followed by a user message, "Your + message above was not sent." (the line's factual half; a plain-text loop + has no `submit` to name). A history written only in a plain-text loop has + no thought-only step, so its requests are byte-for-byte unchanged. +- **Known gap.** Under `finish: :submit`, a forced request with nothing new + to ask, on a history that ends with a plain-text answer step, still ends + on that assistant message, since an answer is not moved. Resume with new + inputs or a `last_request_note`. -Rollback means restoring `0.13.0` in the lock. 0.13.0 loads a program -saved with `last_request_tools` or `finish: :submit` without those options, -so it runs with none and with `finish: :auto`. -This release does not establish how any particular model responds to the new -wording. See the [changelog](CHANGELOG.md) for every change. +Rollback means restoring `0.14.0` in the lock; mixed-mode histories then +replay as described above before each fix. See the +[changelog](CHANGELOG.md) for every change. ## Known limits diff --git a/docs/coming-from-dspy.md b/docs/coming-from-dspy.md index 7ef5fc02..60c81177 100644 --- a/docs/coming-from-dspy.md +++ b/docs/coming-from-dspy.md @@ -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.14.0/examples/deployment/README.md) +[deployment example](https://github.com/deepfates/imp/blob/v0.14.1/examples/deployment/README.md) is a complete OTP application. **Optimizers see what you name.** DSPy finds predictors by walking a module's diff --git a/docs/diving-deeper/modules-and-composition.md b/docs/diving-deeper/modules-and-composition.md index 94b90204..931b5a90 100644 --- a/docs/diving-deeper/modules-and-composition.md +++ b/docs/diving-deeper/modules-and-composition.md @@ -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.14.0/examples/deployment/README.md) +[deployment example](https://github.com/deepfates/imp/blob/v0.14.1/examples/deployment/README.md) does this in a supervised application. ### Built-in variants diff --git a/docs/diving-deeper/saving-and-artifacts.md b/docs/diving-deeper/saving-and-artifacts.md index ba3956ba..34ea3fd3 100644 --- a/docs/diving-deeper/saving-and-artifacts.md +++ b/docs/diving-deeper/saving-and-artifacts.md @@ -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.14.0/examples/deployment) +[deployment example](https://github.com/deepfates/imp/tree/v0.14.1/examples/deployment) does the second. ### 2. JSON with a checksum diff --git a/docs/getting-started/running-in-your-application.md b/docs/getting-started/running-in-your-application.md index c84ccb76..80f018f7 100644 --- a/docs/getting-started/running-in-your-application.md +++ b/docs/getting-started/running-in-your-application.md @@ -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.14.0/examples/deployment/README.md) +[deployment example](https://github.com/deepfates/imp/blob/v0.14.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 diff --git a/docs/getting-started/where-to-go-next.md b/docs/getting-started/where-to-go-next.md index 39df23d1..e0441d6a 100644 --- a/docs/getting-started/where-to-go-next.md +++ b/docs/getting-started/where-to-go-next.md @@ -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.14.0/examples/deployment/README.md) +[deployment example](https://github.com/deepfates/imp/blob/v0.14.1/examples/deployment/README.md) is a complete application to copy from. ## Try it in a notebook diff --git a/docs/production.md b/docs/production.md index d57ec4bb..1d0ad372 100644 --- a/docs/production.md +++ b/docs/production.md @@ -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.14.0/examples/deployment) +[deployment example](https://github.com/deepfates/imp/tree/v0.14.1/examples/deployment) is a complete OTP application that puts them together. ## Imp in your supervision tree @@ -263,7 +263,7 @@ standard error. ## The reference application -[`examples/deployment`](https://github.com/deepfates/imp/tree/v0.14.0/examples/deployment) +[`examples/deployment`](https://github.com/deepfates/imp/tree/v0.14.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 diff --git a/livebooks/05_operating_imp.livemd b/livebooks/05_operating_imp.livemd index d9295509..3e78bd55 100644 --- a/livebooks/05_operating_imp.livemd +++ b/livebooks/05_operating_imp.livemd @@ -103,7 +103,7 @@ Enum.each(busy, &Task.await/1) refused ``` -The [deployment example](https://github.com/deepfates/imp/tree/v0.14.0/examples/deployment) +The [deployment example](https://github.com/deepfates/imp/tree/v0.14.1/examples/deployment) wraps the same pattern in a `ProgramServer` that also reloads parameters while serving. diff --git a/mix.exs b/mix.exs index 33b1d9c0..b389d057 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule Imp.MixProject do use Mix.Project - @version "0.14.0" + @version "0.14.1" def project do [ diff --git a/priv/public_api.json b/priv/public_api.json index 1fd3096a..0d102a86 100644 --- a/priv/public_api.json +++ b/priv/public_api.json @@ -10357,7 +10357,7 @@ "types": [] } ], - "package_version": "0.14.0", + "package_version": "0.14.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." } diff --git a/test/package_contract_test.exs b/test/package_contract_test.exs index 93fcecee..bd206103 100644 --- a/test/package_contract_test.exs +++ b/test/package_contract_test.exs @@ -114,7 +114,7 @@ defmodule PackageContractTest do version = Mix.Project.config()[:version] dependency = hex_dependency(version) - assert version == "0.14.0" + assert version == "0.14.1" assert File.read!("RELEASE_NOTES.md") =~ "# Imp v#{version}" assert File.read!("CHANGELOG.md") =~ "## #{version}" assert File.read!("examples/deployment/mix.exs") =~ dependency