From 49c5a16bbe8bbf43c1a3894bad621cb6719b0bbc Mon Sep 17 00:00:00 2001 From: deepfates Date: Sat, 17 Oct 2026 23:23:41 -0700 Subject: [PATCH 1/4] Warn about user_name unless the adapter names messages; note where name is not shown Imp.Predict exempts :user_name from the extra-input warning only for the adapters that build messages with Chat's assembly (Chat, JSON, XML, PlanFirst); any other adapter ignores it, so the warning stays. The Chat docs note that many upstreams accept name without showing it to the model. --- CHANGELOG.md | 8 +++++--- lib/imp/adapter/chat.ex | 6 ++++++ lib/imp/predict.ex | 20 +++++++++++++++----- test/adapter_chat_message_name_test.exs | 24 ++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed85abcb..ea402a47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,9 +14,11 @@ User-visible changes to Imp are recorded here. history turn names its messages with `:user_name` and `:assistant_name` beside its fields, and the request inputs name the final user message with `:user_name`. These keys are never rendered as fields, `Imp.Predict` does not - warn about `:user_name` as an extra input, and a signature that declares a - field of either name keeps it as an ordinary field. See "Naming messages" in - `Imp.Adapter.Chat`. + warn about `:user_name` as an extra input when its adapter builds messages + this way (Chat, JSON, XML, PlanFirst), and a signature that declares a field + of either name keeps it as an ordinary field. Many upstreams accept `name` + without showing it to the model, so identity that matters belongs in the + content too. See "Naming messages" in `Imp.Adapter.Chat`. ## 0.16.1 — 2026-10-17 diff --git a/lib/imp/adapter/chat.ex b/lib/imp/adapter/chat.ex index 77e96a6d..91671b52 100644 --- a/lib/imp/adapter/chat.ex +++ b/lib/imp/adapter/chat.ex @@ -71,6 +71,12 @@ defmodule Imp.Adapter.Chat do format has no such field (Anthropic's Messages API) does not receive it, and a message without a name goes out exactly as before. + A name that reaches the provider is not always shown to the model. Many + upstreams accept the field and drop it, among them Google's, DeepSeek's, + Kimi's, Mistral's and Grok's, and open-weight models served through hosts + such as DeepInfra, whose chat templates have no place for it. When the model + must know who is speaking, say so in the message content as well. + ## Extending the default rendering A renderer runs for whichever adapter formats the request, which is not diff --git a/lib/imp/predict.ex b/lib/imp/predict.ex index 8d7d5a6c..fcdc7908 100644 --- a/lib/imp/predict.ex +++ b/lib/imp/predict.ex @@ -135,7 +135,7 @@ defmodule Imp.Predict do adapter <- resolve_adapter(predict), {:ok, inputs} <- normalize_inputs(inputs), inputs = apply_input_defaults(predict.signature, inputs), - :ok <- validate_inputs(predict.signature, inputs), + :ok <- validate_inputs(predict.signature, inputs, adapter), {:ok, request_signature, request_config, reasoning_fields} <- prepare_native_reasoning(predict.signature, lm, predict.config), {format_signature, request_config} = @@ -533,10 +533,20 @@ defmodule Imp.Predict do defp fetch_input(inputs, name), do: Imp.FieldMap.get(inputs, name) - defp validate_inputs(signature, inputs) do - # `:user_name` names the request message (see `Imp.Adapter.Chat`); it is - # message metadata, not an ignored field. - :ok = warn_extra_inputs(signature, inputs, [:user_name]) + # Adapters that build their messages with `Imp.Adapter.Chat`'s assembly read + # a request's `:user_name` as the user message's participant name (see + # "Naming messages" there); any other adapter ignores it, so the warning + # stays. + @message_naming_adapters [ + Imp.Adapter.Chat, + Imp.Adapter.JSON, + Imp.Adapter.XML, + Imp.Adapter.PlanFirst + ] + + defp validate_inputs(signature, inputs, adapter) do + except = if adapter in @message_naming_adapters, do: [:user_name], else: [] + :ok = warn_extra_inputs(signature, inputs, except) :ok = warn_type_mismatches(signature, inputs) required = diff --git a/test/adapter_chat_message_name_test.exs b/test/adapter_chat_message_name_test.exs index 49edb7e2..d09ac674 100644 --- a/test/adapter_chat_message_name_test.exs +++ b/test/adapter_chat_message_name_test.exs @@ -111,4 +111,28 @@ defmodule Imp.Adapter.ChatMessageNameTest do refute log =~ "not in signature" assert_received {:messages, [%{role: :system}, %{role: :user, name: "alice"}]} end + + defmodule PlainAdapter do + @behaviour Imp.Adapter + + @impl true + def format(_signature, inputs, _opts), + do: [%{role: :user, content: Map.fetch!(inputs, :question)}] + + @impl true + def parse(signature, raw, opts), do: Imp.Adapter.Chat.parse(signature, raw, opts) + end + + test "an adapter that does not name messages still warns about user_name" do + lm = Imp.LM.Static.new(handler: fn _messages, _opts -> %{answer: "hello"} end) + program = Imp.predict("question -> answer", lm: lm, adapter: PlainAdapter) + + log = + capture_log(fn -> + assert {:ok, _prediction} = Imp.call(program, %{question: "hi", user_name: "alice"}) + end) + + assert log =~ "not in signature" + assert log =~ "user_name" + end end From 95b870fb41dfa4faed1173df77c390e8b838ef6f Mon Sep 17 00:00:00 2001 From: deepfates Date: Sat, 17 Oct 2026 23:24:10 -0700 Subject: [PATCH 2/4] Prepare Imp 0.16.2 release Version 0.16.2 with tag links for v0.16.2; install lines stay ~> 0.16.0, as for earlier patch releases. The changelog's Unreleased section goes under 0.16.2, and the release notes describe #358 and the follow-up. --- CHANGELOG.md | 2 + README.md | 2 +- RELEASE_NOTES.md | 60 +++++++++++-------- 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, 49 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea402a47..fdcde41a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ User-visible changes to Imp are recorded here. ## Unreleased +## 0.16.2 — 2026-10-18 + ### Added - Messages can carry a participant `name` (the OpenAI chat format's optional diff --git a/README.md b/README.md index c030afa1..b6b6e3ba 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.16.1/livebooks) are Livebook notebooks +- [Tutorials](https://github.com/deepfates/imp/tree/v0.16.2/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 f9d336e2..9c2845be 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,13 +1,11 @@ -# Imp v0.16.1 +# Imp v0.16.2 Imp is a framework for typed, optimizable language-model programs on the BEAM. -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. +This release sends a chat message's participant `name` (the OpenAI chat +format's optional `name`) to the provider, where Imp used to drop it, and lets +a program name the messages of its history turns and its request. A message +without a name is sent exactly as before, so no caller that does not name +messages sees any change. Under Imp's versioning rule this is a patch release. ## Install @@ -29,28 +27,40 @@ 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.16.0 +## Changes and upgrading from 0.16.1 Under `{:imp, "~> 0.16.0"}`, run `mix deps.update imp` and commit `mix.lock`. No code or data migration is needed. -- **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. +- **Added: participant names reach the provider.** `Imp.Clients.ReqLLM` + copies a message's `name` (atom or string key) onto the message it sends, + for generated, streamed and batched requests. Before, `Imp.LM.generate(lm, + [%{role: "user", content: "hi", name: "alice"}])` went out with no `name`. + A provider whose format has no such field, such as Anthropic's Messages + API, does not receive it. +- **Added: naming messages through the adapters.** With `Imp.Adapter.Chat`, + and the JSON, XML and PlanFirst adapters built on it, a history turn names + its user message with `:user_name` and its assistant message with + `:assistant_name`, beside its fields, and the request inputs name the final + user message with `:user_name`: -Rollback means restoring `0.16.0` in the lock; such turns then replay their -inputs alone again under `finish: :submit` and `:tools`. See the + ```elixir + history = Imp.History.new([%{question: "Who won?", answer: "Blue.", user_name: "alice"}]) + Imp.call(predict, %{question: "Rematch?", history: history, user_name: "bob"}) + ``` + + The keys are never rendered as fields, and tool results and notes are + never named. A signature that declares a field called `user_name` or + `assistant_name` keeps it as an ordinary field. `Imp.Predict` does not warn + about `:user_name` as an extra input under those adapters. ReActV2 names + the turns of a history it is given, but not its request message. +- **Not every model sees the name.** Many upstreams accept `name` and drop + it, among them Google's, DeepSeek's, Kimi's, Mistral's and Grok's, and + open-weight models served through hosts such as DeepInfra. When the model + must know who is speaking, say so in the content too. + +Rollback means restoring `0.16.1` in the lock; names are then dropped again +and `:user_name` in the inputs warns as an extra input. 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 1de95752..80438cb2 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.16.1/examples/deployment/README.md) +[deployment example](https://github.com/deepfates/imp/blob/v0.16.2/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 d1b8c95b..3f85a01d 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.16.1/examples/deployment/README.md) +[deployment example](https://github.com/deepfates/imp/blob/v0.16.2/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 1d9c2968..3a0068bc 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.16.1/examples/deployment) +[deployment example](https://github.com/deepfates/imp/tree/v0.16.2/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 ad28fb08..8d709cf1 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.16.1/examples/deployment/README.md) +[deployment example](https://github.com/deepfates/imp/blob/v0.16.2/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 c00b466f..87a998ae 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.16.1/examples/deployment/README.md) +[deployment example](https://github.com/deepfates/imp/blob/v0.16.2/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 8b5c1356..b3eaf551 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.16.1/examples/deployment) +[deployment example](https://github.com/deepfates/imp/tree/v0.16.2/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.16.1/examples/deployment) +[`examples/deployment`](https://github.com/deepfates/imp/tree/v0.16.2/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 6fd60705..18815eb7 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.16.1/examples/deployment) +The [deployment example](https://github.com/deepfates/imp/tree/v0.16.2/examples/deployment) wraps the same pattern in a `ProgramServer` that also reloads parameters while serving. diff --git a/mix.exs b/mix.exs index 19aa7c82..38d35923 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule Imp.MixProject do use Mix.Project - @version "0.16.1" + @version "0.16.2" def project do [ diff --git a/priv/public_api.json b/priv/public_api.json index 80c3d221..f397284b 100644 --- a/priv/public_api.json +++ b/priv/public_api.json @@ -10359,7 +10359,7 @@ "types": [] } ], - "package_version": "0.16.1", + "package_version": "0.16.2", "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 316db6f2..88f54439 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.16.1" + assert version == "0.16.2" assert File.read!("RELEASE_NOTES.md") =~ "# Imp v#{version}" assert File.read!("CHANGELOG.md") =~ "## #{version}" assert File.read!("examples/deployment/mix.exs") =~ dependency From f2051060f2d9ade813ff56b1af249955b386ed13 Mon Sep 17 00:00:00 2001 From: deepfates Date: Sat, 17 Oct 2026 23:33:21 -0700 Subject: [PATCH 3/4] Make the release Imp 0.17.0 The new :user_name and :assistant_name input keys are public API a caller can rely on, so under Imp's versioning rule this is a minor release: version 0.17.0, install lines ~> 0.17.0, tag links for v0.17.0, the changelog heading 0.17.0, and release notes for upgrading from 0.16.1. --- CHANGELOG.md | 2 +- README.md | 4 ++-- RELEASE_NOTES.md | 19 +++++++++++-------- docs/coming-from-dspy.md | 4 ++-- 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/setting-up.md | 4 ++-- docs/getting-started/where-to-go-next.md | 2 +- docs/production.md | 8 ++++---- examples/deployment/README.md | 2 +- examples/deployment/mix.exs | 2 +- livebooks/01_real_lm_front_door.livemd | 2 +- livebooks/02_without_a_provider.livemd | 2 +- livebooks/03_evaluate_and_optimize.livemd | 2 +- livebooks/04_tools_agents_mcp_rlm.livemd | 2 +- livebooks/05_operating_imp.livemd | 4 ++-- mix.exs | 2 +- priv/public_api.json | 2 +- test/package_contract_test.exs | 2 +- 20 files changed, 37 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fdcde41a..d23901f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ User-visible changes to Imp are recorded here. ## Unreleased -## 0.16.2 — 2026-10-18 +## 0.17.0 — 2026-10-18 ### Added diff --git a/README.md b/README.md index b6b6e3ba..f49d5c06 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,7 @@ text or JSON you can score, such as an agent's tool descriptions. ## Install ```elixir -{:imp, "~> 0.16.0"} +{:imp, "~> 0.17.0"} ``` Imp needs Elixir 1.19 or later and a C and C++ compiler, for the native code @@ -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.2/livebooks) are Livebook notebooks +- [Tutorials](https://github.com/deepfates/imp/tree/v0.17.0/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 9c2845be..a90e2b16 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,20 +1,21 @@ -# Imp v0.16.2 +# Imp v0.17.0 Imp is a framework for typed, optimizable language-model programs on the BEAM. This release sends a chat message's participant `name` (the OpenAI chat format's optional `name`) to the provider, where Imp used to drop it, and lets a program name the messages of its history turns and its request. A message without a name is sent exactly as before, so no caller that does not name -messages sees any change. Under Imp's versioning rule this is a patch release. +messages sees any change. The new input keys are public API a caller can rely +on, so under Imp's versioning rule this is a minor release. ## Install ```elixir -{:imp, "~> 0.16.0"} +{:imp, "~> 0.17.0"} ``` -`~> 0.16.0` takes later 0.16 patch releases and nothing newer. The two-part -form, `~> 0.16`, means `>= 0.16.0 and < 1.0.0` in Mix, so `mix deps.update +`~> 0.17.0` takes later 0.17 patch releases and nothing newer. The two-part +form, `~> 0.17`, means `>= 0.17.0 and < 1.0.0` in Mix, so `mix deps.update imp` under it takes every later 0.x release, including minor releases that change what callers receive. @@ -29,8 +30,10 @@ Ordinary Imp startup starts no protocol endpoint. ## Changes and upgrading from 0.16.1 -Under `{:imp, "~> 0.16.0"}`, run `mix deps.update imp` and commit `mix.lock`. -No code or data migration is needed. +Change the Imp dependency to `{:imp, "~> 0.17.0"}`, run `mix deps.update imp`, +and commit `mix.lock`. No code or data migration is needed. A program that +already puts `name` on a message it gives `Imp.LM`, or `:user_name` in its +inputs, now sends it. - **Added: participant names reach the provider.** `Imp.Clients.ReqLLM` copies a message's `name` (atom or string key) onto the message it sends, @@ -59,7 +62,7 @@ No code or data migration is needed. open-weight models served through hosts such as DeepInfra. When the model must know who is speaking, say so in the content too. -Rollback means restoring `0.16.1` in the lock; names are then dropped again +Rollback means restoring `{:imp, "~> 0.16.0"}` and `0.16.1` in the lock; names are then dropped again and `:user_name` in the inputs warns as an extra input. See the [changelog](CHANGELOG.md) for every change. diff --git a/docs/coming-from-dspy.md b/docs/coming-from-dspy.md index 80438cb2..c2241c02 100644 --- a/docs/coming-from-dspy.md +++ b/docs/coming-from-dspy.md @@ -12,7 +12,7 @@ brought in. ## The five-minute version ```elixir -# pip install dspy -> {:imp, "~> 0.16.0"} +# pip install dspy -> {:imp, "~> 0.17.0"} # lm = dspy.LM("openai/...") -> lm = Imp.req_llm("openai:gpt-5.4-mini", api_key: ...) # dspy.Predict("q -> a") -> program = Imp.predict("q -> a", lm: lm) # program(q="...") -> {:ok, pred} = Imp.call(program, %{q: "..."}) @@ -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.2/examples/deployment/README.md) +[deployment example](https://github.com/deepfates/imp/blob/v0.17.0/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 3f85a01d..d3b43d89 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.16.2/examples/deployment/README.md) +[deployment example](https://github.com/deepfates/imp/blob/v0.17.0/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 3a0068bc..d0b80f60 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.16.2/examples/deployment) +[deployment example](https://github.com/deepfates/imp/tree/v0.17.0/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 8d709cf1..dfd2942a 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.16.2/examples/deployment/README.md) +[deployment example](https://github.com/deepfates/imp/blob/v0.17.0/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/setting-up.md b/docs/getting-started/setting-up.md index 7f1edc70..5d84bff3 100644 --- a/docs/getting-started/setting-up.md +++ b/docs/getting-started/setting-up.md @@ -5,7 +5,7 @@ Add Imp to a Mix project: ~~~elixir def deps do [ - {:imp, "~> 0.16.0"} + {:imp, "~> 0.17.0"} ] end ~~~ @@ -13,7 +13,7 @@ end or, for a script or a Livebook notebook: ~~~elixir -Mix.install([{:imp, "~> 0.16.0"}]) +Mix.install([{:imp, "~> 0.17.0"}]) ~~~ Imp needs Elixir 1.19 and a C and C++ compiler, for the native code in two diff --git a/docs/getting-started/where-to-go-next.md b/docs/getting-started/where-to-go-next.md index 87a998ae..1f472dd4 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.16.2/examples/deployment/README.md) +[deployment example](https://github.com/deepfates/imp/blob/v0.17.0/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 b3eaf551..55f4fc59 100644 --- a/docs/production.md +++ b/docs/production.md @@ -5,16 +5,16 @@ 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.2/examples/deployment) +[deployment example](https://github.com/deepfates/imp/tree/v0.17.0/examples/deployment) is a complete OTP application that puts them together. ## Imp in your supervision tree -Imp is an OTP application, and adding `{:imp, "~> 0.16.0"}` to your +Imp is an OTP application, and adding `{:imp, "~> 0.17.0"}` to your dependencies starts it with yours. It supervises its settings, its response cache and the task pools that bound its work. It opens no listener and starts no subprocess, and the MCP and ACP runtimes start only when you use -them. In a script, `Mix.install([{:imp, "~> 0.16.0"}])` starts it too. +them. In a script, `Mix.install([{:imp, "~> 0.17.0"}])` starts it too. ## Credentials at runtime @@ -263,7 +263,7 @@ standard error. ## The reference application -[`examples/deployment`](https://github.com/deepfates/imp/tree/v0.16.2/examples/deployment) +[`examples/deployment`](https://github.com/deepfates/imp/tree/v0.17.0/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/examples/deployment/README.md b/examples/deployment/README.md index 7787ce15..9e8c1099 100644 --- a/examples/deployment/README.md +++ b/examples/deployment/README.md @@ -150,4 +150,4 @@ Use a whole-program artifact for a supported portable Imp shape. Use its selected predictor parameters should cross the persistence boundary. During source development set `IMP_PATH` to the Imp checkout. Other -applications use the Hex dependency in `mix.exs`, `{:imp, "~> 0.16.0"}`. +applications use the Hex dependency in `mix.exs`, `{:imp, "~> 0.17.0"}`. diff --git a/examples/deployment/mix.exs b/examples/deployment/mix.exs index 7149c877..7b370e49 100644 --- a/examples/deployment/mix.exs +++ b/examples/deployment/mix.exs @@ -11,7 +11,7 @@ defmodule ImpDeployment.MixProject do defp deps do case System.get_env("IMP_PATH") do - nil -> [{:imp, "~> 0.16.0"}] + nil -> [{:imp, "~> 0.17.0"}] path -> [{:imp, path: path}] end end diff --git a/livebooks/01_real_lm_front_door.livemd b/livebooks/01_real_lm_front_door.livemd index ba9629a6..2ddda403 100644 --- a/livebooks/01_real_lm_front_door.livemd +++ b/livebooks/01_real_lm_front_door.livemd @@ -7,7 +7,7 @@ imp_path = System.get_env("IMP_PATH") lockfile = imp_path && Path.join(imp_path, "mix.lock") cond do - is_nil(imp_path) -> Mix.install([{:imp, "~> 0.16.0"}]) + is_nil(imp_path) -> Mix.install([{:imp, "~> 0.17.0"}]) File.regular?(lockfile) -> Mix.install([{:imp, path: imp_path}], lockfile: lockfile) true -> Mix.install([{:imp, path: imp_path}]) end diff --git a/livebooks/02_without_a_provider.livemd b/livebooks/02_without_a_provider.livemd index 47176b20..7fa41733 100644 --- a/livebooks/02_without_a_provider.livemd +++ b/livebooks/02_without_a_provider.livemd @@ -7,7 +7,7 @@ imp_path = System.get_env("IMP_PATH") lockfile = imp_path && Path.join(imp_path, "mix.lock") cond do - is_nil(imp_path) -> Mix.install([{:imp, "~> 0.16.0"}]) + is_nil(imp_path) -> Mix.install([{:imp, "~> 0.17.0"}]) File.regular?(lockfile) -> Mix.install([{:imp, path: imp_path}], lockfile: lockfile) true -> Mix.install([{:imp, path: imp_path}]) end diff --git a/livebooks/03_evaluate_and_optimize.livemd b/livebooks/03_evaluate_and_optimize.livemd index 81997f94..b1c645e4 100644 --- a/livebooks/03_evaluate_and_optimize.livemd +++ b/livebooks/03_evaluate_and_optimize.livemd @@ -7,7 +7,7 @@ imp_path = System.get_env("IMP_PATH") lockfile = imp_path && Path.join(imp_path, "mix.lock") cond do - is_nil(imp_path) -> Mix.install([{:imp, "~> 0.16.0"}]) + is_nil(imp_path) -> Mix.install([{:imp, "~> 0.17.0"}]) File.regular?(lockfile) -> Mix.install([{:imp, path: imp_path}], lockfile: lockfile) true -> Mix.install([{:imp, path: imp_path}]) end diff --git a/livebooks/04_tools_agents_mcp_rlm.livemd b/livebooks/04_tools_agents_mcp_rlm.livemd index 1844ec03..6d562926 100644 --- a/livebooks/04_tools_agents_mcp_rlm.livemd +++ b/livebooks/04_tools_agents_mcp_rlm.livemd @@ -7,7 +7,7 @@ imp_path = System.get_env("IMP_PATH") lockfile = imp_path && Path.join(imp_path, "mix.lock") cond do - is_nil(imp_path) -> Mix.install([{:imp, "~> 0.16.0"}]) + is_nil(imp_path) -> Mix.install([{:imp, "~> 0.17.0"}]) File.regular?(lockfile) -> Mix.install([{:imp, path: imp_path}], lockfile: lockfile) true -> Mix.install([{:imp, path: imp_path}]) end diff --git a/livebooks/05_operating_imp.livemd b/livebooks/05_operating_imp.livemd index 18815eb7..ebe27e91 100644 --- a/livebooks/05_operating_imp.livemd +++ b/livebooks/05_operating_imp.livemd @@ -7,7 +7,7 @@ imp_path = System.get_env("IMP_PATH") lockfile = imp_path && Path.join(imp_path, "mix.lock") cond do - is_nil(imp_path) -> Mix.install([{:imp, "~> 0.16.0"}]) + is_nil(imp_path) -> Mix.install([{:imp, "~> 0.17.0"}]) File.regular?(lockfile) -> Mix.install([{:imp, path: imp_path}], lockfile: lockfile) true -> Mix.install([{:imp, path: imp_path}]) end @@ -103,7 +103,7 @@ Enum.each(busy, &Task.await/1) refused ``` -The [deployment example](https://github.com/deepfates/imp/tree/v0.16.2/examples/deployment) +The [deployment example](https://github.com/deepfates/imp/tree/v0.17.0/examples/deployment) wraps the same pattern in a `ProgramServer` that also reloads parameters while serving. diff --git a/mix.exs b/mix.exs index 38d35923..1f175045 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule Imp.MixProject do use Mix.Project - @version "0.16.2" + @version "0.17.0" def project do [ diff --git a/priv/public_api.json b/priv/public_api.json index f397284b..beac3fc2 100644 --- a/priv/public_api.json +++ b/priv/public_api.json @@ -10359,7 +10359,7 @@ "types": [] } ], - "package_version": "0.16.2", + "package_version": "0.17.0", "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 88f54439..b1367d30 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.16.2" + assert version == "0.17.0" assert File.read!("RELEASE_NOTES.md") =~ "# Imp v#{version}" assert File.read!("CHANGELOG.md") =~ "## #{version}" assert File.read!("examples/deployment/mix.exs") =~ dependency From 27377c926208dc41eebfa59eb34bd934ae3eb338 Mon Sep 17 00:00:00 2001 From: deepfates Date: Sat, 17 Oct 2026 23:39:54 -0700 Subject: [PATCH 4/4] Rewrap the 0.17.0 release notes' rollback paragraph --- RELEASE_NOTES.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index a90e2b16..aa8956b2 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -62,9 +62,9 @@ inputs, now sends it. open-weight models served through hosts such as DeepInfra. When the model must know who is speaking, say so in the content too. -Rollback means restoring `{:imp, "~> 0.16.0"}` and `0.16.1` in the lock; names are then dropped again -and `:user_name` in the inputs warns as an extra input. See the -[changelog](CHANGELOG.md) for every change. +Rollback means restoring `{:imp, "~> 0.16.0"}` and `0.16.1` in the lock; +names are then dropped again and `:user_name` in the inputs warns as an +extra input. See the [changelog](CHANGELOG.md) for every change. ## Known limits