npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@molecule/api-ai-decisions-liquid-d1

v1.1.3

Published

Liquid d1 decisions provider for molecule.dev — typed choice/score/yes-no answers with images from Liquid AI's d1 decision models, hosted or self-hosted via llama-server

Readme

@molecule/api-ai-decisions-liquid-d1

Liquid d1 decisions provider for molecule.dev — typed choice/score/yes-no answers, with images, from Liquid AI's d1 decision models, hosted or self-hosted.

d1 answers choice, score and yes/no questions about text, JSON or images with a probability distribution instead of generated text, in one forward pass. This bond calls POST https://api.liquid.ai/decisions/v1/systemone with your LIQUID_API_KEY — or, when LIQUID_DECISIONS_URL is set, the identical /v1/systemone route of a llama-server running the open d1 weights, so the same bond covers hosted and self-hosted.

Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit src/index.ts JSDoc, not this file.

Quick Start

# Self-host instead of paying for the API (no key needed):
llama-server -hf LiquidAI/d1-3B-GGUF:Q8_0   # serves /v1/systemone on :8080
# then set LIQUID_DECISIONS_URL=http://127.0.0.1:8080
import { setProvider, requireProvider } from '@molecule/api-ai-decisions'
import { provider } from '@molecule/api-ai-decisions-liquid-d1'

setProvider(provider) // reads LIQUID_API_KEY on each call

const { answers } = await requireProvider().decide({
  state: 'I was charged twice this month, please refund one of them.',
  questions: {
    refund: { type: 'yesNo', instructions: 'The customer is asking for a refund.' },
    team: {
      type: 'choice',
      instructions: 'Which team should handle this?',
      criteria: { billing: 'charges, refunds, invoices', technical: 'app or site faults' },
    },
    urgency: {
      type: 'score',
      instructions: 'How urgent is this?',
      criteria: ['Can wait', 'Today', 'Blocking the customer now'],
    },
  },
})
answers.refund.probability // 0.999
answers.team.choice // 'billing'
answers.urgency.score // 2.9995

Type

provider

Installation

npm install @molecule/api-ai-decisions-liquid-d1 @molecule/api-ai-decisions @molecule/api-secrets

API

Interfaces

LiquidD1Config

Configuration for the Liquid d1 decisions provider.

interface LiquidD1Config {
  /**
   * Liquid API key (console.liquid.ai → Dashboard > API Keys; keys start with
   * `liquid_`). Defaults to the `LIQUID_API_KEY` env var. Required for the
   * hosted API; not needed when `decisionsUrl` points at your own keyless
   * `llama-server`.
   */
  apiKey?: string
  /**
   * Extra request headers, resolved before each request (retries included)
   * and merged over the defaults — for a host whose auth expires or is not a
   * bearer token (a Cloud Run ID token, Modal proxy auth). Pair with
   * `@molecule/api-model-hosting`: `headers: () => hosting.authHeaders(endpoint.id)`.
   */
  headers?: () => Record<string, string> | Promise<Record<string, string>>
  /**
   * Hosted API base URL. Defaults to `LIQUID_BASE_URL`, then
   * `https://api.liquid.ai`; the route `/decisions/v1/systemone` is appended.
   * Point it at a gateway that relays the Liquid decisions API.
   */
  baseUrl?: string
  /**
   * Base URL of a self-hosted server that serves the systemone route at
   * `/v1/systemone` — a `llama-server` running the open d1 weights
   * (`llama-server -hf LiquidAI/d1-3B-GGUF:Q8_0` → `http://127.0.0.1:8080`).
   * Defaults to the `LIQUID_DECISIONS_URL` env var. When set, no API key is
   * required (set `apiKey` only for an authenticating proxy in front of it).
   */
  decisionsUrl?: string
  /** Default model id: `'d1'` (text + images) or `'d1:free'` (text-only). Defaults to `'d1'`. */
  model?: string
  /**
   * Per-request deadline in milliseconds. Defaults to 30000 (a d1 decision is
   * one forward pass; this bounds a hung connection, which no retry can fix).
   * The caller's `signal`, when one is passed, and this deadline BOTH apply —
   * whichever fires first. Retries share the one budget, like the sibling
   * `ai-decisions` bonds.
   */
  timeoutMs?: number
}

WireQuestion

One question as sent on the wire.

interface WireQuestion {
  type: 'choice' | 'score' | 'noul'
  instructions: string
  criteria?: Record<string, string> | string[]
}

WireAnswer

