@braze/sanity-plugin-ai-translation 0.3.0 → 0.3.2

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/README.md CHANGED
@@ -64,7 +64,7 @@ Your Studio must already support internationalized documents.
64
64
 
65
65
  Required:
66
66
 
67
- - **Sanity Studio v4** (plugin is built for Sanity Studio v4)
67
+ - **Sanity Studio v6.12 or newer** with `@sanity/ui` v4, React 19.2+, and Node.js 22.12+ (the plugin invokes Sanity Functions through Studio's client, which needs `@sanity/client` 8.3+; Studio 6.12 is the first release that bundles it)
68
68
  - `@sanity/document-internationalization` configured
69
69
  - `sanity-plugin-internationalized-array` installed and configured (used for glossary term translations)
70
70
  - `@sanity/client` **7.1.0+** (required for Agent Actions)
@@ -159,6 +159,7 @@ export default defineConfig({
159
159
 
160
160
  - `schemaId` is usually `'_.schemas.default'` for most projects.
161
161
  - `languageFieldName` must match the field your documents use (often `language`).
162
+ - `glossaryDataset` (optional) reads glossary terms from another dataset in the same project. See [`translationGlossaryTerm`](#translationglossaryterm).
162
163
 
163
164
  ### 3) Add translation history metadata
164
165
 
@@ -247,9 +248,16 @@ aiTranslationPlugin({
247
248
  The plugin exposes a server-only entry at `@braze/sanity-plugin-ai-translation/server` for running the OpenAI call outside the browser (for example in a Sanity function). It does not load React or Studio, it is not part of the Studio bundle, and it does not read the API key from the environment, so the caller passes it in.
248
249
 
249
250
  ```typescript
250
- import { requestOpenAICompletion } from "@braze/sanity-plugin-ai-translation/server";
251
+ import {
252
+ isOpenAIModel,
253
+ OPENAI_MODELS,
254
+ requestOpenAICompletion,
255
+ } from "@braze/sanity-plugin-ai-translation/server";
251
256
 
252
257
  // payload: { model, messages, reasoningEffort?, temperature? } assembled in Studio
258
+ if (!isOpenAIModel(payload.model)) {
259
+ throw new Error(`model must be one of ${OPENAI_MODELS.join(", ")}`);
260
+ }
253
261
  const { content, tokensUsed } = await requestOpenAICompletion(payload, {
254
262
  apiKey: process.env.OPENAI_API_KEY!,
255
263
  });
@@ -261,7 +269,7 @@ The first argument is the client-supplied request and is validated at runtime. T
261
269
  - Only `role` and `content` are sent for each message. Any other fields in the payload are dropped.
262
270
  - Logs never include prompt or document text, even with plugin logging turned on. They include only the model, message roles, and content lengths.
263
271
 
264
- Serialization, prompt assembly, structure validation, and the Content Release write stay in Studio. The entry does not allowlist models. Do that in the caller before invoking it.
272
+ Serialization, prompt assembly, structure validation, and the Content Release write stay in Studio. The entry does not allowlist models on its own. Do that in the caller before invoking it, with `isOpenAIModel` (or `OPENAI_MODELS`): it is the same list the `openai.model` plugin option accepts, so Studio and the function never disagree about which models are allowed.
265
273
 
266
274
  ---
267
275
 
@@ -382,6 +390,19 @@ Conceptual example:
382
390
 
383
391
  Glossary terms are used to build locale-specific translation instructions.
384
392
 
393
+ By default, glossary terms are read from the dataset of the document being translated. To share one glossary across datasets, set `glossaryDataset` to another dataset in the same project:
394
+
395
+ ```typescript
396
+ aiTranslationPlugin({
397
+ // ...
398
+ glossaryDataset: "production",
399
+ });
400
+ ```
401
+
402
+ - Terms are read with the editor's existing Studio session, so editors need read access to that dataset.
403
+ - Only glossary terms come from `glossaryDataset`. `translationSettings` (global prompt, locale prompts, protected phrases) is still read from the document's dataset.
404
+ - Nothing is written to either dataset's glossary.
405
+
385
406
  ---
386
407
 
387
408
  ## Advanced configuration
package/dist/index.d.ts CHANGED
@@ -1,9 +1,5 @@
1
- import { ArrayDefinition } from "sanity";
2
- import { FieldDefinitionBase } from "sanity";
3
1
  import { Plugin as Plugin_2 } from "sanity";
4
2
  import type { SanityDocument } from "sanity";
5
- import { WidenInitialValue } from "sanity";
6
- import { WidenValidation } from "sanity";
7
3
 
8
4
  /**
9
5
  * AI translation plugin for Sanity Studio
@@ -269,6 +265,26 @@ export declare type InferOriginalType<T> =
269
265
  export declare type InferTranslatableType<T> =
270
266
  T extends FieldTransform<unknown, infer R> ? R : never;
271
267
 
268
+ /**
269
+ * OpenAI models the plugin supports.
270
+ *
271
+ * This is the single source of truth: Studio's `PluginConfig['openai']['model']` is derived from
272
+ * it, and the translation function allowlists against it before calling `requestOpenAICompletion`,
273
+ * so the two can never drift apart.
274
+ *
275
+ * @public
276
+ */
277
+ declare const OPENAI_MODELS: readonly [
278
+ "gpt-5.1",
279
+ "gpt-5",
280
+ "gpt-5-mini",
281
+ "gpt-5-nano",
282
+ "gpt-5-pro",
283
+ "gpt-4o",
284
+ "gpt-4-turbo",
285
+ "gpt-4",
286
+ ];
287
+
272
288
  /**
273
289
  * Event emitted when an OpenAI API call is made
274
290
  * @public
@@ -288,6 +304,13 @@ declare interface OpenAIApiCallEvent extends TelemetryEventBase {
288
304
  };
289
305
  }
290
306
 
307
+ /**
308
+ * An OpenAI model id from {@link OPENAI_MODELS}
309
+ *
310
+ * @public
311
+ */
312
+ export declare type OpenAIModel = (typeof OPENAI_MODELS)[number];
313
+
291
314
  /**
292
315
  *
293
316
  * Configuration for the Sanity AI Translation plugin.
@@ -310,22 +333,18 @@ export declare interface PluginConfig {
310
333
  * Requires `@sanity/client` 8.1 or newer as Studio's client.
311
334
  */
312
335
  openai?: {
313
- /** Blueprint stack that contains the translation function, e.g. `ST-bfgaj9tnk7`. Required for OpenAI mode. */
336
+ /** Blueprint stack that contains the translation function, e.g. `ST-xxxxxxxxxx`. Required for OpenAI mode. */
314
337
  stackId: string;
315
338
  /**
316
339
  * Name of the translation function, as declared in the blueprint.
317
340
  * @defaultValue 'ai-translation'
318
341
  */
319
342
  functionName?: string;
320
- model?:
321
- | "gpt-5.1"
322
- | "gpt-5"
323
- | "gpt-5-mini"
324
- | "gpt-5-nano"
325
- | "gpt-5-pro"
326
- | "gpt-4o"
327
- | "gpt-4-turbo"
328
- | "gpt-4";
343
+ /**
344
+ * OpenAI model id. Must be one of `OPENAI_MODELS` from the server entry.
345
+ * @defaultValue 'gpt-5'
346
+ */
347
+ model?: OpenAIModel;
329
348
  reasoningEffort?: "low" | "medium" | "high";
330
349
  enabled?: boolean;
331
350
  defaultMode?: TranslationMode;
@@ -362,6 +381,17 @@ export declare interface PluginConfig {
362
381
  * ```
363
382
  */
364
383
  unsetFields?: UnsetFieldEntry[];
384
+ /**
385
+ * Dataset (in the same project) to read `translationGlossaryTerm` documents from.
386
+ * Defaults to the dataset of the document being translated.
387
+ * Translation settings and locale prompts are always read from the document's dataset.
388
+ *
389
+ * @example
390
+ * ```typescript
391
+ * glossaryDataset: 'production'
392
+ * ```
393
+ */
394
+ glossaryDataset?: string;
365
395
  /**
366
396
  * Optional telemetry configuration for monitoring translation operations.
367
397
  * Provides callbacks for lightweight events and detailed error bundles.
@@ -684,12 +714,51 @@ declare type TranslationErrorType =
684
714
  * @public
685
715
  */
686
716
  export declare const translationHistorySchema: {
687
- type: "array";
688
717
  name: "translationHistory";
689
- } & Omit<ArrayDefinition, "preview"> &
690
- FieldDefinitionBase &
691
- WidenValidation &
692
- WidenInitialValue;
718
+ type: "array";
719
+ of: {
720
+ name: string;
721
+ type: "object";
722
+ fields: (
723
+ | {
724
+ name: "language";
725
+ title: string;
726
+ description: string;
727
+ type: "string";
728
+ }
729
+ | {
730
+ name: "sourceId";
731
+ title: string;
732
+ description: string;
733
+ type: "string";
734
+ }
735
+ | {
736
+ name: "sourceRev";
737
+ title: string;
738
+ description: string;
739
+ type: "string";
740
+ }
741
+ | {
742
+ name: "sourceLanguage";
743
+ title: string;
744
+ description: string;
745
+ type: "string";
746
+ }
747
+ | {
748
+ name: "sourceSnapshot";
749
+ title: string;
750
+ description: string;
751
+ type: "text";
752
+ }
753
+ | {
754
+ name: "targetId";
755
+ title: string;
756
+ description: string;
757
+ type: "string";
758
+ }
759
+ )[];
760
+ }[];
761
+ };
693
762
 
694
763
  /**
695
764
  * Event emitted when an individual translation job completes successfully