@mccaskie-labs/workbench-ai 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.md +3 -2
- package/dist/ai.js +7 -2
- package/dist/fake.d.ts +2 -0
- package/dist/fake.js +1 -0
- package/dist/images.js +2 -0
- package/dist/types.d.ts +24 -3
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @mccaskie-labs/workbench-ai
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Breaking: there is no built-in Gateway address any more (hard rule 13, `docs/install-plan.md`). `DEFAULT_GATEWAY_URL` is removed from workbench-config, `createAi` needs `url`, and `createMailer`'s gateway transport needs `gateway.url`. Pass the app's `WORKBENCH_GATEWAY_URL`, which Control writes into `wrangler.jsonc` when it creates a project. A missing address fails at start-up with `invalid_config` and the message "WORKBENCH_GATEWAY_URL is missing". The gateway manifest marks `WORKBENCH_GATEWAY_URL` required with no default.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies
|
|
12
|
+
- @mccaskie-labs/workbench-config@0.2.0
|
|
13
|
+
|
|
14
|
+
## 0.1.5
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- Kept calls (`docs/spec.md` §2.9): a `keep` option on `text`, `structured`, `stream`, `countTokens`, `images.generate` and `images.search`. `keep: false` sends `X-Workbench-Keep: no`, so the Gateway keeps the call's facts but not its text; a refused call is kept whatever it says. `subject` is documented as how a person's kept calls are found: an app with signed-in users sends `user:<its own user id>`. `createFakeAi` records `keep` on each call.
|
|
19
|
+
|
|
3
20
|
## 0.1.4
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -10,14 +10,14 @@ npm install @mccaskie-labs/workbench-ai @mccaskie-labs/workbench-config zod
|
|
|
10
10
|
import { createAi } from '@mccaskie-labs/workbench-ai'
|
|
11
11
|
import { workbenchEnv } from '@mccaskie-labs/workbench-config'
|
|
12
12
|
|
|
13
|
-
const ai = createAi({ key: env.WORKBENCH_GATEWAY_KEY, env: workbenchEnv(env) })
|
|
13
|
+
const ai = createAi({ key: env.WORKBENCH_GATEWAY_KEY, env: workbenchEnv(env), url: env.WORKBENCH_GATEWAY_URL })
|
|
14
14
|
const r = await ai.text({ model: 'claude-default', job: 'article.write', prompt })
|
|
15
15
|
await save({ text: r.text, model: r.model, requestId: r.requestId })
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
| Export | What it does |
|
|
19
19
|
| --- | --- |
|
|
20
|
-
| `createAi({ key, env, url
|
|
20
|
+
| `createAi({ key, env, url, fetch? })` | The client. `url` is this install's Gateway address (`WORKBENCH_GATEWAY_URL`); there is no built-in one. Throws `invalid_config` at once if it is missing or the key isn't for `env` (`wbk_dev_`, `wbk_stg_`, `wbk_prd_`) |
|
|
21
21
|
| `ai.text(req)` | One reply as text. Throws `refused` on a refusal and `truncated` on `max_tokens`; never returns partial text |
|
|
22
22
|
| `ai.structured({ ...req, schema, mayLack? })` | JSON matching a zod schema, through structured outputs (`output_config.format`, not forced tool use). Asks once more for required fields left empty, except those in `mayLack`; throws `invalid_output` if they're still empty |
|
|
23
23
|
| `ai.stream(req)` | Async iterable of Anthropic stream events, then `finalMessage()`, which throws like `text` |
|
|
@@ -26,6 +26,7 @@ await save({ text: r.text, model: r.model, requestId: r.requestId })
|
|
|
26
26
|
| `ai.images.generate({ prompt, aspectRatio, job, model?, references? })` | One image: `{ bytes, mimeType, model, requestId, costUsd }`. `model` is `image-default` (the default), `image-pro` or `image-openai`; `references` work on the Google aliases only. Needs the `images` scope |
|
|
27
27
|
| `ai.images.search({ q, job, num?, gl?, hl? })` | Serper image search: a list of `{ title, imageUrl, thumbnailUrl, width, height, source, pageUrl }` that also carries `requestId`, `costUsd` and `credits`. Needs the `search` scope |
|
|
28
28
|
| `voice`, `spelling` on `text`, `structured`, `stream`, `countTokens` | `voice: 'conversational'` (or `casual`, `plain`, `formal`, `strict-formal`) has the Gateway add Workbench's house writing standards and that voice ahead of your system prompt; `spelling: 'en-AU'` (or `en-PH`, `en-GB`, `en-US`) adds one line. The text is the same bytes for every app, so it is cached and shared. `voice: { instructions }` is your own voice: it goes first in your own system prompt and no Workbench writing rule is added. A text result carries `voice: { name, spelling, version }` when one was named |
|
|
29
|
+
| `subject`, `keep` on every call | The Gateway keeps what each call sent and got back, for audit (`docs/spec.md` §2.9). `subject: 'user:<id>'` (your own user id, never a name or email address) is how a person's calls are found and deleted. `keep: false` keeps the call's facts but not its text; a refused call is kept whatever it says |
|
|
29
30
|
| `checkWriting(text, { voice?, paragraphTarget?, exempt? })` | Checks text against the house standards with no AI call. Stops: long dashes and the banned phrases. Paragraph length is a target: no issue up to 1.25 times it, a note up to double, a stop only past double. Sentence length is never an issue. `exempt` is required wording to leave out |
|
|
30
31
|
| `writingStops(issues)`, `writingNotes(issues)`, `writingFixNote(issues)` | The stops, the notes, and the text to add to a prompt when asking once more after a stop |
|
|
31
32
|
| `ai.anthropic` | The official `@anthropic-ai/sdk` client pointed at the Gateway, for anything else |
|
package/dist/ai.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import Anthropic from "@anthropic-ai/sdk";
|
|
2
2
|
import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";
|
|
3
|
-
import { DEFAULT_GATEWAY_URL } from "@mccaskie-labs/workbench-config";
|
|
4
3
|
import { createCapture, isGatewayMeta, REQUEST_ID_HEADER } from "./capture.js";
|
|
5
4
|
import { errorFromGateway, WorkbenchAiError } from "./errors.js";
|
|
6
5
|
import { gatewayFetch } from "./http.js";
|
|
@@ -34,7 +33,11 @@ export function createAi(opts) {
|
|
|
34
33
|
if (!opts.key.startsWith(prefix)) {
|
|
35
34
|
throw new WorkbenchAiError("invalid_config", `WORKBENCH_GATEWAY_KEY is not a ${env} key (expected the ${prefix} prefix)`);
|
|
36
35
|
}
|
|
37
|
-
|
|
36
|
+
// No built-in address: each Workbench install has its own Gateway (hard rule 13).
|
|
37
|
+
if (typeof opts.url !== "string" || opts.url === "") {
|
|
38
|
+
throw new WorkbenchAiError("invalid_config", "WORKBENCH_GATEWAY_URL is missing");
|
|
39
|
+
}
|
|
40
|
+
const url = opts.url.replace(/\/+$/, "");
|
|
38
41
|
const baseFetch = opts.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
39
42
|
const client = (f) => new Anthropic({
|
|
40
43
|
apiKey: opts.key,
|
|
@@ -60,6 +63,8 @@ export function createAi(opts) {
|
|
|
60
63
|
throw new WorkbenchAiError("invalid_request", "subject must be type:id");
|
|
61
64
|
h["X-Workbench-Subject"] = req.subject;
|
|
62
65
|
}
|
|
66
|
+
if (req.keep === false)
|
|
67
|
+
h["X-Workbench-Keep"] = "no";
|
|
63
68
|
// Checked here too, so a wrong name fails before the network. The Gateway refuses it as well.
|
|
64
69
|
if (typeof req.voice === "string") {
|
|
65
70
|
if (!VOICE_NAMES.includes(req.voice))
|
package/dist/fake.d.ts
CHANGED
|
@@ -28,6 +28,8 @@ export interface FakeCall {
|
|
|
28
28
|
job: string | null;
|
|
29
29
|
promptVersion: string | null;
|
|
30
30
|
subject: string | null;
|
|
31
|
+
/** False when the call was marked do-not-keep (X-Workbench-Keep: no, docs/spec.md §2.9). */
|
|
32
|
+
keep: boolean;
|
|
31
33
|
/** The Workbench voice and spelling named (X-Workbench-Voice, X-Workbench-Spelling). */
|
|
32
34
|
voice: string | null;
|
|
33
35
|
spelling: string | null;
|
package/dist/fake.js
CHANGED
|
@@ -90,6 +90,7 @@ export function fakeGateway(script = {}) {
|
|
|
90
90
|
job: req.headers.get("x-workbench-job"),
|
|
91
91
|
promptVersion: req.headers.get("x-workbench-prompt-version"),
|
|
92
92
|
subject: req.headers.get("x-workbench-subject"),
|
|
93
|
+
keep: req.headers.get("x-workbench-keep")?.toLowerCase() !== "no",
|
|
93
94
|
voice: req.headers.get("x-workbench-voice"),
|
|
94
95
|
spelling: req.headers.get("x-workbench-spelling"),
|
|
95
96
|
body,
|
package/dist/images.js
CHANGED
package/dist/types.d.ts
CHANGED
|
@@ -10,8 +10,8 @@ export interface AiOptions {
|
|
|
10
10
|
key: string;
|
|
11
11
|
/** From `workbenchEnv(env)`. Must match the key's environment. */
|
|
12
12
|
env: WorkbenchEnv;
|
|
13
|
-
/**
|
|
14
|
-
url
|
|
13
|
+
/** This install's Gateway address (`WORKBENCH_GATEWAY_URL`). There is no built-in one. */
|
|
14
|
+
url: string;
|
|
15
15
|
fetch?: typeof fetch;
|
|
16
16
|
}
|
|
17
17
|
export interface TextRequest {
|
|
@@ -34,8 +34,17 @@ export interface TextRequest {
|
|
|
34
34
|
voice?: VoiceName | OwnVoice;
|
|
35
35
|
/** The spelling to write in. The Gateway adds one line for it. Separate from the voice. */
|
|
36
36
|
spelling?: SpellingName;
|
|
37
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* `type:id` of the thing the call is about. An app with signed-in users sends `user:<its own user id>`
|
|
39
|
+
* (never a name, email address or phone number), so a person's kept calls can be found and deleted.
|
|
40
|
+
*/
|
|
38
41
|
subject?: string;
|
|
42
|
+
/**
|
|
43
|
+
* False marks this call do-not-keep (`X-Workbench-Keep: no`): the Gateway keeps its facts but
|
|
44
|
+
* not its text (docs/spec.md §2.9). A refused call is kept whatever this says. Left out, the
|
|
45
|
+
* project's own setting decides.
|
|
46
|
+
*/
|
|
47
|
+
keep?: boolean;
|
|
39
48
|
}
|
|
40
49
|
export interface StructuredRequest<T> extends TextRequest {
|
|
41
50
|
/** A zod schema. Its JSON Schema is sent as `output_config.format`; the reply is validated against it. */
|
|
@@ -137,6 +146,12 @@ export interface ImageGenerateRequest {
|
|
|
137
146
|
}[];
|
|
138
147
|
promptVersion?: string;
|
|
139
148
|
subject?: string;
|
|
149
|
+
/**
|
|
150
|
+
* False marks this call do-not-keep (`X-Workbench-Keep: no`): the Gateway keeps its facts but
|
|
151
|
+
* not its text (docs/spec.md §2.9). A refused call is kept whatever this says. Left out, the
|
|
152
|
+
* project's own setting decides.
|
|
153
|
+
*/
|
|
154
|
+
keep?: boolean;
|
|
140
155
|
}
|
|
141
156
|
export interface GeneratedImage {
|
|
142
157
|
bytes: Uint8Array;
|
|
@@ -157,6 +172,12 @@ export interface ImageSearchRequest {
|
|
|
157
172
|
/** Language, e.g. `en`. */
|
|
158
173
|
hl?: string;
|
|
159
174
|
subject?: string;
|
|
175
|
+
/**
|
|
176
|
+
* False marks this call do-not-keep (`X-Workbench-Keep: no`): the Gateway keeps its facts but
|
|
177
|
+
* not its text (docs/spec.md §2.9). A refused call is kept whatever this says. Left out, the
|
|
178
|
+
* project's own setting decides.
|
|
179
|
+
*/
|
|
180
|
+
keep?: boolean;
|
|
160
181
|
}
|
|
161
182
|
export interface ImageSearchResult {
|
|
162
183
|
title: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mccaskie-labs/workbench-ai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Claude through the Workbench Gateway: text, structured output, streaming and budget status, with no Anthropic key in the app. Refusals and truncations throw; createFakeAi for tests.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@anthropic-ai/sdk": "^1.5.0",
|
|
39
|
-
"@mccaskie-labs/workbench-config": "^0.
|
|
39
|
+
"@mccaskie-labs/workbench-config": "^0.2.0"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"zod": "^4.1.0"
|