One answer as received on the wire (only the fields we read).

interface WireAnswer {
  type?: string
  choice?: string
  score?: number
  noul?: number
  probabilities?: Record<string, number>
}

WireResponse

The response body (only the fields we read).

interface WireResponse {
  model?: string
  answers?: Record<string, WireAnswer>
  usage?: { input_tokens?: number; output_tokens?: number }
}

PostOptions

Options for {@link postSystemOne}.

interface PostOptions {
  /** Full endpoint URL. */
  url: string
  /** Bearer token, if any. */
  apiKey?: string
  /** Extra headers resolved before each attempt, retries included (merged over the defaults). */
  headers?: () => Record<string, string> | Promise<Record<string, string>>
  /** Request body. */
  body: Record<string, unknown>
  /** Abort signal. */
  signal?: AbortSignal
  /** Label for error messages (`'Liquid d1'`). */
  label: string
  /** Max retries on a retryable status (default 3). */
  maxRetries?: number
}

Functions

createProvider(config)

Creates a Liquid d1 decisions provider.

function createProvider(config?: LiquidD1Config): AIDecisionsProvider
  • config — API key, base URL / self-host URL and default model.

Returns: An AIDecisionsProvider backed by Liquid's d1 decision models.

toWireQuestions(questions)

Converts the core's questions into wire questions (yesNo → noul).

function toWireQuestions(questions: Record<string, DecisionQuestion>): Record<string, WireQuestion>
  • questions — The questions keyed by id.

Returns: The wire questions object.

fromWireAnswer(id, question, wire, minConfidence)

Converts one wire answer into the core's answer for question.

function fromWireAnswer(
  id: string,
  question: DecisionQuestion,
  wire: WireAnswer | undefined,
  minConfidence?: number,
): DecisionAnswer
  • id — The question id (for error messages).
  • question — The question that was asked.
  • wire — The wire answer.
  • minConfidence — Optional low-confidence threshold.

Returns: The typed answer.

fromWireResponse(questions, body, minConfidence)

Converts a whole wire response into the core's result.

function fromWireResponse<Q extends Record<string, DecisionQuestion>>(
  questions: Q,
  body: WireResponse,
  minConfidence?: number,
): DecideResult<Q>
  • questions — The questions that were asked.
  • body — The parsed response body.
  • minConfidence — Optional low-confidence threshold.

Returns: The typed result.

postSystemOne(opts)

POSTs a systemone request with retry on 429/5xx-busy, honouring Retry-After; an aborting signal ends the backoff early with the signal's reason. Throws an Error carrying status on a non-2xx response or a 2xx response whose body is not JSON.

function postSystemOne(opts: PostOptions): Promise<WireResponse>
  • opts — Request options.

Returns: The parsed response body.

Constants

DEFAULT_LIQUID_D1_URL

Liquid AI's API host.

const DEFAULT_LIQUID_D1_URL: 'https://api.liquid.ai'

HOSTED_LIQUID_D1_PATH

