Commit Graph

4 Commits

Author SHA1 Message Date
david
3f2a37c157 feat(wallet-monitor): Slack alerts for low operator wallet balances
A one-shot monitor for the vector-marketplace box: checks each wallet in
wallets.json against a threshold (default 10 APEX) via Ogmios
(ReadOnlyOgmiosProvider) and posts a combined Slack message when any wallet is
below it. Re-reminds every 6h while a wallet stays low and posts a recovery
message when it climbs back above, deduped through a persisted state file.

Runs hourly under host cron via `docker compose run --rm` — no long-running
server, no ports/healthcheck. Reuses the buyer/supplier tsx Docker pattern and
the existing balance-query client; addresses come from a static, gitignored
wallets.json so the monitor never reads a private key.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 11:00:30 +02:00
david
c552c4caa3 buyer: PDF book summarizer (/book) — chunk → map-reduce over suppliers
Adds an authenticated upload flow to the buyer web app: an operator
uploads a PDF, the server extracts + paragraph-aligned-chunks it
(~2.2k tokens), then runs a hierarchical map-reduce summarization by
paying marketplace suppliers per chunk — one on-chain escrow per map
call and per reduce node. One book upload fans out into dozens of real
mainnet escrows (the intended marketplace traffic) while producing a
genuine full-book summary.

Server module (buyer/src/pdf/): PDF extraction (text-only, rejects
scanned), token-aware chunker, supplier discovery/round-robin filtered
to capable models (kimi/deepseek/gpt; excludes qwen2.5:0.5b), pre-spend
cost estimate + wallet-floor guard, and a JobStore orchestrator that
reuses submitPrompt → findSubmittedEscrowRef → runAccept per call.
v1 serializes PostEscrow under a mutex (the shared tx builder can't pin
wallet inputs, so parallel escrows would double-spend); retries failed
calls on a different supplier then continues with gaps. 6 /v1/pdf-*
routes (multipart upload, estimate, confirm-gated start, SSE progress,
result, .md download) behind the existing session auth + page/chunk/
balance caps. SPA adds a 4-step BookSummarizer wizard.

Tests: chunk/estimate/supplier-pool/extract unit tests + orchestrator
(happy/gap/failover) + routes integration. Full buyer suite green
(181 tests); SPA build + typecheck clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 11:37:49 +02:00
jelisaveta-talbot
1f1a7d5a59 buyer: response archive (SQLite + filesystem) for off-chain audit trail
The on-chain Submit tx anchors sha256(canonicalize(receipt)) so the
receipt's signature is provably tied to a specific Cardano block — but
without persisting the receipt content + the actual response bytes
nowhere can buyer or supplier produce the artefacts a dispute would
need to argue about. This commit closes that gap on the buyer side.

What's new

- buyer/src/db/schema.ts (new):
  `responses` table keyed by escrow_ref, indexes on completed_at /
  capability_id / supplier_pkh. Stores metadata + receipt JSON +
  Ed25519 signature; large blobs (request envelope, response bytes)
  live on disk per-escrow.

- buyer/src/db/archive.ts (new):
  ResponseArchive class — persistChat / persistTts / list / get /
  readRequest / readResponse. Filesystem layout under ARCHIVE_DIR:
    <txhash>_<index>/request.json   (always JSON)
    <txhash>_<index>/response.{ext} (txt/json for chat, mp3/wav/… for TTS)
  Synchronous (better-sqlite3 + fs.writeFileSync) — volume is low and
  the simplicity is worth more than async overhead.

- buyer/src/server.ts:
  - AppDeps.archive plumbed through createApp.
  - /v1/submit-prompt + /v1/submit-tts persist BEFORE returning to
    the SPA, using canonicalize() for chat (same bytes the supplier
    hashed; JSON.stringify produced different bytes → wrong sha256).
    Persist failures are logged but never abort the response — the
    on-chain receipt is still authoritative.
  - New read-only endpoints:
      GET /v1/responses?limit=…           list metadata + parsed receipts
      GET /v1/responses/:ref               single-row detail
      GET /v1/responses/:ref/request       request artefact (application/json)
      GET /v1/responses/:ref/response      response artefact (verbatim bytes)
  - URL path uses "<txhash>_<index>" (the dir-safe form) since "#"
    can't appear unencoded in URLs (fragment delimiter); server
    normalises back to canonical "<txhash>#<index>" for SQLite lookup.
  - 503 service_unavailable on /v1/responses* when boot has no
    archive (ARCHIVE_DIR unset or DB unopenable) — the rest of
    the buyer-app still works.

