Developer docs · Browse & search
EMBED

React hooks and components

@jwtson/react: start runs, follow them, chat, and answer questions and approvals in your own UI.

React hooks and components for a signed-in user's page, on top of @jwtson/sdk. Your server makes a client session and the page uses what it gives. The API key never reaches the browser.

tsx
import { connectClient } from '@jwtson/sdk';
import { JwtsonProvider, PendingRequests, useChat, useRun, useStart } from '@jwtson/react';

const client = connectClient(await fetch('/session', { method: 'POST' }).then(reply => reply.json()));

function Summarize() {
  const { start, run, starting, error } = useStart('summarize');
  const live = useRun(run);
  return <>
    <button disabled={starting} onClick={() => start({ document: 'd1' })}>Summarize</button>
    {error && <p role="alert">{error.message}</p>}
    <p>{live.text}</p>
    <PendingRequests items={live.pending} />
  </>;
}

Put <JwtsonProvider client={client}> around your app. apps/client-example is a complete app with its server.

Hooks

HookWhat it gives
useStart(target, { group })start(input), run, starting, error, reset(). Pressing twice starts it once, and retrying after a failure reaches the same run if the first attempt got through
useRun(run or id)status, state, phase, text, events, pending, result, error, finished, canCancel, gap, plus cancel() and say(text). Unmounting stops watching, not the run
useChat(target, { group, id })messages, send(text or { input }), current (useRun of the turn in progress), sending, error, id. Pass id to carry on an earlier chat
useRuns({ group, limit }), useChats({ group, limit })The newest 50 unless you say otherwise: items (null while loading), error, refresh()

Components

Unstyled, with real form controls and labels. Pass className and use your own CSS. What a run supplies is shown as text, never as HTML.

ComponentWhat it does
PendingRequestsEverything in items, each in the right form below
InputRequestA question. A flat schema of text, number, yes/no and fixed-choice fields becomes a form. Anything else asks for JSON. The server checks the answer against the schema either way
ApprovalRequestWhat's proposed, with Approve, Reject, a comment and, where the workflow allows it, Request changes. If it changed since it was read, the decision is refused and this says so. Someone who can't decide sees no buttons
RunProgressWhere the run really stands, in a live region that screen readers announce. It never estimates a percentage. labels replaces the wording. A failure is an alert, and a stop someone asked for isn't
ResultFilesA Save button for each file the run delivered, with its size. The secret stays in a header, so files are fetched rather than linked to

A session only gets approvals its role can read, so someone without the reviewer role has nothing in pending for one. RunProgress shows them "Waiting for approval". A question shows its form only to someone who can answer it. A viewer or reviewer sees the question without one. useClientSession() gives the page its role, so you can leave out Start and Send for those roles too, as the example does. The server refuses them anyway.

When things go wrong

  • The connection drops: following picks up by itself. Text so far is only a preview, so it starts over, and gap says some was missed. The finished result replaces it.
  • The session ends: pass getSecret to connectClient. The request is retried once with what your server gives, and nothing is started again.
  • The person is signed out or loses access: error is set, following stops, pending empties, and the last known state stays on screen. The run keeps going, and your server can still reach it.
  • Leaving a page, or remounting: only watching stops.

Pages can save delivered files, and can't list or read the run's working folder.

Without React

run.watch() in @jwtson/sdk is what useRun is built on: subscribe, getSnapshot and stop, for any framework.

From the @jwtson/react README, as the code stands.