Skip to content

Releases

lm15 2.3.0 for R

Choose a tag to compare

@MaximeRivest MaximeRivest released this 10 Nov 02:28

In R: new_router(base_models = list("tinker://..." = "Qwen/Qwen3.5-4B")) (also new_lm(base_models = )), then "tinker:tinker://..." as the model.

Tinker (Thinking Machines): base models and the models you trained there (lm15-contract 18b3788; 2,266 of 2,266 checks, the same in every lm15 language; the new rules MAP-21, MAP-22 and MAP-23 are decided and wait for ratification).

  • Two new providers, one key (TINKER_API_KEY). tinker speaks Chat Completions (also spelled thinkingmachines); tinker-anthropic speaks the Anthropic Messages wire. Both serve Tinker's base models (tinker:Qwen/Qwen3.5-4B) and any sampler checkpoint by its path.
  • A URI is one model name. tinker://<run>:train:0/sampler_weights/<n> routes to Tinker whole. Before, any name://… string was read as provider name with model //….
  • Reasoning is switched the way each model family takes it. On tinker, Qwen 3/3.5/3.6, Kimi K2.6 and Nemotron take an on/off switch (an effort word turns reasoning on, and the substitution is recorded); Inkling takes the effort words. A checkpoint follows the family of the model it was trained from when you name that model (the line above). Listing models on tinker shows your checkpoints as fine-tunes with their base model.
  • Error messages from FastAPI servers are kept. An error body that is {"detail": "…"} used to raise with an empty message on every OpenAI-family and Anthropic-family adapter; the detail is now the message (MAP-21).
  • stop means the same everywhere. Tinker returns the stop sequence it stopped at; lm15 cuts it off (MAP-22).
  • New compat values: thinking format reasoning_effort_switch, reasoning off switch, JSON schema none (a server that applies no response_format: dropped with a record), and stop_in_text. Registry entries may carry aliases.
  • Known, not changed in this release: the reader for a Chat Completions response body that did not come through lm15 (response_from_openai_chat in Python; the same door under each language's spelling) is given no request, so a Tinker body that stopped at a stop sequence keeps the sequence at its end. Ordinary calls and streams cut it. On the Messages door, Tinker itself refuses reasoning off and effort words for the on/off families; use tinker for those.

Nothing is removed and no field changes meaning. Tinker calls both of its doors beta.

lm15 2.2.0 for R

Choose a tag to compare

@MaximeRivest MaximeRivest released this 07 Nov 01:23

In R: reasoning("low", summary = "auto").

Claude shows its thinking when you ask for it (lm15-contract 9d8e1ce, MAP-7 rule 7 amended; 2,140 of 2,140 checks, the same in every lm15 language).

  • A reasoning summary on Claude sends thinking.display: "summarized". Since Opus 4.7, Anthropic returns a thinking block with empty text unless the request asks for the summary. On Opus 4.7, Opus 4.8 and every Claude 5 model, asking lm15 for reasoning with summary: "auto" gave an empty thinking part (checked live on 14 Claude models). Any summary now adds the setting, with adaptive thinking and with a thinking budget alike; every Claude model accepts it. Without a summary nothing changes: each model's own default.
  • DeepSeek, Meta and Moonshot over the Anthropic Messages wire are unchanged: they return their thinking without the setting.
  • Known, not changed in this release: effort: "off" sends no thinking object to Claude, and Claude 5 thinks anyway. See the contract's change entry, changes/2026-11-07-anthropic-thinking-display.md.

Nothing is removed and no field changes meaning.

lm15 2.1.0 for R

Choose a tag to compare

@MaximeRivest MaximeRivest released this 05 Nov 16:46

No API change in R. response.incomplete already ended an R stream, and the realtime completion path already completed from final copies; a Responses stream cut by a content filter now finishes content_filter, as the complete call does.

Streams now yield what the provider said, all of it, once (MAP-20). Nothing is removed and no field changes meaning. Contract 77f22f4, 2,132 of 2,132 checks, the same in all six lm15 languages.

  • Parallel tool calls on the Codex subscription keep their inputs. The Codex backend sends the arguments of parallel calls only in each call's final response.output_item.done, never in pieces, and lm15 read only the pieces, so every call arrived with input {} (from stream and from complete alike). A stream's final copy of an item now completes whatever its pieces did not deliver: arguments, message text, a reasoning summary. A piece that contradicts the final copy is refused (StreamAssemblyError, code stream_assembly, with the part's index) instead of either value being chosen.
  • A streamed reasoning summary reads like the complete one: its parts are joined with a newline, as the complete call joins them. They used to run together (**First idea****Second idea**).
  • Gemini parallel calls sent one per chunk stay separate calls. gemini-2.5-pro and gemini-3-flash-preview send each parallel call in a chunk of its own. Every call landed in the same slot and their arguments ran into one unreadable input.

How this is checked from now on: the contract harness compares every OpenAI Responses stream with the stream's own final copy of the whole response (no hand-written answer needed), replays legal re-shapings of every recorded stream (fragments cut elsewhere, joined, re-chunked, dropped where a final copy restates them, contradicted), and every kind of event a recorded stream carries must be classified (mapping/stream-events.json). See lm15-contract/changes/2026-11-05-stream-final-copy.md.

lm15 2.0.0 for R

Choose a tag to compare

@MaximeRivest MaximeRivest released this 01 Nov 10:58

Stable (declared 2026-11-01, after live calls from R to ten providers with API keys and five saved sign-ins: receipts). Within 2.x the chat core keeps working as documented; files, batches, media generation, stored caches, live sessions, Chat Completions ingest and sign-in stay provisional. See the README's Stability section.

In R: usage() refuses parts that do not fit; estimate_cost() returns the exact list price or NULL; pricing_tier() for long prompts; reasoning_usage compat.

A usage counter means one thing on every provider (MAP-19). Breaking for code that reads token counts or estimates cost.

  • input_tokens is every token the model read, cached tokens included; output_tokens is every token it wrote, reasoning included. The cache, reasoning and audio counts are parts of one of those two, and total_tokens is input + output. In 1.x each counter was the provider's own, so the same field meant different things.
  • What changes: Anthropic input (and DeepSeek, Meta and Moonshot on the Anthropic wire) now includes cache reads and writes; a call that read 4,873 cached tokens and 20 new ones reports 4,893, not 20. Gemini and Vertex output now includes the thinking, and input includes the tokens Gemini's own tools added. xAI output now includes reasoning. OpenAI and other Chat Completions servers: unchanged. The provider's own numbers stay in provider_data, for checking a bill.
  • A counter is exact or empty, never a partial sum. When a provider's numbers do not add up the way its wire is documented to count, input and/or output are left empty rather than wrong.
  • Building a usage record checks that parts fit inside wholes and that the total is input + output. A usage record saved by 1.x from Anthropic, Gemini or xAI can fail to load for this reason; the message says so.

Cost estimates are exact or unknown. The estimate takes a usage record and returns the list price, or nothing when an unknown count could change it (and for calls with audio tokens). 1.x charged cached tokens twice on OpenAI, Gemini and xAI (a call with 90,000 of its 100,000 input tokens cached cost $0.039 and was estimated at $0.219) and missed Gemini's and xAI's reasoning. Pricing tiers reprice a whole call above a prompt length, as OpenAI does above 272,000 input tokens and Claude Haiku 5.5 above 100,000; an absent price is unknown, never zero. A reasoning_usage compat setting covers servers that count reasoning beside the completion (the xai preset).

Major version 2 in every language. The major version is now the contract's generation, the same in every lm15 language (lm15-contract playbooks/api-family.md, rule 8); generation 2 begins with this change.

Contract cfc3edc (ratified on 2026-11-01 in 6b801f9, wording only): 1,968 of 1,968 checks, the same commit as Python 2.0.0, TypeScript 2.0.0, Rust 2.0.0-rc.1, Go v2.0.0-rc.1, Julia 2.0.0 (LanguageModel15, joining Julia's registry) and R 2.0.0. Rules in lm15-contract changes/2026-10-31-usage-one-meaning.md.

Not on CRAN.

remotes::install_github("lm15-dev/lm15-r@v2.0.0")

lm15 1.4.0 for R

Choose a tag to compare

@MaximeRivest MaximeRivest released this 15 Oct 23:16

Bounding boxes: ask where something is in a picture. A new judgment helper (bounding_box in Python, Rust, Julia and R; boundingBox in TypeScript; BoundingBox in Go) declares a question whose answer is four numbers, top, bottom, left and right, each a fraction of the picture measured from its top-left corner. Multiply by the picture's size in pixels to draw or crop. On TypeSafe it is Jev 1.14's new Box question (use jev-preview or jev-1.14.0; jev-latest is still 1.13 and answers "Invalid request."), and lm15 sends TypeSafe's TypeSafe-Danger-Zone-Images: true opt-in header for you. On OpenAI, Anthropic, Gemini and the others the same request is ordinary structured output, so one program runs everywhere; how close the box is depends on the model (on one test picture: Jev 1.14 and GPT-5.6 Luna within 0.04, Claude Sonnet 4.5 and Gemini 2.5 Flash about 0.15, Claude Haiku 4.5 off by 0.3 to 0.4). A box is always four edges, even when the thing is absent: ask a yes/no beside it. A box has no probabilities, so probabilities concerns only the other judgments. TypeSafe calls images and boxes beta. Rules in lm15-contract changes/2026-10-15-bounding-box.md.

In R the helper is bounding_box(), not box(), so attaching lm15 never masks graphics::box().

Contract 6dc1c5c: 1,921 of 1,921 checks, the same commit as Python 1.5.0, TypeScript and Rust 1.0.0-rc.11, Go v1.1.0-rc.10, Julia 1.0.0 and R 1.4.0.

Not on CRAN.

lm15 1.3.1 for R

Choose a tag to compare

@MaximeRivest MaximeRivest released this 15 Oct 16:28

A saved cloud login's settings are used. A cloud login saved with its host settings (for example configure("vertex-anthropic", method="cloud", answers={"named": "cli"}, settings={"project": "my-project", "location": "us-east5"})) now sends that project and location. Before, the router ignored them and fell back to the environment and the defaults, so a Vertex login saved with its project failed with "setting 'project' is required" or quietly used GOOGLE_CLOUD_PROJECT and the global location. A router settings entry for the same provider changes only the settings it names; an explicit key or named identity uses no saved login, so none of its settings apply. Found and fixed in lm15-python by Rolf Fredheim (lm15-dev/lm15-python#21). Rules in lm15-contract changes/2026-10-15-saved-cloud-settings.md.

Contract 1cbdc12: 1,912 of 1,912 checks, the same commit as Python 1.4.1, TypeScript and Rust 1.0.0-rc.10, Go v1.1.0-rc.9, Julia 1.0.0 and R 1.3.1.

Not on CRAN.

lm15 1.3.0 for R

Choose a tag to compare

@MaximeRivest MaximeRivest released this 15 Oct 00:28

Jev reads pictures. Put a PNG or JPEG image in the one user message sent to TypeSafe's Jev, on its own or beside text and data (sent as an ordered list), and ask judgments about it ("Is there a person in the image?", "Do the two images show the same product?"). lm15 sends the bytes where TypeSafe expects them and adds its TypeSafe-Danger-Zone-Images: true opt-in header for you. TypeSafe calls images beta: check answers on pictures you know before relying on them. A picture given by URL or file id, another format (WebP, GIF), audio and video are refused before anything is sent. Box questions are not supported yet. Tested live on jev-1.13.0; rules in lm15-contract changes/2026-10-14-jev-images.md.

Contract 10645e5: 1,911 of 1,911 checks, the same commit as Python 1.4.0, TypeScript and Rust 1.0.0-rc.9, Go v1.1.0-rc.8, Julia 1.0.0 and R 1.3.0.

Not on CRAN.

lm15 1.2.0 for R

Choose a tag to compare

@MaximeRivest MaximeRivest released this 12 Oct 23:30

Implements the lm15 contract at commit 88bb560, the same commit as lm15 for
Python 1.3.0, TypeScript and Rust 1.0.0-rc.8 and Go v1.1.0-rc.7, and passes
all 1,905 of its checks.

  • The Meta login (device) reaches the Muse Code subscription, Muse Wonder
    Full included (meta:muse-wonder-full-1.0, Power and Maximum Usage
    plans). Requests go to the address Meta names when it hands out the key,
    only when that is an https meta.ai address; any other address is
    refused before the key is sent. The connection's account label names the
    plan, or says "no Muse Code subscription (pay-as-you-go)". The method stays
    unverified: Meta's terms reserve this key for Muse Code.

lm15 1.1.2 for R

Choose a tag to compare

@MaximeRivest MaximeRivest released this 10 Oct 18:37

Implements the lm15 contract at commit 8eedbae, the same commit as lm15 for
Python 1.2.3, TypeScript and Rust 1.0.0-rc.7 and Go v1.1.0-rc.6, and passes
all 1,904 of its checks.

  • response_text() and parse_json() read a structured answer that came
    back as a data part (a JSON schema with a boolean or enum property is a
    judgment request, answered that way); they returned NULL and failed.

  • reasoning(thinking_budget = 1024) works without effort: it is the
    highest level whose budget (1024, 2048, 8192, 16384, 24576, 32768) is at or
    below the given one, "minimal" below 1024. reasoning("none") and a zero
    budget say how to turn thinking off (reasoning("off")).

lm15 1.1.1 for R

Choose a tag to compare

@MaximeRivest MaximeRivest released this 10 Oct 14:44

Implements the lm15 contract at commit 880f72c, the same commit as lm15 for
Python 1.2.2, TypeScript and Rust 1.0.0-rc.6 and Go v1.1.0-rc.5, and passes
all 1,904 of its checks.

  • A wrong or expired key is an AuthError (code "auth") on every provider.
    Gemini and xAI answer a key they refuse with HTTP 400, and lm15 reported
    invalid_request, so a handler for AuthError missed them. Gemini is
    recognized by the reason its error carries (API_KEY_INVALID, which also
    covers an expired key), xAI by its exact sentence; any other Gemini 400 is
    still invalid_request. lm15-contract MAP-18
    (changes/2026-10-10-bad-key-and-misplaced-key.md).

  • A value given as a named cloud identity that is not one of platform,
    workload, environment or cli is no longer repeated in the error: it is
    usually an API key in the wrong argument. The message says so and that a key
    goes in api_key.