The systemone route on the hosted Liquid API (nesting is Liquid's).

const HOSTED_LIQUID_D1_PATH: '/decisions/v1/systemone'

SELF_HOSTED_D1_PATH

The systemone route on a llama-server running the open d1 weights.

const SELF_HOSTED_D1_PATH: '/v1/systemone'

DEFAULT_LIQUID_D1_MODEL

Liquid's flagship model id.

const DEFAULT_LIQUID_D1_MODEL: 'd1'

LIQUID_D1_LIMITS

The hosted API's request limits (docs.liquid.ai/lfm/models/d1).

const LIQUID_D1_LIMITS: {
  readonly maxImages: 8
  readonly minScoreLevels: 2
  readonly maxScoreLevels: 10
}

LIQUID_D1_IMAGE_TYPES

Image media types the hosted API accepts (llama-server decodes the same four).

const LIQUID_D1_IMAGE_TYPES: readonly string[]

provider

The default provider, configured from env vars on each call (wire with setProvider). Safe to import before secrets are loaded.

const provider: AIDecisionsProvider

aiDecisionsLiquidD1SecretDefinitions

Secret definitions required by the Liquid d1 decisions bond.

const aiDecisionsLiquidD1SecretDefinitions: SecretDefinition[]

Core Interface

Implements @molecule/api-ai-decisions interface.

Bond Wiring

Setup function to register this provider with the core interface:

import { setProvider } from '@molecule/api-ai-decisions'
import { provider } from '@molecule/api-ai-decisions-liquid-d1'

export function setupAiDecisionsLiquidD1(): void {
  setProvider(provider)
}

Injection Notes

Requirements

Peer dependencies:

  • @molecule/api-ai-decisions ^1.0.0
  • @molecule/api-secrets ^1.0.1

Environment Variables

  • LIQUID_API_KEY (optional) — Liquid AI API key
    • Setup: Create a key at console.liquid.ai (Dashboard > API Keys; keys start with liquid_); it is sent as a bearer token to the Liquid d1 decisions API. Required for the hosted API; not needed when LIQUID_DECISIONS_URL points at your own llama-server.
    • Get it here: https://console.liquid.ai
    • Example: liquid_...
  • LIQUID_DECISIONS_URL (optional) — Self-hosted d1 server URL
    • Setup: Base URL of a llama-server running the open d1 weights (llama-server -hf LiquidAI/d1-3B-GGUF:Q8_0 binds http://127.0.0.1:8080); the bond calls its /v1/systemone route with no API key. Leave unset to use the hosted Liquid API.
    • Get it here: https://docs.liquid.ai/lfm/models/d1-3b
    • Example: http://127.0.0.1:8080

Runtime Dependencies

  • @molecule/api-ai-decisions

  • @molecule/api-secrets

  • Config: LIQUID_API_KEY (required for the hosted API — sent as Authorization: Bearer …, created at console.liquid.ai; keys start with liquid_), LIQUID_DECISIONS_URL (self-hosted llama-server base URL, no key needed), LIQUID_BASE_URL (gateway in front of the hosted API), and createProvider({ model }) (default 'd1', or pass model per call).

  • d1:free is text-only. Passing images with model d1:free throws before any request (the API's answer is "The model 'd1:free' does not accept images."). Paid d1 reads images.

  • Image rules (count and format are checked here before sending): up to 8 images per request of JPEG / PNG / WebP / GIF, base64 only — remote URLs are rejected. The API itself also enforces: whole request body under 4.5 MB, ≤10,000 patches of 32×32 px across all images (1.5 tokens per patch), and the longer side at most 100× the shorter.

  • Billing quirk: every question is charged for its text AND all images again (a 1024×1024 image is 1,536 tokens per question). Batch questions over one state instead of one call per question. Billed on input tokens only ($0.04/M at launch); usage.outputTokens is always 0.

  • Score questions take 2–10 levels, lowest first (checked here before sending).

  • confidence in the answers is the probability of the reported answer, NOT d1's own confidence field (see the core's remarks).

  • License — the open weights are NOT Apache-2.0. d1-3B and d1-omni-600M ship the LFM Open License v1.0: commercial use by a legal entity over $10,000,000 annual revenue is NOT licensed — buy a commercial license from Liquid AI or pick another provider (the hosted API is unaffected; this bond's code is Apache-2.0).

  • d1-omni-600M is an early research release (Decision Index 15.95 vs d1-3B's 48.57) and its audio input is not served over HTTP — this bond sends text and images only.

  • Your data leaves your servers when hosted (Liquid processes the state). For data that must stay in your environment, run llama-server -hf LiquidAI/d1-3B-GGUF:Q8_0 and set LIQUID_DECISIONS_URL — a llama-server has NO authentication, so keep it on a private network or behind an authenticating proxy (set LIQUID_API_KEY/apiKey and it is sent as the bearer token).

  • 429 (rate limit) and 5xx-busy are retried up to 3 times with backoff — an aborting signal cuts the backoff short; other errors throw immediately with the API's message and a status property.

  • Every request carries a deadline (30 s by default, timeoutMs config) so a hung connection fails instead of leaving decide() pending forever; the caller's signal and the deadline both apply, whichever fires first.

  • Use the core's setProvider, not bond('ai-decisions', …) directly.

E2E Tests

Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual screens/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip:

  • [ ] Each flow that makes a decision (routing, triage, moderation, a guardrail) runs it from the real UI, and the answer DRIVES what happens next (the item lands in the chosen queue, the badge shows, the action is blocked) — not just printed.
  • [ ] Both directions: a clearly-billing input routes to billing AND a clearly-technical one routes elsewhere. One label for every input is a broken integration.
  • [ ] A low-confidence answer takes the app's fallback path (human review, "unsure" state) instead of being acted on.
  • [ ] Provider errors (service down, bad key) show a visible, recoverable state — never a blank screen or an unhandled rejection.
  • [ ] The call runs server-side: no provider request or key in the browser's Network tab.