Skip to content

Releases: latere-ai/lux

v0.18.1

Choose a tag to compare

@github-actions github-actions released this 20 Oct 17:00

Added

  • With OTEL_EXPORTER_OTLP_ENDPOINT set, luxd also exports the Go
    runtime's metrics: memory, goroutines, GOGC, GOMAXPROCS and scheduling
    latency, as go.memory.*, go.goroutine.count, go.config.gogc,
    go.processor.limit and go.schedule.duration, through
    latere.ai/x/pkg v0.106.0.

v0.18.0

Choose a tag to compare

@github-actions github-actions released this 18 Oct 14:28

Added

  • GET /v1/requests filters by a request's own labels with
    request_label=<name>=<value>, repeatable, every pair matched against
    the record's requestLabels, the pairs a caller sent in Lux-Labels.
    A harness that labels each call with the run it belongs to reads a
    run's records without keeping their request ids. It is a second filter
    beside label, which still reads the Key's labels, and it narrows what
    the authorizer's filter already lets the caller read: the usage.read
    resource is unchanged. A pair outside the Lux-Labels syntax, or more
    names than the eight a request keeps, is invalid_field at
    request_label; two values for one name are an empty list.
    GET /v1/usage refuses the parameter, since a request label is never
    an aggregate dimension. A cursor is bound to the pairs, and one issued
    for a query without them keeps working. The archive is partitioned by a
    Key label, so a query by request label alone reads every partition and
    its pages may hold fewer records than limit while next_cursor is
    present. lux requests -request-label k=v sends it. For the packages:
    metering.RecordQuery.RequestLabels, gateway.MaxRequestLabels,
    gateway.ValidRequestLabel, and gateway.RequestLabelSyntax.
  • A Key can name the doors it opens with spec.doors, a list of
    openai, anthropic, gemini, and lux. A request on any other door
    is refused with the new code door_not_allowed, 403, in that door's
    error shape, on every route of the door, its model list included,
    before any Model, Provider, window, or upstream is read, and recorded
    as a refused request. A Key without the member opens every door, and
    renders as it did before, so an authorizer or a client built against
    an earlier release reads it unchanged; the authorizer's proposal
    carries the list when it is set. An empty list, an unknown door, and a
    door named twice are invalid_field. A change to the list reaches each
    replica at its next read of the journal. lux keys create -doors
    writes it. For the packages: v1.KeySpec.Doors,
    v1.KeySpec.OpensDoor, and gateway.CodeDoorNotAllowed.

v0.17.3

Choose a tag to compare

@github-actions github-actions released this 18 Oct 12:16

Fixed

  • A picture a tool returns reaches the model when a request is translated
    to OpenAI chat completions or Responses, as every request through the
    Anthropic door to an OpenRouter model is. It was dropped and recorded as
    tool_result.image loss. The Responses door also takes a
    function_call_output whose output is a list of text and image parts.
    (latere.ai/x/pkg v0.101.0)

v0.17.2

Choose a tag to compare

@github-actions github-actions released this 17 Oct 21:48

Fixed

  • The release is published. v0.17.1 was tagged, but its release workflow
    stopped before publishing anything: the step that records the next
    release's fixture read the control plane right after the conformance
    suite had spent its rate for the minute, and the 429 failed the step.
    The step now waits out the refusal. v0.17.2 carries everything v0.17.1
    lists.

v0.17.0

Choose a tag to compare

@github-actions github-actions released this 10 Oct 14:14

Changed

  • A Key's spec.zeroRetention is written only when it is true. v0.16.0
    wrote zeroRetention: false on every Key, in a read, in the store,
    and in the proposal an authorizer is asked about, so an authorizer or
    a client built against v0.15.0 that holds a Key to the members it
    knows refused every Key, whether it used the member or not. A Key
    that does not ask is now the same on the wire as in v0.15.0. Read a
    Key without the member as one that does not ask; a typed client that
    requires the member needs that change. false, written in a manifest,
    is still accepted. The OpenAPI document no longer lists the member as
    required.

v0.16.0

Choose a tag to compare

@github-actions github-actions released this 10 Oct 06:58

Added

  • A Key can ask that none of its requests reach an upstream that may
    keep them, with spec.zeroRetention: true, and a Provider declares
    how its upstream keeps nothing of a request with spec.zeroRetention:
    {} when the upstream keeps nothing of any request, or
    requestFields, merged into such a request's body after
    spec.requestFields and winning on every member both name, for an
    upstream that takes the option per request, such as OpenRouter's
    provider: {zdr: true}. A request on a zero-retention Key reaches
    only declaring Providers and falls over only among them. A Model with
    no declaring target refuses it with the new code
    zero_retention_unavailable, 403, before any upstream is called;
    declaring targets that cannot be tried now answer
    provider_unavailable. An opaque route on such a Key is served only
    toward a Provider that declares {}. A body the fields cannot be
    written into is invalid_request. Requests on other Keys are routed
    and sent as before. The request record (zeroRetention in
    GET /v1/requests), the request log line (zero_retention), and each
    lux.upstream span (lux.zero_retention) say whether a request was
    on such a Key. A change to the flag reaches each replica at its next
    read of the journal. The catalog's openrouter Provider declares
    provider: {zdr: true}, and docs/security.md walks the case. For
    the packages: v1.KeySpec.ZeroRetention, v1.ProviderSpec.ZeroRetention
    and v1.ZeroRetention, gateway.CodeZeroRetentionUnavailable,
    gateway.AttrZeroRetention, and ZeroRetention on gateway.Record
    and metering.Record, which a platform's own Recorder copies across.

