{"_id":"sqwishai","_rev":"15-f2e4cf96a0e704238ebbd1ca32d6cdeb","name":"sqwishai","dist-tags":{"latest":"0.4.1"},"versions":{"0.3.1":{"name":"sqwishai","version":"0.3.1","keywords":["onehot","sqwish","sqwish labs","kettei","decisions","classification","probabilities","llm","ai","sdk"],"author":{"url":"https://docs.sqwish.ai","name":"Sqwish Labs","email":"help@sqwish.ai"},"license":"Apache-2.0","_id":"sqwishai@0.3.1","maintainers":[{"name":"sqwish","email":"federica@sqwish.ai"}],"homepage":"https://docs.sqwish.ai/guides/sdks","bugs":{"email":"help@sqwish.ai"},"dist":{"shasum":"ee0ddc9ecf02f88a2cb07b815636b33f16478cef","tarball":"https://registry.npmjs.org/sqwishai/-/sqwishai-0.3.1.tgz","fileCount":5,"integrity":"sha512-/nzS8/ZUnCzj6BDcUJOQpQLLcN6EmwT15Cd/qDv9OO+sZnAsdDaErfnu06viIRrUyOWX+GbTVmpKN0fieVFgoQ==","signatures":[{"sig":"MEUCIAYh59kHLphWVmYxQKJotj6c+FBPkEdsou72QPSs5IFBAiEApURauFDjah+RXqcKnyed9NwGf92QoFI6m8+KoaJlyvU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIC6AGbO/Wq7oUltVffkZELIMWqa3ZgQ8kD2zWWKLOZdVAiEAiYaZGqFG/pHM1omtkZg9E9OilXwYXt2ZSbc5OHry3d0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":138329},"main":"./dist/onehot.js","type":"module","types":"./dist/onehot.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/onehot.d.ts","default":"./dist/onehot.js"}},"gitHead":"ce4f57c31aa1d3d08de775762f02c0eb46314a51","scripts":{"test":"node --experimental-strip-types --test tests/*.test.ts","build":"tsc","prepack":"tsc"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"69ceb8dc-93ea-4554-a73c-b252fa0a3fae"}},"_npmVersion":"11.19.0","description":"TypeScript SDK for OneHot API by Sqwish Labs","directories":{},"_nodeVersion":"24.21.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"7.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sqwishai_0.3.1_1791732880044_0.03359676128682887","host":"s3://npm-registry-packages-npm-production"}},"0.3.2":{"name":"sqwishai","version":"0.3.2","keywords":["onehot","sqwish","sqwish labs","kettei","decisions","classification","probabilities","llm","ai","sdk"],"author":{"url":"https://docs.sqwish.ai","name":"Sqwish Labs","email":"help@sqwish.ai"},"license":"Apache-2.0","_id":"sqwishai@0.3.2","maintainers":[{"name":"sqwish","email":"federica@sqwish.ai"}],"homepage":"https://docs.sqwish.ai/guides/sdks","bugs":{"email":"help@sqwish.ai"},"dist":{"shasum":"9b4f628d90d53e0ec1d8a345f065e4d6d8f0c286","tarball":"https://registry.npmjs.org/sqwishai/-/sqwishai-0.3.2.tgz","fileCount":5,"integrity":"sha512-wR0AKS/hdk9A0sI6eW7PN88bPimhH6fY2lrVH2nij9b9mJPUYnenwt9yys0fW620+2MlN6lA8uOpg82LDs1gmg==","signatures":[{"sig":"MEUCIQDZfSHP02eXB4MpoSvJpTJmvQ9TYK3Y0qa1hpoOsQ2bfQIgMInAKuSHxj8PPLXuGdGmZHlK1O6SI8GivRdTkA4ylKU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEQCIE0T4RWcagaYG+X4GEOqo0LYPLT10lnJaNwPAi7CwOGWAiBKqyvreIjE+KO+3vV0A2cFrxW201NfJJ4aLUS3gb+pBw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":143181},"main":"./dist/onehot.js","type":"module","types":"./dist/onehot.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/onehot.d.ts","default":"./dist/onehot.js"}},"gitHead":"366c7f3a4d6e8215431bfe881816ad84ed8ceb3d","scripts":{"test":"node --experimental-strip-types --test tests/*.test.ts","build":"tsc","prepack":"tsc"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"69ceb8dc-93ea-4554-a73c-b252fa0a3fae"}},"_npmVersion":"11.19.0","description":"TypeScript SDK for OneHot API by Sqwish Labs","directories":{},"_nodeVersion":"24.21.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"7.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sqwishai_0.3.2_1791743997837_0.5738009358574756","host":"s3://npm-registry-packages-npm-production"}},"0.3.3":{"name":"sqwishai","version":"0.3.3","keywords":["onehot","sqwish","sqwish labs","kettei","decisions","classification","probabilities","llm","ai","sdk"],"author":{"url":"https://docs.sqwish.ai","name":"Sqwish Labs","email":"help@sqwish.ai"},"license":"Apache-2.0","_id":"sqwishai@0.3.3","maintainers":[{"name":"sqwish","email":"federica@sqwish.ai"}],"homepage":"https://docs.sqwish.ai/guides/sdks","bugs":{"email":"help@sqwish.ai"},"dist":{"shasum":"77900618752e5f9350c8e5a2d71a9c664bb1e552","tarball":"https://registry.npmjs.org/sqwishai/-/sqwishai-0.3.3.tgz","fileCount":5,"integrity":"sha512-mSOlr4ymo+EfORqrxo6fTSc+7QZ3VJ8IK7zZBiQdbzaYCiZbUANygVKqY3Gy6RlRZ0H3qvlI8y9om8+WNTos3w==","signatures":[{"sig":"MEUCIH6/Fm3cV3HmvBJB+UxByV/96Pzt7uqd17iOBg/cq+Q8AiEA2t+LN9mIbmR8eNUlg8b1G+G6iN/vWFXJjEQJsAfcIiM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEYCIQDM0Ljq9j+dq9Ti72TnUeprPyApSwxMUbJgDmqnWS7iQwIhANQ9c1ywrBdCggeTbqjQAdcWSmsorHJ12lowTVFua2h8","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":144697},"main":"./dist/onehot.js","type":"module","types":"./dist/onehot.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/onehot.d.ts","default":"./dist/onehot.js"}},"gitHead":"169b9a752d5bc8d53ba4f6a4574c0f8bb1cab0fa","scripts":{"test":"node --experimental-strip-types --test tests/*.test.ts","build":"tsc","prepack":"tsc"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"69ceb8dc-93ea-4554-a73c-b252fa0a3fae"}},"_npmVersion":"11.19.0","description":"TypeScript SDK for OneHot API by Sqwish Labs","directories":{},"_nodeVersion":"24.21.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"7.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sqwishai_0.3.3_1791750157760_0.3734481098404143","host":"s3://npm-registry-packages-npm-production"}},"0.3.4":{"name":"sqwishai","version":"0.3.4","keywords":["onehot","sqwish","sqwish labs","kettei","decisions","classification","probabilities","llm","ai","sdk"],"author":{"url":"https://docs.sqwish.ai","name":"Sqwish Labs","email":"help@sqwish.ai"},"license":"Apache-2.0","_id":"sqwishai@0.3.4","maintainers":[{"name":"sqwish","email":"federica@sqwish.ai"}],"homepage":"https://docs.sqwish.ai/guides/sdks","bugs":{"email":"help@sqwish.ai"},"dist":{"shasum":"e7d9208b503c13c277ff2feed2651ee3f003589e","tarball":"https://registry.npmjs.org/sqwishai/-/sqwishai-0.3.4.tgz","fileCount":5,"integrity":"sha512-zXxXqcVHPc9+6jD7p/eZNoNQOpRe73AKF4dnDhQsbYfulqrSq419BFIigArTOxVCeQ0D8i5BbiKI8T4o2BmRMQ==","signatures":[{"sig":"MEUCIQDR2oP76RrvoBMVlrLVpEL0hlrJqagk++1Qa4vvs51sWQIgChWL8P8bEJvo2gMai2AB0rLQWy+/ecEpev/MVoPUXcs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEYCIQDaNVnBtSsTUUiGPIN0V54IyZdxr4M2We4cts44NBsa4QIhAKHffj6RF+3VMgzP1NUudrbW5oPMnEUK+AwOdOQOAJZ+","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":154462},"main":"./dist/onehot.js","type":"module","types":"./dist/onehot.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/onehot.d.ts","default":"./dist/onehot.js"}},"gitHead":"5bdd06346da2e03f6a1b49074d637ca370644aa3","scripts":{"test":"node --experimental-strip-types --test tests/*.test.ts","build":"tsc","prepack":"tsc"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"69ceb8dc-93ea-4554-a73c-b252fa0a3fae"}},"_npmVersion":"11.19.0","description":"TypeScript SDK for OneHot API by Sqwish Labs","directories":{},"_nodeVersion":"24.21.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"7.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sqwishai_0.3.4_1791762354751_0.14151983228249088","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"sqwishai","version":"0.4.0","keywords":["onehot","sqwish","sqwish labs","kettei","decisions","classification","probabilities","llm","ai","sdk"],"author":{"url":"https://docs.sqwish.ai","name":"Sqwish Labs","email":"help@sqwish.ai"},"license":"Apache-2.0","_id":"sqwishai@0.4.0","maintainers":[{"name":"sqwish","email":"federica@sqwish.ai"}],"homepage":"https://docs.sqwish.ai/guides/sdks","bugs":{"email":"help@sqwish.ai"},"dist":{"shasum":"10ea42fccba76e706086e4355004f6264190c2dc","tarball":"https://registry.npmjs.org/sqwishai/-/sqwishai-0.4.0.tgz","fileCount":5,"integrity":"sha512-hdZOrXp1K0ccEoHzo+97jRZEDUHvs14h8BShp3xfvi8cD+LFXHyY6W3sYpohYrlVHyBBeKrGDMqCgmmj1IPtuQ==","signatures":[{"sig":"MEUCIQCc0GlNByIglkh1K/N0SuztGOGoi4GZba4q38lLXJJPggIgDrf4YtQ65LyupSztuh+b1c2GQl+BV63AYvkvKupRqHw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIE938C5k7fa5m/sEUVXiw1lro1WfxcNhWP8UPvvushVHAiEAmbhCAucz/kaqriH54kjC9uu1/6DZ5LJE/rsbVq3vQVk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":156249},"main":"./dist/onehot.js","type":"module","types":"./dist/onehot.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/onehot.d.ts","default":"./dist/onehot.js"}},"gitHead":"2a0a95b7f8c4c52414bd6e3e62487a008b86d77b","scripts":{"test":"node --experimental-strip-types --test tests/*.test.ts","build":"tsc","prepack":"tsc"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"69ceb8dc-93ea-4554-a73c-b252fa0a3fae"}},"_npmVersion":"11.19.0","description":"TypeScript SDK for OneHot API by Sqwish Labs","directories":{},"_nodeVersion":"24.21.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"7.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sqwishai_0.4.0_1791977433074_0.0036494824569694995","host":"s3://npm-registry-packages-npm-production"}},"0.4.1":{"_id":"sqwishai@0.4.1","bugs":{"email":"help@sqwish.ai"},"dist":{"shasum":"355219fe05bc75dc5cce788b7ac61f26c3f19a26","tarball":"https://registry.npmjs.org/sqwishai/-/sqwishai-0.4.1.tgz","fileCount":5,"integrity":"sha512-EkWH95D+YoUJBUXqzmgV/dq+mdnI4pHvK6RUGtP1y+E7qzDvGz6z4anqEyCJ5O/OpUxkoIAnFiBCKIaUqA0lHw==","signatures":[{"sig":"MEQCIHY2M7T1nmIpE7HR+EY+l9HL7Pg+Gjh8ObLe3+nTtrpdAiBVun+iosIEuBah75f0fw8NB1tomoBfX7eFYyXmQ8UQUg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDreVVSdjtao0jo1C/cNv3L9dL86PK5cIL/xT5oprluWgIgC6GkRui9nWxocWpTzNgozNYREQzVF6SHeNe7icgDJb0="}],"unpackedSize":170141},"main":"./dist/onehot.js","name":"sqwishai","type":"module","types":"./dist/onehot.d.ts","author":{"url":"https://docs.sqwish.ai","name":"Sqwish Labs","email":"help@sqwish.ai"},"engines":{"node":">=20"},"exports":{".":{"types":"./dist/onehot.d.ts","default":"./dist/onehot.js"}},"gitHead":"fdfe11c6640ce094b1cda371936057fe43d4f599","license":"Apache-2.0","scripts":{"test":"node --experimental-strip-types --test tests/*.test.ts","build":"tsc","prepack":"tsc"},"version":"0.4.1","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"69ceb8dc-93ea-4554-a73c-b252fa0a3fae"}},"homepage":"https://docs.sqwish.ai/guides/sdks","keywords":["onehot","sqwish","sqwish labs","kettei","decisions","classification","probabilities","llm","ai","sdk"],"_npmVersion":"11.19.0","description":"TypeScript SDK for OneHot API by Sqwish Labs","directories":{},"maintainers":[{"name":"sqwish","email":"federica@sqwish.ai"}],"_nodeVersion":"24.21.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"7.0.2"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sqwishai_0.4.1_1792027383788_0.3470414890317446"}}},"time":{"created":"2024-12-09T12:49:00.488Z","modified":"2026-10-15T01:23:04.140Z","1.0.0":"2024-12-09T12:49:00.646Z","0.1.0":"2026-10-08T19:04:19.540Z","0.2.0":"2026-10-09T18:32:39.248Z","0.3.0":"2026-10-11T14:01:14.289Z","0.3.1":"2026-10-11T15:34:40.152Z","0.3.2":"2026-10-11T18:39:57.924Z","0.3.3":"2026-10-11T20:22:37.853Z","0.3.4":"2026-10-11T23:45:54.830Z","0.4.0":"2026-10-14T11:30:33.164Z","0.4.1":"2026-10-15T01:23:03.872Z"},"bugs":{"email":"help@sqwish.ai"},"author":{"url":"https://docs.sqwish.ai","name":"Sqwish Labs","email":"help@sqwish.ai"},"license":"Apache-2.0","homepage":"https://docs.sqwish.ai/guides/sdks","keywords":["onehot","sqwish","sqwish labs","kettei","decisions","classification","probabilities","llm","ai","sdk"],"description":"TypeScript SDK for OneHot API by Sqwish Labs","maintainers":[{"name":"sqwish","email":"federica@sqwish.ai"}],"readme":"# OneHot TypeScript client\n\nA client for the OneHot API, built on the standard `fetch` with no dependencies. A\ndecision is one question with a fixed set of allowed outcomes, and OneHot answers it with\na probability for each outcome.\n\n## Install\n\n```sh\nnpm install sqwishai\n```\n\nThe package installs and imports as `sqwishai`: `import { OneHot } from \"sqwishai\"`.\nIt is JavaScript with its type declarations, so it needs no build step or type\nstripping of your own, and it has no dependencies. It needs Node 20 or newer.\n\n## Your first decision\n\nMake a key in the console at [console.sqwish.ai](https://console.sqwish.ai). Keys look like\n`onehot_sk_...`.\n\n```typescript\nimport { OneHot } from \"sqwishai\";\n\nconst client = new OneHot(\"https://api.sqwish.ai\", { apiKey: process.env.SQWISH_API_KEY });\nconst result = await client.decide({\n  context: \"Please refund the duplicate charge on my invoice.\",\n  decisions: [{\n    id: \"team\", question: \"Which team should handle this?\",\n    outcomes: [\"billing\", \"technical\", \"sales\"],\n  }],\n});\nconst team = result.decisions.team;\nconsole.log(team.top, team.p_top, team.probabilities);\n```\n\nThe address is the server's origin, without `/v1`. An argument you pass wins, an empty one\nincluded, which fails rather than fall back. If you leave out the address or the key, the\nclient reads `SQWISH_BASE_URL` or `SQWISH_API_KEY`, and with no address there either it uses\n`https://api.sqwish.ai`, so `new OneHot()` works once `SQWISH_API_KEY` is set. With no\nkey anywhere, a call throws before anything is sent; only `lint` and `systemoneTranslate` work\nwithout one. The address can also go with the other options:\n`new OneHot({ baseUrl, apiKey, fetch, timeout, maxRetries })`.\n\nLeaving `model` out uses the server's default base size. To pick a size, pass its id as\n`model`, such as `\"kettei-core\"`: `client.models.list()` lists every size with its status.\nA family you have trained is called by its name, such as `\"router\"`, and one of its\nmodels by its number, such as `\"router@3\"`.\n\nEach decision has an `id`, a `question` and its outcomes. `kind` is `\"single\"` (the\ndefault), `\"binary\"` (yes or no) or `\"ordinal\"` (levels `\"0\"`, `\"1\"` and so on, with one\n`rubric` line per level). `temperature`, from 0.2 to 5, reshapes every answer's\nprobabilities: below 1 sharper, above 1 softer, and the top answer never changes. Left out,\nthe model's own default applies, and `temperature: 1` gives the probabilities as the model\nscored them. Fields on the wire keep the API's snake_case (`p_top`, `dataset_id`), while method names\nare camelCase.\n\n## What else it covers\n\n| Call | What it does |\n|---|---|\n| `client.lint({ context, decisions })` | Warns about how questions are asked. No model runs, and it needs no key. |\n| `client.datasets` | `create`, `upload` (JSONL, up to 16 MiB), `get`, `list`, `iter`, `rows` (of any version), `rename`, `delete` and `restore`; `addRows` and `uploadRows` (JSONL or CSV) add a version, `versions` lists them and `download` reads one; `labelsToCheck` lists the labels prompt tuning doubted, each with its row |\n| `client.evaluations` | Score models on a dataset's evaluation rows: `create`, `get`, `list`, `iter`, `cancel`, `retry` |\n| `client.fineTuning` | `create` (starts a family), `continueFamily`, `estimate` and `estimateContinue` (their exact fee first), `get`, `wait`, `events`, `records` (the held-out answers), `cancel`, `list`, `iter` |\n| `client.promptTuning` | Better wording for one decision: `create`, `get`, `wait`, `cancel`, `list`, `iter`; `dismissLabelIssues` takes a job's doubted labels off its dataset's labels to check |\n| `client.families` | `list`, `get`, `switch`, `keepWarm`, `rollback` and `delete`; `wait` until a family serves its new model |\n| `client.models` | `list`, `get`, `ready`, `switch`, `keepWarm` and `delete`, each model by its number, such as `\"router@3\"` |\n| `client.labeling` | Teacher labels for your own queries: `teachers`, then `sources.upload` and `jobs.create`, `wait`, `results`, `finalize`; `sources.delete` removes a source and its teacher answers for good |\n| `client.account` | `get`, and `keys.list`, a page at a time, active or revoked, each with its `scope` and, for an inference key, its monthly cap. Keys are made and revoked in the console. |\n| `client.capabilities()` | Models, recipes, limits, training availability and capacity warnings |\n\nMost `list` calls return one page with `has_more` and `next_cursor`, and `iter` walks every\npage: `for await (const dataset of client.datasets.iter())`. `client.models.list()`,\n`client.families.list()` and `client.datasets.versions()` return the whole list. The lists of\njobs filter by `status`, one or several, and those of fine-tunes and prompt-tuning jobs by\n`dataset_id` too: `client.fineTuning.list({ status: [\"queued\", \"running\"], dataset_id })`.\n`wait` polls a job until it ends.\nEvery method takes a last `options` argument, `{ timeout, signal, idempotencyKey }`, typed as\n`RequestOptions`.\nThe [fine-tuning](https://docs.sqwish.ai/guides/fine-tuning) and\n[labelling](https://docs.sqwish.ai/guides/labelling) guides describe both flows step by step.\n\n`(await client.capabilities()).warnings ?? []` contains advisory messages. `engine_busy`\nnames the affected catalogue model in `model`; `queue_full` and `tenant_queue_full`\nconcern fine-tuning. Display `message` beside the relevant action. Read again to clear\nwarnings as capacity returns. These snapshots do not reserve capacity, and missing or\nstale engine measurements produce no warning, not a promise of spare capacity.\n\n## Families and models\n\nA family, such as `router`, is a line of models, each trained from the one before:\n`router@1`, `router@2` and on. A model joins only if it passes its gate. Calls to `\"router\"`\nget the model the family serves while it is switched on, and calls to `\"router@1\"` get that\nmodel while its own switch is on.\n\n```typescript\nconst quote = await client.fineTuning.estimate({ dataset_id, family: \"router\" }); // the exact fee, before anything starts\nconst job = await client.fineTuning.create({ dataset_id, family: \"router\", max_price_usd: quote.price_usd }); // from core; add from: \"router@2\" to start from a model\nawait client.fineTuning.wait(job.id); // router@1; outcome \"joined\" if it passed its gate\nawait client.datasets.uploadRows(dataset_id, csv, { format: \"csv\", family: \"router\" }); // the next version\nconst again = await client.fineTuning.estimateContinue(\"router\");\nconst next = await client.fineTuning.continueFamily(\"router\", {\n  dataset_version: again.dataset_version, max_price_usd: again.price_usd }); // its next model, on the rows quoted\nconst trained = await client.fineTuning.wait(next.id); // a family trains one model at a time\nawait client.families.wait(\"router\", trained.output_model!); // calls to \"router\" now get it\nconst family = await client.families.get(\"router\"); // served, latest, move, jobs and continue\nawait client.models.switch(\"router@1\", { on: true }); // answers calls to its number as well\nawait client.families.rollback(\"router\", { to: \"router@1\", discard: [\"router@2\"] }); // discards router@2\n```\n\nA model that joins doesn't answer calls to the family's name straight away: a family that is\nswitched on moves to it by itself once it has loaded and answered a probe. `client.families.wait`\nresolves with the family once it serves the model you name, the job's `output_model` or its\nnumber. It throws at once for a wait that can't end: a family switched off never moves, a model\nthat failed its gate never joined, and a family moves only to its latest model. Running out of\ntime throws a `TimeoutError`, saying what the move waits for. Continue a family only once its\nlast fine-tune has finished: while one trains, `continueFamily` throws `family_training`. It\ntrains on the dataset's newest version, or the `dataset_version` you name. The example names\nthe version it was quoted for, so rows added meanwhile wait for the next model. The version\nmust be newer than the one the family's last finished job trained on, or the call throws\n`no_new_data`.\n\nA switch or keep warm past your tier's limit throws `live_limit` or `warm_limit`. Its\n`details` list what answers or is kept warm, least recently used first, and `switch_off` frees\na place in the same call. `served` on a family and `adapter` on a model are the id you read:\nthe call throws `served_changed` or `model_changed` if it changed since. `RefusalCode` lists\nthe codes these calls refuse with.\n\n## Estimates and paid requests\n\n`client.estimate(input)` returns billed input tokens, the pinned billing tokenizer,\neach eligible model's `quote_usd`, and `maximum_charge_usd`, the requested model's price,\nwhich a fallback never exceeds.\n`client.systemoneEstimate({ state, questions, model })` quotes a Jev request. Estimates\nrun no model and reserve no credit; a later request uses the policy at admission.\n\n`client.fineTuning.estimate(input)` takes the input of `create`, and\n`client.fineTuning.estimateContinue(family, input)` that of `continueFamily`, without\n`max_price_usd`. Each returns the exact `price_usd` the job would hold if it started now, with\nits `trained_tokens`, `steps`, `seed`, `passes`, the `rows` it takes (new, earlier and\nevaluation) and whether `minimum_applied`. Send `price_usd` as `max_price_usd`: a job that\ncosts more by then, because a price, the dataset or the family changed, is refused with\n`price_changed` and holds nothing.\n\n`decide` and `systemone` generate one operation key per call and keep it across network\nand retryable API failures. Persist an explicit `{ idempotencyKey: \"your-operation-id\" }`\nto retry the same payload safely after a timeout or restart. Changing the payload under\nthat key returns `idempotency_conflict`; a new key starts a new billable operation.\n\nWhen a call fails, its `APIError` names the key it sent as `idempotencyKey`: yours, or the\none the client made. After an error that is `retryable`, or whose `idempotencyStatus` is\n`\"pending\"`, call again with that key and the same payload. A pending error is a\n`409 request_in_progress` with an `operationId`: the server accepted the decision, and it is\nstill running or its end isn't known yet. Only its key asks about it. A call without the key\nis a new operation, and you are charged for both if the first was answered after all.\n\n```typescript\nimport { APIError } from \"sqwishai\";\n\nconst input = {\n  context: \"Please refund the duplicate charge on my invoice.\",\n  decisions: [{\n    id: \"team\", question: \"Which team should handle this?\",\n    outcomes: [\"billing\", \"technical\", \"sales\"],\n  }],\n};\nconst result = await client.decide(input).catch(async (error: unknown) => {\n  if (!(error instanceof APIError)) throw error;\n  if (!error.retryable && error.idempotencyStatus !== \"pending\") throw error;\n  await new Promise((resolve) => setTimeout(resolve, (error.retryAfter ?? 2) * 1000));\n  // The same key asks about the same decision. Without it, this would be a second one.\n  return client.decide(input, { idempotencyKey: error.idempotencyKey });\n});\n```\n\nThe same holds for every call that takes `idempotencyKey`, such as `fineTuning.create`: a\nsecond call without the key starts a second job. A call that got no answer leaves its outcome\nunknown too: it ends with a `TimeoutError`, or with fetch's own error for a lost connection,\nand that error carries the same `idempotencyKey` property. Call again with it:\n`(error as { idempotencyKey?: string }).idempotencyKey` reads it from any of these errors. An\nabort through your own `signal` throws your reason as it was, so keep your own key for a call\nyou may abort.\n\nThe client sends a key with every `decide` and `systemone`, so it never meets\n`409 outcome_unknown`. A request sent over plain HTTP without an `Idempotency-Key` gets that\nwhen the server can't say how an accepted decision ended. It is not retryable: nothing names\nthat decision, so a request sent again would be a second one and could be a second charge.\n\nThe result's `charge` contains `amount_usd` for the model that answered, `reserved_usd`\nfor the original maximum hold, `currency`, and `operation_id`. Money is returned as exact\ndecimal strings; avoid converting it to floating-point numbers for accounting. Unused\nreserved credit is released. Replaying a result neither runs nor charges again. After\nits response expires, the retained key returns `idempotency_response_expired`.\n\nA final failure releases its hold. `APIError.idempotencyStatus === \"terminal\"` and\n`operationId` identify it, and the SDK stops retrying. The one exception is `model_warming`\nwith `waitForWarm`: that failure charged nothing, so the SDK sends the decide again under a\nnew key after each `Retry-After`, until `waitForWarm` runs out. It does this only for a key\nit made; with your own `idempotencyKey` it throws, and you start a new call deliberately.\n\n## Errors and retries\n\nEvery API error throws `APIError`. It carries `status`, a stable `code` such as\n`model_answers`, `message`, `requestId`, `details` (on a 422, each bad field with its path)\nand `retryable`, which says whether the same request can succeed later. `idempotencyKey` is\nthe key the call sent, or `undefined` for a call that sent none, such as a read:\n[send a paid request again](#estimates-and-paid-requests) with it.\n\n```typescript\nimport { APIError } from \"sqwishai\";\n\ntry {\n  await client.models.delete(\"router@2\");\n} catch (error) {\n  if (!(error instanceof APIError)) throw error;\n  console.log(error.code, error.message); // model_answers: its family serves it, or its own switch is on\n}\n```\n\nThe client retries by itself up to `maxRetries` times (default 2) when an error is\n`retryable`, or when a reply without that flag is a 429, 502, 503, 504 or 529. It waits for\n`Retry-After` when the server sends one, and a random tenth of it at most more, so clients refused\ntogether don't all come back at once, unless that would leave the call no time to try again:\nthen it raises the error at once, with `retryAfter`, as for `warm_capacity`. A create keeps one `Idempotency-Key` across those\nretries, and the error that ends them names it; pass `idempotencyKey` to keep it across\nrestarts as well. `timeout` (default 60 seconds) bounds a whole call, retries included, and ends\nit with a `TimeoutError` that names the key too.\n\nSome calls are never repeated automatically, because a lost reply leaves their outcome\nunknown: a prompt-tuning job's `cancel`, a family's `delete`, and a family's `switch` that\nnames `served`, since a second one would find the family gone or moved. Check `retryable`, or\nread the job with `promptTuning.get` or the family with `families.get`, before you send one again.\n\nA client keeps its connection open between calls for up to `keepAlive` seconds (default\n180), so a call made within that time skips the setup of a new connection, about 100 ms. Pass\n`keepAlive: null` to keep it until the server closes it; with a `fetch` of your own, set it on\nthat fetch instead. When the server has closed a kept connection just as a call goes out, a call\nthat is safe to send twice (a read, or one with an `Idempotency-Key`) goes again at once on a new\nconnection, without using a retry; any other call throws as above. This is on Node, whose fetch\non its own closes an idle connection after 4 s. A dispatcher you gave Node's fetch yourself, such\nas a proxy set with undici's `setGlobalDispatcher`, is used as it is: set `keepAliveTimeout` on it.\nBrowsers, Bun and Deno keep connections their own way.\n\n`wait()` throws `JobFailed` for a failed or cancelled job, with the whole job in\n`error.job`. A timeout or an abort stops the waiting, not the job.\n\n## Moving from Jev\n\n`client.systemone({ state, questions, model })` sends TypeSafe's Jev request shape to\n`POST /v1/systemone` and returns Jev's answer shape. It needs a `model`: a size's id from the\nconsole or `client.models.list()`. Jev's own model ids are accepted too and run on one of our\nbase sizes, and the answer's `model` says which. A field that OneHot doesn't model is\nrejected with a 422 naming it, rather than ignored.\n\n```typescript\n// A base model that is serving, not one of your fine-tunes (which carry an adapter, `lora`);\n// or use the id shown in the console.\nconst models = await client.models.list();\nconst size = models.find((m) => m.status === \"ready\" && !m.lora);\nif (!size) throw new Error(\"No size is serving right now\");\nconst answer = await client.systemone({\n  model: size.id,\n  state: \"My parcel arrived broken.\",\n  questions: { refund: { type: \"noul\", instructions: \"Is the customer asking for a refund?\" } },\n});\nconsole.log(answer.answers);\n```\n\n`client.systemoneTranslate(input)` takes the same input and returns the native request as\n`request`, ready for `client.decide`, with `notes` on what changed. The notes include how a\nJev confidence threshold becomes a `p_top` threshold. It runs no model and needs no key.\n\n## More\n\n- [api.sqwish.ai/openapi.json](https://api.sqwish.ai/openapi.json) is the source of\n  truth for every field.\n- The guides at [docs.sqwish.ai](https://docs.sqwish.ai) cover training, teacher labelling\n  and every other workflow.\n\nDataset creation and JSONL upload accept `evaluationFraction` from `0.1` to `1`, defaulting to\n`0.2`. Teacher-label finalisation accepts the same option. A value of `1` creates an\nevaluation-only dataset. The saved split cannot be edited.\n\nDataset `list` and `iter` accept `archived` to read the datasets we archived, when credit\ncouldn't cover their storage or the account was suspended; `restore` brings one back for new\nwork. `rows` accepts `partition` (`all`, `training` or `evaluation`). `rename` changes only the\nname. `delete` removes a dataset for good, with every version's upload and the copies its\nfine-tunes kept; the fine-tunes and their models stay, and work still reading it refuses.\n","readmeFilename":"README.md"}