- buyer/src/ui/pages/TaskHistory.tsx:
  Joins indexer escrows (chain state) + /v1/responses (archived
  artefacts) by escrow_ref. Each completed lifecycle gets an
  archive panel showing:
  - Request panel (above, chronological order): human-readable
    summary — first user message for chat, `text + voice + format +
    speed` for TTS, raw JSON fallback otherwise.
  - Response panel: assistant text inline for chat, <audio controls>
    for TTS; Download response / Download request anchors.
  - "Verify hash" button — fetches the response bytes, computes
    sha256 via crypto.subtle, compares to receipt.response_hash;
    green ✓ on match, red ✗ with truncated computed/expected on
    mismatch.
  - Collapsible "Receipt + signature" with the full canonical
    receipt JSON + the supplier's Ed25519 hex sig.

- buyer/src/config.ts: + ARCHIVE_DIR env (default "./data/archive").

- buyer/Dockerfile: builder gains python3/make/g++ + the
  better-sqlite3 native-binding fetch step (mirrors indexer/Dockerfile);
  runtime image stays toolchain-free.

- deploy/testnet/docker-compose.buyer.yml: bind-mounts
  ../../data/buyer/archive (repo-root-relative, not file-relative)
  into /data/archive. ARCHIVE_DIR env points at that path.

Tests

- tests/unit/buyer-archive.test.ts (new, 14 tests): persistChat /
  persistTts unit coverage (filesystem layout, INSERT-OR-REPLACE
  semantics, content-type → extension mapping for mp3/wav/opus/aac/
  flac, list ordering + limit, single-row get/read miss-vs-hit) +
  HTTP integration (503 when disabled, happy paths returning
  metadata / request / response artefact bytes with correct
  content-types, 404 on unknown ref).
- tests/unit/buyer-ui-smoke.test.tsx: TaskHistory fetch mock now
  also handles /v1/responses (returns empty list — same code path as
  "archive disabled" production case).

45 buyer tests passing; chat + TTS lifecycle verified end-to-end on
Vector testnet; "Verify hash" button confirms sha256(response bytes)
matches receipt.response_hash for both capabilities.
2026-05-11 08:49:07 +02:00
jelisaveta-talbot
cbab81efbd M1-F: end-to-end LLM marketplace lifecycle on Vector testnet
Bonded-escrow marketplace for prompt → response between buyers and
local-LLM suppliers, settling on Vector (UTxO) via Aiken Plutus V3
validators. Lifecycle PostEscrow → Claim → Submit → Accept proven on
Vector testnet 2026-05-06; AcceptEscrow tx 125e3cfe…7dc100d distributes
payment + bonds per the validator's distribution rule.

Workspace layout (pnpm + TypeScript):
  contracts/marketplace/      Aiken validators (advert + escrow) + plutus.json
  packages/shared/            Tx-builders (lucid-evolution), CBOR codecs,
                              chain providers (mock / read-only / live Ogmios)
  supplier/                   OpenAI-compatible HTTP node (Ollama by default;
                              vLLM / llama.cpp / OpenRouter / OpenClaw all
                              swap-in via OLLAMA_URL); 202+poll chat handler
  buyer/                      Express + React SPA; server-side runs the full
                              chain lifecycle via /v1/submit-prompt and
                              /v1/accept; SPA never holds the buyer key
  indexer/                    Chain-follower (extends apex-dashboard pattern),
                              SSE event stream + REST queries
  indexer-ui/                 Operator dashboard (events + suppliers + escrow
                              lookup)
  tests/                      1098 vitest unit + integration tests (10 skipped
                              under M1-F-D-defer for UPLC test plumbing)
  deploy/testnet/             Per-service docker-compose for the Hetzner stack
  docs/EXECUTIVE_SUMMARY.*    Marp deck (PDF + PPTX) — 11 slides, mermaid
                              diagrams pre-rendered for portable export
  docs/CLOUDFLARE_WAF_BYPASS.md  CF Skip-rule runbook for /v1/* paths
  docs/ARCHITECTURE.md        canonical spec

Live demo (testnet, 2026-05-06):
  https://mp-buyers.vector.testnet.apexfusion.org      (buyer SPA)
  https://mp-suppliers.vector.testnet.apexfusion.org   (supplier API)
  https://mp-indexer.vector.testnet.apexfusion.org     (indexer + UI)
2026-05-06 13:27:48 +02:00