Rebuild Piggy's interface, and give the demo book a business to describe
CI / verify (push) Successful in 4m57s
CI / publish (push) Has been skipped

Piggy answered in raw markdown, threw away every tool result it streamed,
and fought the reader's scroll on every token. The three surfaces that
made it worth having — what it read, how it reasoned, what it cost — were
all on the wire and none of them reached the screen.

The transcript is now composed of five parts under components/piggy:
answers render through streamdown, the container sticks to the bottom
without pinning the reader there, tool steps say what they read and link
to the record, and each turn carries its model and token count. Three
lifecycle bugs went with them: Stop left a permanent spinner, a truncated
stream was indistinguishable from thinking, and a failed send destroyed
the message it failed to send.

Underneath, the inference path grew timeouts, jittered retries on 429 and
5xx, tolerance of the malformed frames a 30B model emits, and an
agent_runs row per turn so chat spend is observable. The system prompt now
states that a field ending in Cents is cents — without it nemotron renders
costPerGpuHourCents: 189 as "$189 per GPU-hour", which is a 100x error on
the most scrutinised number in the room.

The demo book was arithmetically incoherent: every deal's value
contradicted its own allocation revenue by up to 3.6x, nothing had ever
closed, no customer had any paper, and the marketplace was empty. Deal
value is now derived from the allocation, the book clears 5.3% across five
blocks with one deliberately underwater, and the renewal, compliance and
agent-provenance machinery finally has rows to act on. A --clear that
deleted every obligation, SLA term and capacity request in the database
regardless of origin is scoped to the demo's own ids.

