@purposeinplay/payload-ai-translate 0.1.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.
Files changed (301) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +714 -0
  3. package/dist/alerts-collection.d.ts +21 -0
  4. package/dist/alerts-collection.js +159 -0
  5. package/dist/api.d.ts +4 -0
  6. package/dist/api.js +918 -0
  7. package/dist/bulk-translate-batches-collection.d.ts +29 -0
  8. package/dist/bulk-translate-batches-collection.js +404 -0
  9. package/dist/bulk-translate-units-collection.d.ts +35 -0
  10. package/dist/bulk-translate-units-collection.js +310 -0
  11. package/dist/client/estimated-cost-cell.d.ts +6 -0
  12. package/dist/client/estimated-cost-cell.js +12 -0
  13. package/dist/client/excluded-fields-field.d.ts +45 -0
  14. package/dist/client/excluded-fields-field.js +553 -0
  15. package/dist/client/field-translate-button.d.ts +6 -0
  16. package/dist/client/field-translate-button.js +199 -0
  17. package/dist/client/index.d.ts +6 -0
  18. package/dist/client/index.js +6 -0
  19. package/dist/client/lib/use-global-kill-switches.d.ts +20 -0
  20. package/dist/client/lib/use-global-kill-switches.js +58 -0
  21. package/dist/client/translate-button.d.ts +2 -0
  22. package/dist/client/translate-button.js +228 -0
  23. package/dist/client/translate-modal.d.ts +16 -0
  24. package/dist/client/translate-modal.js +549 -0
  25. package/dist/client/translation-progress.d.ts +10 -0
  26. package/dist/client/translation-progress.js +297 -0
  27. package/dist/components/TranslationNavGroup.d.ts +45 -0
  28. package/dist/components/TranslationNavGroup.js +104 -0
  29. package/dist/defaults.d.ts +11 -0
  30. package/dist/defaults.js +16 -0
  31. package/dist/endpoints/client-config.d.ts +44 -0
  32. package/dist/endpoints/client-config.js +145 -0
  33. package/dist/endpoints/estimate.d.ts +5 -0
  34. package/dist/endpoints/estimate.js +237 -0
  35. package/dist/endpoints/progress.d.ts +2 -0
  36. package/dist/endpoints/progress.js +314 -0
  37. package/dist/endpoints/translate.d.ts +11 -0
  38. package/dist/endpoints/translate.js +376 -0
  39. package/dist/endpoints/translation-hub/_helpers.d.ts +140 -0
  40. package/dist/endpoints/translation-hub/_helpers.js +297 -0
  41. package/dist/endpoints/translation-hub/active.d.ts +21 -0
  42. package/dist/endpoints/translation-hub/active.js +220 -0
  43. package/dist/endpoints/translation-hub/cancel.d.ts +22 -0
  44. package/dist/endpoints/translation-hub/cancel.js +233 -0
  45. package/dist/endpoints/translation-hub/enqueue.d.ts +70 -0
  46. package/dist/endpoints/translation-hub/enqueue.js +529 -0
  47. package/dist/endpoints/translation-hub/failures.d.ts +12 -0
  48. package/dist/endpoints/translation-hub/failures.js +67 -0
  49. package/dist/endpoints/translation-hub/force-reset.d.ts +20 -0
  50. package/dist/endpoints/translation-hub/force-reset.js +144 -0
  51. package/dist/endpoints/translation-hub/index.d.ts +21 -0
  52. package/dist/endpoints/translation-hub/index.js +20 -0
  53. package/dist/endpoints/translation-hub/list.d.ts +40 -0
  54. package/dist/endpoints/translation-hub/list.js +182 -0
  55. package/dist/endpoints/translation-hub/preflight.d.ts +19 -0
  56. package/dist/endpoints/translation-hub/preflight.js +141 -0
  57. package/dist/endpoints/translation-hub/retry-failed.d.ts +38 -0
  58. package/dist/endpoints/translation-hub/retry-failed.js +235 -0
  59. package/dist/endpoints/translation-hub/revert.d.ts +88 -0
  60. package/dist/endpoints/translation-hub/revert.js +405 -0
  61. package/dist/endpoints/translation-hub/status.d.ts +45 -0
  62. package/dist/endpoints/translation-hub/status.js +391 -0
  63. package/dist/endpoints/translation-hub/usage-summary.d.ts +114 -0
  64. package/dist/endpoints/translation-hub/usage-summary.js +481 -0
  65. package/dist/exports/client.d.ts +6 -0
  66. package/dist/exports/client.js +6 -0
  67. package/dist/exports/components.d.ts +6 -0
  68. package/dist/exports/components.js +5 -0
  69. package/dist/exports/index.d.ts +8 -0
  70. package/dist/exports/index.js +7 -0
  71. package/dist/exports/providers.d.ts +9 -0
  72. package/dist/exports/providers.js +5 -0
  73. package/dist/exports/views-client.d.ts +23 -0
  74. package/dist/exports/views-client.js +22 -0
  75. package/dist/exports/views.d.ts +30 -0
  76. package/dist/exports/views.js +29 -0
  77. package/dist/hooks/after-change-global.d.ts +4 -0
  78. package/dist/hooks/after-change-global.js +109 -0
  79. package/dist/hooks/after-change.d.ts +16 -0
  80. package/dist/hooks/after-change.js +205 -0
  81. package/dist/hooks/after-delete.d.ts +30 -0
  82. package/dist/hooks/after-delete.js +95 -0
  83. package/dist/index.d.ts +5 -0
  84. package/dist/index.js +5 -0
  85. package/dist/jobs-collection.d.ts +17 -0
  86. package/dist/jobs-collection.js +139 -0
  87. package/dist/lexical/classifier.d.ts +3 -0
  88. package/dist/lexical/classifier.js +108 -0
  89. package/dist/lexical/deserializer.d.ts +4 -0
  90. package/dist/lexical/deserializer.js +263 -0
  91. package/dist/lexical/placeholder-integrity.d.ts +6 -0
  92. package/dist/lexical/placeholder-integrity.js +21 -0
  93. package/dist/lexical/placeholders.d.ts +21 -0
  94. package/dist/lexical/placeholders.js +117 -0
  95. package/dist/lexical/serializer.d.ts +21 -0
  96. package/dist/lexical/serializer.js +233 -0
  97. package/dist/lexical/types.d.ts +32 -0
  98. package/dist/lexical/types.js +1 -0
  99. package/dist/lib/auth-diagnostics.d.ts +14 -0
  100. package/dist/lib/auth-diagnostics.js +19 -0
  101. package/dist/lib/batch-counts.d.ts +58 -0
  102. package/dist/lib/batch-counts.js +105 -0
  103. package/dist/lib/bulk-translate-migrations.d.ts +92 -0
  104. package/dist/lib/bulk-translate-migrations.js +153 -0
  105. package/dist/lib/coalescing-queue.d.ts +38 -0
  106. package/dist/lib/coalescing-queue.js +69 -0
  107. package/dist/lib/content-extractor.d.ts +16 -0
  108. package/dist/lib/content-extractor.js +410 -0
  109. package/dist/lib/content-hash.d.ts +1 -0
  110. package/dist/lib/content-hash.js +19 -0
  111. package/dist/lib/content-patcher.d.ts +15 -0
  112. package/dist/lib/content-patcher.js +293 -0
  113. package/dist/lib/cost-guards.d.ts +2 -0
  114. package/dist/lib/cost-guards.js +18 -0
  115. package/dist/lib/daily-spend-cap.d.ts +58 -0
  116. package/dist/lib/daily-spend-cap.js +233 -0
  117. package/dist/lib/effective-locales.d.ts +181 -0
  118. package/dist/lib/effective-locales.js +302 -0
  119. package/dist/lib/error-messages.d.ts +245 -0
  120. package/dist/lib/error-messages.js +626 -0
  121. package/dist/lib/events.d.ts +39 -0
  122. package/dist/lib/events.js +146 -0
  123. package/dist/lib/exclude-fields.d.ts +3 -0
  124. package/dist/lib/exclude-fields.js +64 -0
  125. package/dist/lib/field-breadcrumb.d.ts +31 -0
  126. package/dist/lib/field-breadcrumb.js +227 -0
  127. package/dist/lib/field-diff.d.ts +1 -0
  128. package/dist/lib/field-diff.js +25 -0
  129. package/dist/lib/field-empty.d.ts +2 -0
  130. package/dist/lib/field-empty.js +68 -0
  131. package/dist/lib/field-resolver.d.ts +3 -0
  132. package/dist/lib/field-resolver.js +164 -0
  133. package/dist/lib/group-soft-skips.d.ts +39 -0
  134. package/dist/lib/group-soft-skips.js +45 -0
  135. package/dist/lib/locale-merge.d.ts +44 -0
  136. package/dist/lib/locale-merge.js +357 -0
  137. package/dist/lib/locale-row-check.d.ts +30 -0
  138. package/dist/lib/locale-row-check.js +64 -0
  139. package/dist/lib/logger.d.ts +74 -0
  140. package/dist/lib/logger.js +97 -0
  141. package/dist/lib/manual-edit-guard.d.ts +128 -0
  142. package/dist/lib/manual-edit-guard.js +393 -0
  143. package/dist/lib/output-validation.d.ts +48 -0
  144. package/dist/lib/output-validation.js +148 -0
  145. package/dist/lib/payload-read.d.ts +16 -0
  146. package/dist/lib/payload-read.js +51 -0
  147. package/dist/lib/per-doc-claim.d.ts +90 -0
  148. package/dist/lib/per-doc-claim.js +140 -0
  149. package/dist/lib/per-doc-lock.d.ts +94 -0
  150. package/dist/lib/per-doc-lock.js +119 -0
  151. package/dist/lib/persist-usage.d.ts +91 -0
  152. package/dist/lib/persist-usage.js +116 -0
  153. package/dist/lib/progress-store.d.ts +103 -0
  154. package/dist/lib/progress-store.js +314 -0
  155. package/dist/lib/rate-limiter.d.ts +3 -0
  156. package/dist/lib/rate-limiter.js +53 -0
  157. package/dist/lib/snapshot-select.d.ts +43 -0
  158. package/dist/lib/snapshot-select.js +108 -0
  159. package/dist/lib/translate-prompt.d.ts +31 -0
  160. package/dist/lib/translate-prompt.js +66 -0
  161. package/dist/lib/translation-token-bucket.d.ts +57 -0
  162. package/dist/lib/translation-token-bucket.js +365 -0
  163. package/dist/lib/truncate-source-value.d.ts +1 -0
  164. package/dist/lib/truncate-source-value.js +27 -0
  165. package/dist/manual-edit-collection.d.ts +22 -0
  166. package/dist/manual-edit-collection.js +124 -0
  167. package/dist/plugin.d.ts +3 -0
  168. package/dist/plugin.js +934 -0
  169. package/dist/providers/ai-sdk-adapter.d.ts +35 -0
  170. package/dist/providers/ai-sdk-adapter.js +100 -0
  171. package/dist/providers/anthropic.d.ts +31 -0
  172. package/dist/providers/anthropic.js +66 -0
  173. package/dist/providers/custom.d.ts +36 -0
  174. package/dist/providers/custom.js +24 -0
  175. package/dist/providers/gemini.d.ts +20 -0
  176. package/dist/providers/gemini.js +48 -0
  177. package/dist/providers/mock.d.ts +2 -0
  178. package/dist/providers/mock.js +29 -0
  179. package/dist/providers/openai.d.ts +28 -0
  180. package/dist/providers/openai.js +69 -0
  181. package/dist/settings-global.d.ts +74 -0
  182. package/dist/settings-global.js +216 -0
  183. package/dist/tasks/bulk-translate-coordinator.d.ts +115 -0
  184. package/dist/tasks/bulk-translate-coordinator.js +708 -0
  185. package/dist/tasks/bulk-translate-doc-task.d.ts +142 -0
  186. package/dist/tasks/bulk-translate-doc-task.js +1000 -0
  187. package/dist/tasks/bulk-translate-janitor.d.ts +87 -0
  188. package/dist/tasks/bulk-translate-janitor.js +311 -0
  189. package/dist/tasks/translate-job-task.d.ts +51 -0
  190. package/dist/tasks/translate-job-task.js +154 -0
  191. package/dist/translate.d.ts +113 -0
  192. package/dist/translate.js +911 -0
  193. package/dist/translation-daily-spend-collection.d.ts +24 -0
  194. package/dist/translation-daily-spend-collection.js +133 -0
  195. package/dist/translation-rate-limits-collection.d.ts +30 -0
  196. package/dist/translation-rate-limits-collection.js +144 -0
  197. package/dist/types.d.ts +672 -0
  198. package/dist/types.js +1 -0
  199. package/dist/usage-collection.d.ts +14 -0
  200. package/dist/usage-collection.js +377 -0
  201. package/dist/views/BulkRunsHub/BatchRow.d.ts +32 -0
  202. package/dist/views/BulkRunsHub/BatchRow.js +1222 -0
  203. package/dist/views/BulkRunsHub/BucketRow.d.ts +62 -0
  204. package/dist/views/BulkRunsHub/BucketRow.js +982 -0
  205. package/dist/views/BulkRunsHub/BulkRunsHub.client.d.ts +18 -0
  206. package/dist/views/BulkRunsHub/BulkRunsHub.client.js +331 -0
  207. package/dist/views/BulkRunsHub/EmptyState.d.ts +6 -0
  208. package/dist/views/BulkRunsHub/EmptyState.js +64 -0
  209. package/dist/views/BulkRunsHub/FilterBar.d.ts +16 -0
  210. package/dist/views/BulkRunsHub/FilterBar.js +284 -0
  211. package/dist/views/BulkRunsHub/InFlightBanner.d.ts +14 -0
  212. package/dist/views/BulkRunsHub/InFlightBanner.js +59 -0
  213. package/dist/views/BulkRunsHub/StatusBadge.d.ts +64 -0
  214. package/dist/views/BulkRunsHub/StatusBadge.js +248 -0
  215. package/dist/views/BulkRunsHub/SummaryStrip.d.ts +22 -0
  216. package/dist/views/BulkRunsHub/SummaryStrip.js +249 -0
  217. package/dist/views/BulkRunsHub/bucket-grouping.d.ts +200 -0
  218. package/dist/views/BulkRunsHub/bucket-grouping.js +344 -0
  219. package/dist/views/BulkRunsHub/bucketFailureSummary.d.ts +9 -0
  220. package/dist/views/BulkRunsHub/bucketFailureSummary.js +36 -0
  221. package/dist/views/BulkRunsHub/dedupedStatusFetch.d.ts +5 -0
  222. package/dist/views/BulkRunsHub/dedupedStatusFetch.js +45 -0
  223. package/dist/views/BulkRunsHub/index.d.ts +17 -0
  224. package/dist/views/BulkRunsHub/index.js +80 -0
  225. package/dist/views/BulkRunsHub/urlFilters.d.ts +14 -0
  226. package/dist/views/BulkRunsHub/urlFilters.js +50 -0
  227. package/dist/views/BulkRunsHub/useBulkRunsList.d.ts +26 -0
  228. package/dist/views/BulkRunsHub/useBulkRunsList.js +204 -0
  229. package/dist/views/BulkRunsHub/useUrlFilters.d.ts +10 -0
  230. package/dist/views/BulkRunsHub/useUrlFilters.js +88 -0
  231. package/dist/views/TranslationHub/ActiveJobs.d.ts +6 -0
  232. package/dist/views/TranslationHub/ActiveJobs.js +320 -0
  233. package/dist/views/TranslationHub/AdvancedPanel.d.ts +17 -0
  234. package/dist/views/TranslationHub/AdvancedPanel.js +996 -0
  235. package/dist/views/TranslationHub/AlertBanner.d.ts +6 -0
  236. package/dist/views/TranslationHub/AlertBanner.js +568 -0
  237. package/dist/views/TranslationHub/AuditPanel.d.ts +6 -0
  238. package/dist/views/TranslationHub/AuditPanel.helpers.d.ts +44 -0
  239. package/dist/views/TranslationHub/AuditPanel.helpers.js +71 -0
  240. package/dist/views/TranslationHub/AuditPanel.js +1367 -0
  241. package/dist/views/TranslationHub/BulkTranslate.types.d.ts +242 -0
  242. package/dist/views/TranslationHub/BulkTranslate.types.js +36 -0
  243. package/dist/views/TranslationHub/BulkTranslateFailureDrawer.d.ts +19 -0
  244. package/dist/views/TranslationHub/BulkTranslateFailureDrawer.js +332 -0
  245. package/dist/views/TranslationHub/BulkTranslateMonitor.d.ts +28 -0
  246. package/dist/views/TranslationHub/BulkTranslateMonitor.js +305 -0
  247. package/dist/views/TranslationHub/BulkTranslateNarrowViewportBanner.d.ts +3 -0
  248. package/dist/views/TranslationHub/BulkTranslateNarrowViewportBanner.js +42 -0
  249. package/dist/views/TranslationHub/BulkTranslatePostEnqueueTransition.d.ts +26 -0
  250. package/dist/views/TranslationHub/BulkTranslatePostEnqueueTransition.js +95 -0
  251. package/dist/views/TranslationHub/BulkTranslatePreflightModal.d.ts +22 -0
  252. package/dist/views/TranslationHub/BulkTranslatePreflightModal.js +879 -0
  253. package/dist/views/TranslationHub/BulkTranslateTerminalCard.d.ts +29 -0
  254. package/dist/views/TranslationHub/BulkTranslateTerminalCard.js +445 -0
  255. package/dist/views/TranslationHub/BulkTranslateTrigger.d.ts +66 -0
  256. package/dist/views/TranslationHub/BulkTranslateTrigger.js +161 -0
  257. package/dist/views/TranslationHub/EditorRecentRunsPanel.d.ts +33 -0
  258. package/dist/views/TranslationHub/EditorRecentRunsPanel.js +290 -0
  259. package/dist/views/TranslationHub/Hub.client.d.ts +74 -0
  260. package/dist/views/TranslationHub/Hub.client.js +357 -0
  261. package/dist/views/TranslationHub/ModelCombobox.d.ts +14 -0
  262. package/dist/views/TranslationHub/ModelCombobox.js +415 -0
  263. package/dist/views/TranslationHub/PerCollectionConfig.d.ts +10 -0
  264. package/dist/views/TranslationHub/PerCollectionConfig.helpers.d.ts +16 -0
  265. package/dist/views/TranslationHub/PerCollectionConfig.helpers.js +19 -0
  266. package/dist/views/TranslationHub/PerCollectionConfig.js +759 -0
  267. package/dist/views/TranslationHub/SettingsRail.d.ts +11 -0
  268. package/dist/views/TranslationHub/SettingsRail.js +382 -0
  269. package/dist/views/TranslationHub/StatusStrip.d.ts +6 -0
  270. package/dist/views/TranslationHub/StatusStrip.js +451 -0
  271. package/dist/views/TranslationHub/UsageTable.d.ts +6 -0
  272. package/dist/views/TranslationHub/UsageTable.helpers.d.ts +69 -0
  273. package/dist/views/TranslationHub/UsageTable.helpers.js +49 -0
  274. package/dist/views/TranslationHub/UsageTable.js +1240 -0
  275. package/dist/views/TranslationHub/alertGrouping.d.ts +70 -0
  276. package/dist/views/TranslationHub/alertGrouping.js +99 -0
  277. package/dist/views/TranslationHub/index.d.ts +20 -0
  278. package/dist/views/TranslationHub/index.js +109 -0
  279. package/dist/views/TranslationHub/tabNavigation.d.ts +53 -0
  280. package/dist/views/TranslationHub/tabNavigation.js +74 -0
  281. package/dist/views/TranslationHub/terminalBannerVisibility.d.ts +33 -0
  282. package/dist/views/TranslationHub/terminalBannerVisibility.js +124 -0
  283. package/dist/views/TranslationHub/useBulkTranslateActive.d.ts +49 -0
  284. package/dist/views/TranslationHub/useBulkTranslateActive.js +251 -0
  285. package/dist/views/TranslationHub/useFocusTrap.d.ts +6 -0
  286. package/dist/views/TranslationHub/useFocusTrap.js +81 -0
  287. package/dist/views/TranslationHub/useTranslationHubUsageSummary.d.ts +77 -0
  288. package/dist/views/TranslationHub/useTranslationHubUsageSummary.js +267 -0
  289. package/dist/views/shared/EditorError.d.ts +97 -0
  290. package/dist/views/shared/EditorError.js +205 -0
  291. package/dist/views/shared/ModelCell.d.ts +18 -0
  292. package/dist/views/shared/ModelCell.js +31 -0
  293. package/dist/views/shared/docHref.d.ts +16 -0
  294. package/dist/views/shared/docHref.js +26 -0
  295. package/dist/views/shared/fetch-error-body.d.ts +25 -0
  296. package/dist/views/shared/fetch-error-body.js +42 -0
  297. package/dist/views/shared/filterPillStyle.d.ts +35 -0
  298. package/dist/views/shared/filterPillStyle.js +40 -0
  299. package/dist/views/shared/format.d.ts +75 -0
  300. package/dist/views/shared/format.js +131 -0
  301. package/package.json +141 -0