Changed

  • Every Key renders spec.zeroRetention, false unless set, as it
    renders its other booleans, so a Key read back after the upgrade
    carries one member more than it was applied with, and the six
    accepted Keys of the golden corpus change that way. A manifest without
    the member keeps the default. The conformance suite's previous-release
    group reads a Key from an earlier release with the member at its
    default.

Fixed

  • A request toward a Provider with spec.requestFields is refused
    invalid_request when any value of its Content-Encoding header, or
    any coding a value lists, is other than identity. Only the first
    header line was read, so a body under a second line naming gzip
    reached the upstream without the fields.

v0.15.0

Choose a tag to compare

@github-actions github-actions released this 09 Oct 19:33

Changed

  • A Key can list up to six Budgets in spec.budgets, up from four. A
    platform that gives each API key it issues a Budget of its own, beside
    an organization's balance, an agent's two limits, and a session's own
    Budget, needs five; the sixth is spare. A Key listing five or six was
    refused invalid_field at spec.budgets and is now accepted, and a
    seventh is refused as a fifth was. Every listed Budget is still a gate
    of its own at the door, held from memory on each request, and
    budget.draw is asked once per entry only when the Key is written.
    v1.MaxKeyBudgets is 6. A Key listing more than four can be written
    once every replica runs this release: a replica on an earlier one
    refuses to write it, and serves one another replica wrote. In the
    golden corpus the refused case key-budgets-five is now
    key-budgets-seven, and the accepted Key agent-session lists six
    Budgets, four of them new accepted cases.

Security

  • Built with Go 1.27.2 and golang.org/x/net v0.60.0, which fix GO-2026-6611, GO-2026-6612, GO-2026-6613 and GO-2026-6617.

v0.14.1

Choose a tag to compare

@github-actions github-actions released this 08 Oct 16:20

Fixed

  • A Model whose every price is zero is served when the Key's Budget or
    spend limit is used up. A hard window lets the request that crosses
    its amount run, so a spent Budget is often past its amount, and from
    then on every request on the Key was refused budget_exhausted, or
    spend_exceeded for the Key's own limit, a free one included. A
    request costs nothing when the Model's input, output,
    cachedInput, and cacheWrite prices are all zero, the same prices
    the meter bills from; such a request is now admitted whatever the
    window has left, counted, and recorded at a cost of 0. Rate limits,
    currency_mismatch, and every check before them still apply to it. A
    request for a priced Model on a spent window is refused as before,
    with the same code, detail, and Retry-After. An unpriced Model is
    not free and is refused as before. The new metering.Free reports
    whether a pricing is free, for a plane with a Limiter of its own.

v0.14.0

Choose a tag to compare

@github-actions github-actions released this 06 Oct 15:38

Fixed

  • A model added to a Key's spec.models is served on the next request
    through any replica. A replica that cached the Key before the change
    used to refuse that model with model_not_allowed until its journal
    tail or LUX_KEY_CACHE caught up, so a call made right after the
    change through another replica than the one that applied it was
    refused. Before it refuses model_not_allowed, a door now reads the
    Key from the store once and decides on what the store holds. A Key
    that truly lacks the model costs one store read per refused request;
    a change that takes access away still reaches every replica within
    LUX_KEY_CACHE, and the door's model list still follows the cache.
    The reread runs when the door's KeyLookup also implements the new
    gateway.KeyRefresher, which serve.KeyCache does; a plane that
    composes the gateway with a KeyLookup of its own and without
    Refresh refuses on what ByHash answered, as before.
  • A provider's refusal is readable in the log and safe in the detail.
    The lux.request log line gains upstream_status, the last provider
    answer's HTTP status and 0 when no provider answered, so a query
    tells a provider's 400 from a refusal Lux made itself; the field was
    never on the line, which is why it read empty. The caller already
    received a provider's 4xx reason in Lux-Error-Detail as it does a
    429 or a 5xx, the status and the first KiB of the provider's body.
    That excerpt now has the Provider's credential replaced with
    [redacted] and any other credential-shaped string, such as a bearer
    token, a JWT, or an sk- key, replaced with *** before it is cut,
    so an upstream that echoes what it was sent no longer hands a
    credential to the caller.

v0.13.0

Choose a tag to compare

@github-actions github-actions released this 04 Oct 15:11

Added

  • A Provider takes spec.requestFields, a JSON object the gateway
    merges into every request body it sends that Provider, after any
    translation, with the Provider's value winning on each member it
    names: an object merges member by member, and any other value, a list
    included, replaces the caller's whole. A caller can then neither leave
    an upstream option out nor turn it off; for OpenRouter,
    provider: {zdr: true} restricts every request to endpoints with zero
    data retention. The merge reaches model routes and opaque routes alike,
    so toward such a Provider an opaque body is read whole rather than
    streamed, and a request whose body is sent under a Content-Encoding
    is refused invalid_request. model, stream, and stream_options
    cannot be set (reserved_prefix), a member cannot be null, and the
    object is at most 16384 bytes encoded (invalid_field). The field is
    not secret: every read and the authorizer's proposal carry it. A
    Provider without it is sent what it was sent before, byte for byte.
    docs/security.md walks the OpenRouter case.