Around that: accounts have a detail page, ⌘K searches the book, Settings
can mint the API keys it always claimed to, and deploy.sh actually ships
the agent instead of silently skipping its compose profile.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
claude
2026-08-14 00:33:41 -07:00
parent 76e3caa1cb
commit 99d165b5e5
81 changed files with 21780 additions and 2250 deletions
+84 -5
View File
@@ -7,6 +7,16 @@
# PIG owns this database exclusively. Do not point it at a database shared with
# another application.
DATABASE_URL=postgres://pig:CHANGEME@localhost:5432/pig
#
# Compose only, and REQUIRED there: docker-compose.yml interpolates it with
# `${POSTGRES_PASSWORD:?…}`, so every compose command — including
# `docker compose config` — fails outright until it is set. It is also half of
# the DATABASE_URL compose builds for the containers, which is why running from
# source needs the line above and running in containers needs this one.
# Generate a fresh one; never reuse another service's.
POSTGRES_PASSWORD=CHANGEME
POSTGRES_USER=pig
POSTGRES_DB=pig
# --- Auth (Supabase) --------------------------------------------------------
# PIG uses Supabase for authentication ONLY. It stores no passwords and issues
@@ -90,6 +100,17 @@ PIG_ADMIN_EMAILS=
# Invite code gating self-serve profile creation. Rotate freely.
PIG_INVITE_CODE=
# Encrypts the credentials an admin types into the settings UI — the Notion and
# Google OAuth secrets in particular, which the API refuses to accept without
# it. Base64-encoded 32 bytes, and NOT interchangeable with any other secret
# here:
#
# openssl rand -base64 32
#
# Rotating it does not re-encrypt what is already stored; anything written
# under the old key has to be entered again.
PIG_SETTINGS_ENCRYPTION_KEY=
# --- Prime Intellect compute API -------------------------------------------
# Used to sync GPU availability into `inventory_listings`.
# Mint a key at https://app.primeintellect.ai/dashboard/tokens with the
@@ -105,19 +126,77 @@ PRIME_SYNC_INTERVAL_MINUTES=30
# Piggy drains a leased queue and serves chat on an authenticated internal
# listener. Generate one internal token and give the same value to API + Piggy.
# Never publish the Piggy listener or put this token in a URL.
PIGGY_INFERENCE_API_KEY=
#
# THREE keys turn the agent on, and all three are required together:
#
# PIGGY_ENABLED=true the API offers the chat surface
# PIGGY_INFERENCE_API_KEY the model credential, held only by Piggy
# PIGGY_INTERNAL_TOKEN 32+ characters, the same value for API and Piggy
#
# (PIGGY_INTERNAL_URL is the fourth thing the API needs, and docker-compose.yml
# already sets it to http://piggy:8931. Only a Piggy running outside Compose
# needs the line below.)
#
# Under Compose that is the whole configuration: scripts/deploy.sh reads
# PIGGY_ENABLED from this file and adds `--profile piggy` to the pull, the
# build, the `up` and the rollback, so the agent ships with the app rather than
# being started by hand and then quietly left on an old image. Everything else
# below has a working default and exists to be lowered.
#
# EVERY Piggy setting is read from this environment ONCE, at Piggy's boot. None
# of it is admin-selectable at runtime: changing the model or a budget means
# editing this file and restarting the container.
PIGGY_ENABLED=false
# Required to turn the agent on. Missing, Piggy exits at boot with
# "PIGGY_INFERENCE_API_KEY is required." and, under `restart: unless-stopped`,
# crash-loops. Mint it at https://app.primeintellect.ai — it is an INFERENCE
# credential and buys tokens, so it is not the same key as PRIME_API_KEY above.
PIGGY_INFERENCE_API_KEY=
# Required to turn the agent on. 32 characters minimum; anything shorter is
# refused at boot rather than accepted as weak.
# openssl rand -hex 32
PIGGY_INTERNAL_TOKEN=
# Where the API reaches the chat server. Under Compose this is set for you to
# http://piggy:8931; set it here only when running Piggy from source.
PIGGY_INTERNAL_URL=http://127.0.0.1:8931
# Model and host. Read by both the API (to display) and Piggy (to call).
PIGGY_MODEL=nvidia/nemotron-3-nano-30b-a3b
PIGGY_INFERENCE_BASE=https://api.pinference.ai/api/v1
PIGGY_LEASE_SECONDS=300
PIGGY_INTERNAL_URL=http://127.0.0.1:8931
PIGGY_INTERNAL_TOKEN=
PIGGY_CHAT_HOST=127.0.0.1
PIGGY_CHAT_PORT=8931
# Only containers on a private network need this; never combine it with a
# published Piggy port.
# published Piggy port. Compose sets it to true for the container, because the
# API calls Piggy across the Compose network.
PIGGY_CHAT_ALLOW_NON_LOOPBACK=false
# Tuning. COMMENTED OUT ON PURPOSE, and worth understanding before you
# uncomment one: an empty line here is not the same as an absent one. Compose
# passes `PIGGY_MAX_TOKENS=` through as the empty string, which coerces to 0 and
# fails Piggy's positive-integer check at boot. Leave a setting commented to get
# the default from apps/piggy/src/config.ts; give it a value or nothing at all.
#
# PIGGY_LEASE_SECONDS=300 # queue lease, renewed at half the interval
# PIGGY_POLL_INTERVAL_MS=2000 # how often an idle worker looks for a task
# PIGGY_MAX_TOKENS=1024 # per queued task
# PIGGY_CHAT_MAX_TOKENS=2048 # per interactive answer; tools return tables
# PIGGY_MAX_TURNS=4 # model calls per chat turn, tool round trips included
# PIGGY_WORKER_ID= # defaults to hostname:pid; only set it if you run two
#
# Reasoning is off. The default model thinks aloud when asked to, reasoning
# tokens bill like any other, and the chat panel is on every page — so the
# volume is set by how often people type. Raise it to make the UI's reasoning
# panel reachable while chasing a wrong figure, not in normal operation.
# PIGGY_REASONING_EFFORT=none # none | low | medium | high
#
# Model price in CENTS PER MILLION TOKENS, which is what makes the recorded cost
# of a run exact integer arithmetic. These are the published prices of the
# default model and must be changed with it: a stale price is worse than none,
# because it still looks like a measurement.
# PIGGY_PRICE_INPUT_CENTS_PER_MTOK=5
# PIGGY_PRICE_OUTPUT_CENTS_PER_MTOK=20
# --- Deployment: the release poller -----------------------------------------
# Only relevant on a host running scripts/autodeploy.sh. These belong in
# /etc/pig/autodeploy.env (read by the systemd unit), not here — they are