@@ -0,0 +1,35 @@
1
+ import type { LanguageModel } from 'ai';
2
+ import type { TranslationProvider } from '../types.js';
3
+ /**
4
+ * Pricing entry used to compute `estimatedCostUsd`. Per-token cost in USD.
5
+ * Provider-specific tables are passed in by each wrapper factory; pass
6
+ * `undefined` to opt out of cost estimation.
7
+ */
8
+ export type PricingPerToken = {
9
+ input: number;
10
+ output: number;
11
+ };
12
+ export type AISDKProviderInput = {
13
+ /** Pre-built `LanguageModel` from `@ai-sdk/<vendor>`'s factory. */
14
+ model: LanguageModel;
15
+ /** Model id for analytics + pricing lookup; mirrored on the provider object. */
16
+ modelId: string;
17
+ /** Optional. Skip to disable cost estimation. */
18
+ pricing?: PricingPerToken;
19
+ temperature?: number;
20
+ /** AI SDK's `maxOutputTokens`. Optional. */
21
+ maxOutputTokens?: number;
22
+ };
23
+ /**
24
+ * The shared core that all four built-in providers (openai, anthropic,
25
+ * gemini, custom) call into. Replaces ~1000 lines of per-provider parser
26
+ * + prompt + JSON-mode plumbing with a single `generateObject` call.
27
+ *
28
+ * Why this exists:
29
+ * - Each vendor's hand-rolled JSON parser had its own quirks (the
30
+ * Anthropic prefill bug that broke single-item requests is one of N).
31
+ * - `generateObject` enforces the response shape via tool-calling /
32
+ * structured-output, so the model literally cannot emit a malformed
33
+ * array. Schema validation happens before we see the data.
34
+ */
35
+ export declare function createAISDKProvider(input: AISDKProviderInput): TranslationProvider;
@@ -0,0 +1,100 @@
1
+ import { generateObject, NoObjectGeneratedError } from 'ai';
2
+ import { buildSystemPrompt, buildUserMessage, estimateUsage, TranslateResponseSchema } from '../lib/translate-prompt.js';
3
+ const DEFAULT_TEMPERATURE = 0.3;
4
+ /**
5
+ * The shared core that all four built-in providers (openai, anthropic,
6
+ * gemini, custom) call into. Replaces ~1000 lines of per-provider parser
7
+ * + prompt + JSON-mode plumbing with a single `generateObject` call.
8
+ *
9
+ * Why this exists:
10
+ * - Each vendor's hand-rolled JSON parser had its own quirks (the
11
+ * Anthropic prefill bug that broke single-item requests is one of N).
12
+ * - `generateObject` enforces the response shape via tool-calling /
13
+ * structured-output, so the model literally cannot emit a malformed
14
+ * array. Schema validation happens before we see the data.
15
+ */ export function createAISDKProvider(input) {
16
+ const temperature = input.temperature ?? DEFAULT_TEMPERATURE;
17
+ return {
18
+ model: input.modelId,
19
+ async translate (request) {
20
+ const start = Date.now();
21
+ const system = buildSystemPrompt(request);
22
+ const userMessage = buildUserMessage(request);
23
+ try {
24
+ const result = await generateObject({
25
+ model: input.model,
26
+ schema: TranslateResponseSchema,
27
+ system,
28
+ messages: [
29
+ {
30
+ role: 'user',
31
+ content: userMessage
32
+ }
33
+ ],
34
+ temperature,
35
+ ...input.maxOutputTokens ? {
36
+ maxOutputTokens: input.maxOutputTokens
37
+ } : {},
38
+ abortSignal: request.signal ?? undefined
39
+ });
40
+ const latencyMs = Date.now() - start;
41
+ const inputTokens = result.usage?.inputTokens ?? 0;
42
+ const outputTokens = result.usage?.outputTokens ?? 0;
43
+ return {
44
+ items: result.object.items,
45
+ usage: {
46
+ inputTokens,
47
+ outputTokens,
48
+ estimatedCostUsd: input.pricing ? inputTokens * input.pricing.input + outputTokens * input.pricing.output : undefined
49
+ },
50
+ model: input.modelId,
51
+ latencyMs
52
+ };
53
+ } catch (error) {
54
+ // `NoObjectGeneratedError` carries partial usage data when the
55
+ // model spent tokens but emitted output that failed schema
56
+ // validation. Surface that so the per-locale failure persists
57
+ // accurate cost data, and include a snippet of the raw model
58
+ // output so operators can diagnose oversize inputs vs garbage
59
+ // output. The previous error message just bubbled the AI SDK's
60
+ // generic "could not parse" string with no clue about the cause.
61
+ if (NoObjectGeneratedError.isInstance(error)) {
62
+ const usage = error.usage;
63
+ const rawText = typeof error.text === 'string' ? error.text : '';
64
+ const headSnippet = rawText ? ` (model output head: ${JSON.stringify(rawText.slice(0, 200))}${rawText.length > 200 ? '…' : ''})` : '';
65
+ // Output-budget exhaustion is the dominant cause of schema-validation
66
+ // failures on Anthropic + AI SDK `generateObject` for translation
67
+ // batches: the model produces JSON until it hits `maxOutputTokens`,
68
+ // gets cut off mid-string, and the partial response fails Zod parse.
69
+ // Detect this by comparing observed outputTokens to the configured
70
+ // cap — outputs sitting at (or within ~5% of) the cap are truncation,
71
+ // not malformed output. Naming `maxTokens` and the concrete observed
72
+ // value collapses minutes of debugging down to "raise the knob".
73
+ const outputTokens = usage?.outputTokens ?? 0;
74
+ const cap = input.maxOutputTokens;
75
+ const lookedTruncated = !!cap && outputTokens > 0 && outputTokens >= cap * 0.95;
76
+ let hint = '';
77
+ if (lookedTruncated) {
78
+ hint = ` — output truncated at ${outputTokens} tokens` + (cap ? ` (maxOutputTokens=${cap})` : '') + `. Raise \`maxTokens\` on the provider config, or lower \`perCallCharLimit\` so each batch fits within the model's output budget.`;
79
+ } else {
80
+ const totalChars = request.items.reduce((sum, item)=>sum + item.text.length, 0);
81
+ if (totalChars > 10_000) {
82
+ hint = ` — input was ${totalChars} chars across ${request.items.length} items; if the model output got truncated, raise \`maxTokens\` or lower \`perCallCharLimit\`.`;
83
+ }
84
+ }
85
+ throw Object.assign(new Error(`[ai-translate] Schema validation failed for ${input.modelId}: model returned non-JSON output${headSnippet}${hint}`), {
86
+ cause: error,
87
+ usage: {
88
+ inputTokens: usage?.inputTokens ?? 0,
89
+ outputTokens
90
+ }
91
+ });
92
+ }
93
+ throw error;
94
+ }
95
+ },
96
+ async estimate (request) {
97
+ return estimateUsage(request, input.pricing);
98
+ }
99
+ };
100
+ }
@@ -0,0 +1,31 @@
1
+ import type { TranslationProvider } from '../types.js';
2
+ import { type PricingPerToken } from './ai-sdk-adapter.js';
3
+ export type AnthropicProviderOptions = {
4
+ apiKey: string;
5
+ model?: string;
6
+ baseURL?: string;
7
+ temperature?: number;
8
+ /** Maps to the AI SDK `maxOutputTokens` setting. */
9
+ maxTokens?: number;
10
+ };
11
+ /**
12
+ * Per-token pricing in USD. Updated as of April 2026.
13
+ * Unknown models return undefined for cost estimates — not an error.
14
+ */
15
+ export declare const MODEL_PRICING: Record<string, PricingPerToken>;
16
+ /**
17
+ * Anthropic resolves an alias like `claude-haiku-4-5` to a dated id
18
+ * (`claude-haiku-4-5-20251001`). The pricing table is keyed by the alias,
19
+ * so strip a trailing `-YYYYMMDD` before lookup.
20
+ */
21
+ export declare function lookupPricing(model: string): PricingPerToken | undefined;
22
+ /**
23
+ * Builds an AI-SDK-backed `TranslationProvider` that talks to Anthropic.
24
+ *
25
+ * The previous implementation used a "JSON prefill" hack (`{"items":` as
26
+ * the assistant's pre-filled message) and concatenated the model's
27
+ * continuation. That broke for single-item requests on Haiku 4.5, which
28
+ * emitted an extra closing brace. `generateObject` enforces the response
29
+ * shape via tool-calling — the model literally cannot emit a stray brace.
30
+ */
31
+ export declare function createAnthropicProvider(options: AnthropicProviderOptions): TranslationProvider;
@@ -0,0 +1,66 @@
1
+ import { createAnthropic } from '@ai-sdk/anthropic';
2
+ import { createAISDKProvider } from './ai-sdk-adapter.js';
3
+ const DEFAULT_MODEL = 'claude-sonnet-4-6';
4
+ // Default output budget per translate call. History: 4096 truncated
5
+ // Haiku mid-response, 16384 was the 1.1.12 fix and held until a
6
+ // consumer using perCallCharLimit=50_000 hit it on Spanish/French
7
+ // (output expansion of 20-30% pushed legitimate outputs past 16k).
8
+ // 64_000 gives ~4x headroom over the largest output we have observed
9
+ // in production translations and sits within Claude 4.x per-request
10
+ // output ceilings via every upstream we route to. The cap is billed
11
+ // per actual output token used, so the unused headroom is free unless
12
+ // the model fills it. Consumers can still override via `maxTokens`.
13
+ const DEFAULT_MAX_TOKENS = 64_000;
14
+ /**
15
+ * Per-token pricing in USD. Updated as of April 2026.
16
+ * Unknown models return undefined for cost estimates — not an error.
17
+ */ export const MODEL_PRICING = {
18
+ 'claude-opus-4-6': {
19
+ input: 15.0 / 1_000_000,
20
+ output: 75.0 / 1_000_000
21
+ },
22
+ 'claude-sonnet-4-6': {
23
+ input: 3.0 / 1_000_000,
24
+ output: 15.0 / 1_000_000
25
+ },
26
+ 'claude-haiku-4-5': {
27
+ input: 0.8 / 1_000_000,
28
+ output: 4.0 / 1_000_000
29
+ }
30
+ };
31
+ /**
32
+ * Anthropic resolves an alias like `claude-haiku-4-5` to a dated id
33
+ * (`claude-haiku-4-5-20251001`). The pricing table is keyed by the alias,
34
+ * so strip a trailing `-YYYYMMDD` before lookup.
35
+ */ export function lookupPricing(model) {
36
+ if (MODEL_PRICING[model]) return MODEL_PRICING[model];
37
+ const stripped = model.replace(/-\d{8}$/, '');
38
+ return MODEL_PRICING[stripped];
39
+ }
40
+ // ---------------------------------------------------------------------------
41
+ // Provider factory
42
+ // ---------------------------------------------------------------------------
43
+ /**
44
+ * Builds an AI-SDK-backed `TranslationProvider` that talks to Anthropic.
45
+ *
46
+ * The previous implementation used a "JSON prefill" hack (`{"items":` as
47
+ * the assistant's pre-filled message) and concatenated the model's
48
+ * continuation. That broke for single-item requests on Haiku 4.5, which
49
+ * emitted an extra closing brace. `generateObject` enforces the response
50
+ * shape via tool-calling — the model literally cannot emit a stray brace.
51
+ */ export function createAnthropicProvider(options) {
52
+ const modelId = options.model ?? DEFAULT_MODEL;
53
+ const anthropic = createAnthropic({
54
+ apiKey: options.apiKey,
55
+ ...options.baseURL ? {
56
+ baseURL: options.baseURL
57
+ } : {}
58
+ });
59
+ return createAISDKProvider({
60
+ model: anthropic(modelId),
61
+ modelId,
62
+ pricing: lookupPricing(modelId),
63
+ temperature: options.temperature,
64
+ maxOutputTokens: options.maxTokens ?? DEFAULT_MAX_TOKENS
65
+ });
66
+ }
@@ -0,0 +1,36 @@
1
+ import type { TranslationProvider } from '../types.js';
2
+ export type CustomProviderPricing = {
3
+ /** Cost per input token in USD. */
4
+ input: number;
5
+ /** Cost per output token in USD. */
6
+ output: number;
7
+ };
8
+ export type CustomProviderOptions = {
9
+ apiKey: string;
10
+ /**
11
+ * Base URL of the OpenAI-compatible endpoint
12
+ * (Together AI, Groq, vLLM, OpenRouter, etc).
13
+ */
14
+ baseURL: string;
15
+ model: string;
16
+ /**
17
+ * Optional pricing override. The custom endpoint's vendor/model pair
18
+ * isn't known to the plugin, so consumers supply their own table when
19
+ * they want cost tracking.
20
+ */
21
+ pricing?: CustomProviderPricing;
22
+ temperature?: number;
23
+ /** Maps to the AI SDK `maxOutputTokens` setting. */
24
+ maxTokens?: number;
25
+ /**
26
+ * Friendly name for the endpoint (default: "custom"). Surfaces in
27
+ * AI SDK error messages when something goes wrong upstream.
28
+ */
29
+ name?: string;
30
+ };
31
+ /**
32
+ * Builds an AI-SDK-backed `TranslationProvider` that talks to any
33
+ * OpenAI-compatible endpoint via `@ai-sdk/openai-compatible`. Drop-in
34
+ * replacement for the previous custom-provider implementation.
35
+ */
36
+ export declare function createCustomProvider(options: CustomProviderOptions): TranslationProvider;
@@ -0,0 +1,24 @@
1
+ import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
2
+ import { createAISDKProvider } from './ai-sdk-adapter.js';
3
+ // ---------------------------------------------------------------------------
4
+ // Provider factory
5
+ // ---------------------------------------------------------------------------
6
+ /**
7
+ * Builds an AI-SDK-backed `TranslationProvider` that talks to any
8
+ * OpenAI-compatible endpoint via `@ai-sdk/openai-compatible`. Drop-in
9
+ * replacement for the previous custom-provider implementation.
10
+ */ export function createCustomProvider(options) {
11
+ const { model: modelId, pricing } = options;
12
+ const provider = createOpenAICompatible({
13
+ name: options.name ?? 'custom',
14
+ apiKey: options.apiKey,
15
+ baseURL: options.baseURL
16
+ });
17
+ return createAISDKProvider({
18
+ model: provider(modelId),
19
+ modelId,
20
+ pricing,
21
+ temperature: options.temperature,
22
+ maxOutputTokens: options.maxTokens
23
+ });
24
+ }
@@ -0,0 +1,20 @@
1
+ import type { TranslationProvider } from '../types.js';
2
+ import { type PricingPerToken } from './ai-sdk-adapter.js';
3
+ export type GeminiProviderOptions = {
4
+ apiKey: string;
5
+ model?: string;
6
+ temperature?: number;
7
+ /** Maps to the AI SDK `maxOutputTokens` setting. */
8
+ maxOutputTokens?: number;
9
+ };
10
+ /**
11
+ * Per-token pricing in USD. Updated as of April 2026.
12
+ * Unknown models return undefined for cost estimates — not an error.
13
+ */
14
+ export declare const MODEL_PRICING: Record<string, PricingPerToken>;
15
+ export declare function lookupPricing(model: string): PricingPerToken | undefined;
16
+ /**
17
+ * Builds an AI-SDK-backed `TranslationProvider` that talks to Google's
18
+ * Gemini API. Uses `@ai-sdk/google`'s `createGoogleGenerativeAI`.
19
+ */
20
+ export declare function createGeminiProvider(options: GeminiProviderOptions): TranslationProvider;
@@ -0,0 +1,48 @@
1
+ import { createGoogleGenerativeAI } from '@ai-sdk/google';
2
+ import { createAISDKProvider } from './ai-sdk-adapter.js';
3
+ const DEFAULT_MODEL = 'gemini-2.5-flash';
4
+ // Default output budget per translate call. Without this the AI SDK
5
+ // inherits Google's per-request default (which has bitten consumers
6
+ // on long-form blog posts). 64_000 sits within Gemini 2.5 Flash and
7
+ // Pro output ceilings and is billed per actual use, so headroom is
8
+ // free. Consumers can override via `maxOutputTokens`.
9
+ const DEFAULT_MAX_OUTPUT_TOKENS = 64_000;
10
+ /**
11
+ * Per-token pricing in USD. Updated as of April 2026.
12
+ * Unknown models return undefined for cost estimates — not an error.
13
+ */ export const MODEL_PRICING = {
14
+ 'gemini-2.5-pro': {
15
+ input: 1.25 / 1_000_000,
16
+ output: 10.0 / 1_000_000
17
+ },
18
+ 'gemini-2.5-flash': {
19
+ input: 0.15 / 1_000_000,
20
+ output: 0.6 / 1_000_000
21
+ },
22
+ 'gemini-2.0-flash': {
23
+ input: 0.1 / 1_000_000,
24
+ output: 0.4 / 1_000_000
25
+ }
26
+ };
27
+ export function lookupPricing(model) {
28
+ return MODEL_PRICING[model];
29
+ }
30
+ // ---------------------------------------------------------------------------
31
+ // Provider factory
32
+ // ---------------------------------------------------------------------------
33
+ /**
34
+ * Builds an AI-SDK-backed `TranslationProvider` that talks to Google's
35
+ * Gemini API. Uses `@ai-sdk/google`'s `createGoogleGenerativeAI`.
36
+ */ export function createGeminiProvider(options) {
37
+ const modelId = options.model ?? DEFAULT_MODEL;
38
+ const google = createGoogleGenerativeAI({
39
+ apiKey: options.apiKey
40
+ });
41
+ return createAISDKProvider({
42
+ model: google(modelId),
43
+ modelId,
44
+ pricing: lookupPricing(modelId),
45
+ temperature: options.temperature,
46
+ maxOutputTokens: options.maxOutputTokens ?? DEFAULT_MAX_OUTPUT_TOKENS
47
+ });
48
+ }
@@ -0,0 +1,2 @@
1
+ import type { TranslationProvider } from '../types.js';
2
+ export declare function createMockProvider(): TranslationProvider;
@@ -0,0 +1,29 @@
1
+ export function createMockProvider() {
2
+ return {
3
+ model: 'mock',
4
+ async translate (request) {
5
+ const start = Date.now();
6
+ const items = request.items.map((item)=>({
7
+ id: item.id,
8
+ text: `[${request.targetLocale}] ${item.text}`
9
+ }));
10
+ const totalChars = request.items.reduce((sum, item)=>sum + item.text.length, 0);
11
+ return {
12
+ items,
13
+ usage: {
14
+ inputTokens: totalChars,
15
+ outputTokens: totalChars
16
+ },
17
+ model: 'mock',
18
+ latencyMs: Date.now() - start
19
+ };
20
+ },
21
+ async estimate (request) {
22
+ const totalChars = request.items.reduce((sum, item)=>sum + item.text.length, 0);
23
+ return {
24
+ inputTokens: totalChars,
25
+ estimatedCostUsd: 0
26
+ };
27
+ }
28
+ };
29
+ }
@@ -0,0 +1,28 @@
1
+ import type { TranslationProvider } from '../types.js';
2
+ import { type PricingPerToken } from './ai-sdk-adapter.js';
3
+ export type OpenAIProviderOptions = {
4
+ apiKey: string;
5
+ model?: string;
6
+ baseURL?: string;
7
+ temperature?: number;
8
+ /** Maps to the AI SDK `maxOutputTokens` setting. */
9
+ maxTokens?: number;
10
+ };
11
+ /**
12
+ * Per-token pricing in USD. Updated as of April 2026.
13
+ * Unknown models return undefined for cost estimates — not an error.
14
+ */
15
+ export declare const MODEL_PRICING: Record<string, PricingPerToken>;
16
+ /**
17
+ * OpenAI returns dated model ids (`gpt-4o-mini-2024-07-18`). Strip a
18
+ * trailing `-YYYY-MM-DD` suffix before pricing lookup.
19
+ */
20
+ export declare function lookupPricing(model: string): PricingPerToken | undefined;
21
+ /**
22
+ * Builds an AI-SDK-backed `TranslationProvider` that talks to OpenAI.
23
+ * Public API is unchanged from the pre-AI-SDK implementation — consumers
24
+ * pass `{ apiKey, model }` and get back a `TranslationProvider`. Internals
25
+ * delegate to `createAISDKProvider`, which uses `generateObject` and
26
+ * eliminates manual JSON parsing.
27
+ */
28
+ export declare function createOpenAIProvider(options: OpenAIProviderOptions): TranslationProvider;
@@ -0,0 +1,69 @@
1
+ import { createOpenAI } from '@ai-sdk/openai';
2
+ import { createAISDKProvider } from './ai-sdk-adapter.js';
3
+ const DEFAULT_MODEL = 'gpt-4o';
4
+ // Default output budget per translate call. The AI SDK's
5
+ // `generateObject` has no built-in cap, so without this the upstream's
6
+ // own default applies (commonly 4096 on GPT-4o) and large batches
7
+ // truncate mid-JSON, producing the misleading "non-JSON output"
8
+ // error. 64_000 sits within GPT-4o / GPT-4.1 family output ceilings
9
+ // and is billed per actual use, so headroom is free. Consumers can
10
+ // override via `maxTokens`.
11
+ const DEFAULT_MAX_TOKENS = 64_000;
12
+ /**
13
+ * Per-token pricing in USD. Updated as of April 2026.
14
+ * Unknown models return undefined for cost estimates — not an error.
15
+ */ export const MODEL_PRICING = {
16
+ 'gpt-4o': {
17
+ input: 2.5 / 1_000_000,
18
+ output: 10.0 / 1_000_000
19
+ },
20
+ 'gpt-4o-mini': {
21
+ input: 0.15 / 1_000_000,
22
+ output: 0.6 / 1_000_000
23
+ },
24
+ 'gpt-4.1': {
25
+ input: 2.0 / 1_000_000,
26
+ output: 8.0 / 1_000_000
27
+ },
28
+ 'gpt-4.1-mini': {
29
+ input: 0.4 / 1_000_000,
30
+ output: 1.6 / 1_000_000
31
+ },
32
+ 'gpt-4.1-nano': {
33
+ input: 0.1 / 1_000_000,
34
+ output: 0.4 / 1_000_000
35
+ }
36
+ };
37
+ /**
38
+ * OpenAI returns dated model ids (`gpt-4o-mini-2024-07-18`). Strip a
39
+ * trailing `-YYYY-MM-DD` suffix before pricing lookup.
40
+ */ export function lookupPricing(model) {
41
+ if (MODEL_PRICING[model]) return MODEL_PRICING[model];
42
+ const stripped = model.replace(/-\d{4}-\d{2}-\d{2}$/, '');
43
+ return MODEL_PRICING[stripped];
44
+ }
45
+ // ---------------------------------------------------------------------------
46
+ // Provider factory
47
+ // ---------------------------------------------------------------------------
48
+ /**
49
+ * Builds an AI-SDK-backed `TranslationProvider` that talks to OpenAI.
50
+ * Public API is unchanged from the pre-AI-SDK implementation — consumers
51
+ * pass `{ apiKey, model }` and get back a `TranslationProvider`. Internals
52
+ * delegate to `createAISDKProvider`, which uses `generateObject` and
53
+ * eliminates manual JSON parsing.
54
+ */ export function createOpenAIProvider(options) {
55
+ const modelId = options.model ?? DEFAULT_MODEL;
56
+ const openai = createOpenAI({
57
+ apiKey: options.apiKey,
58
+ ...options.baseURL ? {
59
+ baseURL: options.baseURL
60
+ } : {}
61
+ });
62
+ return createAISDKProvider({
63
+ model: openai(modelId),
64
+ modelId,
65
+ pricing: lookupPricing(modelId),
66
+ temperature: options.temperature,
67
+ maxOutputTokens: options.maxTokens ?? DEFAULT_MAX_TOKENS
68
+ });
69
+ }
@@ -0,0 +1,74 @@
1
+ import type { Access, GlobalConfig } from 'payload';
2
+ export declare const DEFAULT_SETTINGS_GLOBAL_SLUG = "translation-settings";
3
+ /**
4
+ * Per-collection (or global) settings row stored under
5
+ * `translation-settings.perCollection`. Lets admins configure each
6
+ * translatable surface independently. **AUTOMATION ONLY** — none of
7
+ * these fields gate the manual Translate… dialog or per-field button.
8
+ * Editors retain full manual access to every tracked surface; this
9
+ * layer narrows what `after-change` fan-out does.
10
+ *
11
+ * - `enabled` — when `false`, the auto-translate after-change hook
12
+ * skips this surface entirely. Manual Translate still works.
13
+ * - `autoOnPublish` — when `false`, the on-publish trigger skips this
14
+ * surface. Manual Translate still works. Effectively a synonym for
15
+ * `enabled: false` today; both surface as `skipAutoOnPublish: true`
16
+ * in `effective-locales`. Kept distinct in the field schema so
17
+ * admins can express intent (collection-disabled vs auto-disabled).
18
+ * - `targetLocalesOverride` — restrict which target locales auto-
19
+ * translate. Empty / unset = inherit from site-wide
20
+ * `enabledTargetLocales`. A locale must be in BOTH lists to fan out.
21
+ * - `excludedFieldPaths` — field paths excluded from translation for
22
+ * this surface. **Applies to BOTH manual and automation paths** —
23
+ * distinct from the automation-only `enabled` / `autoOnPublish`
24
+ * flags above. The admin decision "never translate `meta.description`
25
+ * on posts" is global; it would be surprising if Manual Translate…
26
+ * silently ignored it. Empty / unset = translate every auto-resolved
27
+ * field (back-compat with pre-D6 deployments).
28
+ * - `translateSlug` — opt-in for slug translation. Defaults to false
29
+ * because the plugin's `excludeFields` config typically lists
30
+ * `'slug'` to prevent accidental per-locale URL churn. When `true`
31
+ * AND the consumer's slug field is `localized: true` on the
32
+ * collection schema, the slug becomes translatable for this
33
+ * surface. Applies to BOTH manual and automation paths.
34
+ */
35
+ export type PerCollectionSettings = {
36
+ slug: string;
37
+ enabled?: boolean | null;
38
+ autoOnPublish?: boolean | null;
39
+ targetLocalesOverride?: string[] | null;
40
+ excludedFieldPaths?: string[] | null;
41
+ translateSlug?: boolean | null;
42
+ };
43
+ /**
44
+ * Auto-registered global that lets admins manage translation runtime
45
+ * settings without editing `payload.config.ts` and restarting the
46
+ * server. Carries two responsibilities:
47
+ *
48
+ * 1. **Active provider selection** — when the consumer configured a
49
+ * `providers` map, each entry becomes a select option here. The
50
+ * translate path resolves the active provider at request time (see
51
+ * `resolveProvider` in `api.ts`). Hidden when no `providers` map is
52
+ * configured (single-provider consumer).
53
+ *
54
+ * 2. **Per-locale auto-translate toggle** — admins can opt locales out
55
+ * of the on-publish auto-translate hook without losing the ability
56
+ * to manually translate to those locales via the Translate button.
57
+ * Empty / unset = "all enabled" (back-compat).
58
+ *
59
+ * Read/write access defaults to admin-only: these settings affect
60
+ * operational cost and translation coverage, so editors shouldn't
61
+ * silently change them. Override via `settings.access`.
62
+ */
63
+ export type TranslationSettingsConfig = {
64
+ /** Master switch. Defaults to `true` when targetLocales is configured. */
65
+ enabled?: boolean;
66
+ /** Override the auto-registered global slug. */
67
+ globalSlug?: string;
68
+ /** Override the default admin-only read/update access. */
69
+ access?: {
70
+ read?: Access;
71
+ update?: Access;
72
+ };
73
+ };
74
+ export declare function createSettingsGlobal(providerKeys: string[], targetLocales: string[], options?: TranslationSettingsConfig, trackedSurfaces?: string[]): GlobalConfig;