@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.
- package/LICENSE +21 -0
- package/README.md +714 -0
- package/dist/alerts-collection.d.ts +21 -0
- package/dist/alerts-collection.js +159 -0
- package/dist/api.d.ts +4 -0
- package/dist/api.js +918 -0
- package/dist/bulk-translate-batches-collection.d.ts +29 -0
- package/dist/bulk-translate-batches-collection.js +404 -0
- package/dist/bulk-translate-units-collection.d.ts +35 -0
- package/dist/bulk-translate-units-collection.js +310 -0
- package/dist/client/estimated-cost-cell.d.ts +6 -0
- package/dist/client/estimated-cost-cell.js +12 -0
- package/dist/client/excluded-fields-field.d.ts +45 -0
- package/dist/client/excluded-fields-field.js +553 -0
- package/dist/client/field-translate-button.d.ts +6 -0
- package/dist/client/field-translate-button.js +199 -0
- package/dist/client/index.d.ts +6 -0
- package/dist/client/index.js +6 -0
- package/dist/client/lib/use-global-kill-switches.d.ts +20 -0
- package/dist/client/lib/use-global-kill-switches.js +58 -0
- package/dist/client/translate-button.d.ts +2 -0
- package/dist/client/translate-button.js +228 -0
- package/dist/client/translate-modal.d.ts +16 -0
- package/dist/client/translate-modal.js +549 -0
- package/dist/client/translation-progress.d.ts +10 -0
- package/dist/client/translation-progress.js +297 -0
- package/dist/components/TranslationNavGroup.d.ts +45 -0
- package/dist/components/TranslationNavGroup.js +104 -0
- package/dist/defaults.d.ts +11 -0
- package/dist/defaults.js +16 -0
- package/dist/endpoints/client-config.d.ts +44 -0
- package/dist/endpoints/client-config.js +145 -0
- package/dist/endpoints/estimate.d.ts +5 -0
- package/dist/endpoints/estimate.js +237 -0
- package/dist/endpoints/progress.d.ts +2 -0
- package/dist/endpoints/progress.js +314 -0
- package/dist/endpoints/translate.d.ts +11 -0
- package/dist/endpoints/translate.js +376 -0
- package/dist/endpoints/translation-hub/_helpers.d.ts +140 -0
- package/dist/endpoints/translation-hub/_helpers.js +297 -0
- package/dist/endpoints/translation-hub/active.d.ts +21 -0
- package/dist/endpoints/translation-hub/active.js +220 -0
- package/dist/endpoints/translation-hub/cancel.d.ts +22 -0
- package/dist/endpoints/translation-hub/cancel.js +233 -0
- package/dist/endpoints/translation-hub/enqueue.d.ts +70 -0
- package/dist/endpoints/translation-hub/enqueue.js +529 -0
- package/dist/endpoints/translation-hub/failures.d.ts +12 -0
- package/dist/endpoints/translation-hub/failures.js +67 -0
- package/dist/endpoints/translation-hub/force-reset.d.ts +20 -0
- package/dist/endpoints/translation-hub/force-reset.js +144 -0
- package/dist/endpoints/translation-hub/index.d.ts +21 -0
- package/dist/endpoints/translation-hub/index.js +20 -0
- package/dist/endpoints/translation-hub/list.d.ts +40 -0
- package/dist/endpoints/translation-hub/list.js +182 -0
- package/dist/endpoints/translation-hub/preflight.d.ts +19 -0
- package/dist/endpoints/translation-hub/preflight.js +141 -0
- package/dist/endpoints/translation-hub/retry-failed.d.ts +38 -0
- package/dist/endpoints/translation-hub/retry-failed.js +235 -0
- package/dist/endpoints/translation-hub/revert.d.ts +88 -0
- package/dist/endpoints/translation-hub/revert.js +405 -0
- package/dist/endpoints/translation-hub/status.d.ts +45 -0
- package/dist/endpoints/translation-hub/status.js +391 -0
- package/dist/endpoints/translation-hub/usage-summary.d.ts +114 -0
- package/dist/endpoints/translation-hub/usage-summary.js +481 -0
- package/dist/exports/client.d.ts +6 -0
- package/dist/exports/client.js +6 -0
- package/dist/exports/components.d.ts +6 -0
- package/dist/exports/components.js +5 -0
- package/dist/exports/index.d.ts +8 -0
- package/dist/exports/index.js +7 -0
- package/dist/exports/providers.d.ts +9 -0
- package/dist/exports/providers.js +5 -0
- package/dist/exports/views-client.d.ts +23 -0
- package/dist/exports/views-client.js +22 -0
- package/dist/exports/views.d.ts +30 -0
- package/dist/exports/views.js +29 -0
- package/dist/hooks/after-change-global.d.ts +4 -0
- package/dist/hooks/after-change-global.js +109 -0
- package/dist/hooks/after-change.d.ts +16 -0
- package/dist/hooks/after-change.js +205 -0
- package/dist/hooks/after-delete.d.ts +30 -0
- package/dist/hooks/after-delete.js +95 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/jobs-collection.d.ts +17 -0
- package/dist/jobs-collection.js +139 -0
- package/dist/lexical/classifier.d.ts +3 -0
- package/dist/lexical/classifier.js +108 -0
- package/dist/lexical/deserializer.d.ts +4 -0
- package/dist/lexical/deserializer.js +263 -0
- package/dist/lexical/placeholder-integrity.d.ts +6 -0
- package/dist/lexical/placeholder-integrity.js +21 -0
- package/dist/lexical/placeholders.d.ts +21 -0
- package/dist/lexical/placeholders.js +117 -0
- package/dist/lexical/serializer.d.ts +21 -0
- package/dist/lexical/serializer.js +233 -0
- package/dist/lexical/types.d.ts +32 -0
- package/dist/lexical/types.js +1 -0
- package/dist/lib/auth-diagnostics.d.ts +14 -0
- package/dist/lib/auth-diagnostics.js +19 -0
- package/dist/lib/batch-counts.d.ts +58 -0
- package/dist/lib/batch-counts.js +105 -0
- package/dist/lib/bulk-translate-migrations.d.ts +92 -0
- package/dist/lib/bulk-translate-migrations.js +153 -0
- package/dist/lib/coalescing-queue.d.ts +38 -0
- package/dist/lib/coalescing-queue.js +69 -0
- package/dist/lib/content-extractor.d.ts +16 -0
- package/dist/lib/content-extractor.js +410 -0
- package/dist/lib/content-hash.d.ts +1 -0
- package/dist/lib/content-hash.js +19 -0
- package/dist/lib/content-patcher.d.ts +15 -0
- package/dist/lib/content-patcher.js +293 -0
- package/dist/lib/cost-guards.d.ts +2 -0
- package/dist/lib/cost-guards.js +18 -0
- package/dist/lib/daily-spend-cap.d.ts +58 -0
- package/dist/lib/daily-spend-cap.js +233 -0
- package/dist/lib/effective-locales.d.ts +181 -0
- package/dist/lib/effective-locales.js +302 -0
- package/dist/lib/error-messages.d.ts +245 -0
- package/dist/lib/error-messages.js +626 -0
- package/dist/lib/events.d.ts +39 -0
- package/dist/lib/events.js +146 -0
- package/dist/lib/exclude-fields.d.ts +3 -0
- package/dist/lib/exclude-fields.js +64 -0
- package/dist/lib/field-breadcrumb.d.ts +31 -0
- package/dist/lib/field-breadcrumb.js +227 -0
- package/dist/lib/field-diff.d.ts +1 -0
- package/dist/lib/field-diff.js +25 -0
- package/dist/lib/field-empty.d.ts +2 -0
- package/dist/lib/field-empty.js +68 -0
- package/dist/lib/field-resolver.d.ts +3 -0
- package/dist/lib/field-resolver.js +164 -0
- package/dist/lib/group-soft-skips.d.ts +39 -0
- package/dist/lib/group-soft-skips.js +45 -0
- package/dist/lib/locale-merge.d.ts +44 -0
- package/dist/lib/locale-merge.js +357 -0
- package/dist/lib/locale-row-check.d.ts +30 -0
- package/dist/lib/locale-row-check.js +64 -0
- package/dist/lib/logger.d.ts +74 -0
- package/dist/lib/logger.js +97 -0
- package/dist/lib/manual-edit-guard.d.ts +128 -0
- package/dist/lib/manual-edit-guard.js +393 -0
- package/dist/lib/output-validation.d.ts +48 -0
- package/dist/lib/output-validation.js +148 -0
- package/dist/lib/payload-read.d.ts +16 -0
- package/dist/lib/payload-read.js +51 -0
- package/dist/lib/per-doc-claim.d.ts +90 -0
- package/dist/lib/per-doc-claim.js +140 -0
- package/dist/lib/per-doc-lock.d.ts +94 -0
- package/dist/lib/per-doc-lock.js +119 -0
- package/dist/lib/persist-usage.d.ts +91 -0
- package/dist/lib/persist-usage.js +116 -0
- package/dist/lib/progress-store.d.ts +103 -0
- package/dist/lib/progress-store.js +314 -0
- package/dist/lib/rate-limiter.d.ts +3 -0
- package/dist/lib/rate-limiter.js +53 -0
- package/dist/lib/snapshot-select.d.ts +43 -0
- package/dist/lib/snapshot-select.js +108 -0
- package/dist/lib/translate-prompt.d.ts +31 -0
- package/dist/lib/translate-prompt.js +66 -0
- package/dist/lib/translation-token-bucket.d.ts +57 -0
- package/dist/lib/translation-token-bucket.js +365 -0
- package/dist/lib/truncate-source-value.d.ts +1 -0
- package/dist/lib/truncate-source-value.js +27 -0
- package/dist/manual-edit-collection.d.ts +22 -0
- package/dist/manual-edit-collection.js +124 -0
- package/dist/plugin.d.ts +3 -0
- package/dist/plugin.js +934 -0
- package/dist/providers/ai-sdk-adapter.d.ts +35 -0
- package/dist/providers/ai-sdk-adapter.js +100 -0
- package/dist/providers/anthropic.d.ts +31 -0
- package/dist/providers/anthropic.js +66 -0
- package/dist/providers/custom.d.ts +36 -0
- package/dist/providers/custom.js +24 -0
- package/dist/providers/gemini.d.ts +20 -0
- package/dist/providers/gemini.js +48 -0
- package/dist/providers/mock.d.ts +2 -0
- package/dist/providers/mock.js +29 -0
- package/dist/providers/openai.d.ts +28 -0
- package/dist/providers/openai.js +69 -0
- package/dist/settings-global.d.ts +74 -0
- package/dist/settings-global.js +216 -0
- package/dist/tasks/bulk-translate-coordinator.d.ts +115 -0
- package/dist/tasks/bulk-translate-coordinator.js +708 -0
- package/dist/tasks/bulk-translate-doc-task.d.ts +142 -0
- package/dist/tasks/bulk-translate-doc-task.js +1000 -0
- package/dist/tasks/bulk-translate-janitor.d.ts +87 -0
- package/dist/tasks/bulk-translate-janitor.js +311 -0
- package/dist/tasks/translate-job-task.d.ts +51 -0
- package/dist/tasks/translate-job-task.js +154 -0
- package/dist/translate.d.ts +113 -0
- package/dist/translate.js +911 -0
- package/dist/translation-daily-spend-collection.d.ts +24 -0
- package/dist/translation-daily-spend-collection.js +133 -0
- package/dist/translation-rate-limits-collection.d.ts +30 -0
- package/dist/translation-rate-limits-collection.js +144 -0
- package/dist/types.d.ts +672 -0
- package/dist/types.js +1 -0
- package/dist/usage-collection.d.ts +14 -0
- package/dist/usage-collection.js +377 -0
- package/dist/views/BulkRunsHub/BatchRow.d.ts +32 -0
- package/dist/views/BulkRunsHub/BatchRow.js +1222 -0
- package/dist/views/BulkRunsHub/BucketRow.d.ts +62 -0
- package/dist/views/BulkRunsHub/BucketRow.js +982 -0
- package/dist/views/BulkRunsHub/BulkRunsHub.client.d.ts +18 -0
- package/dist/views/BulkRunsHub/BulkRunsHub.client.js +331 -0
- package/dist/views/BulkRunsHub/EmptyState.d.ts +6 -0
- package/dist/views/BulkRunsHub/EmptyState.js +64 -0
- package/dist/views/BulkRunsHub/FilterBar.d.ts +16 -0
- package/dist/views/BulkRunsHub/FilterBar.js +284 -0
- package/dist/views/BulkRunsHub/InFlightBanner.d.ts +14 -0
- package/dist/views/BulkRunsHub/InFlightBanner.js +59 -0
- package/dist/views/BulkRunsHub/StatusBadge.d.ts +64 -0
- package/dist/views/BulkRunsHub/StatusBadge.js +248 -0
- package/dist/views/BulkRunsHub/SummaryStrip.d.ts +22 -0
- package/dist/views/BulkRunsHub/SummaryStrip.js +249 -0
- package/dist/views/BulkRunsHub/bucket-grouping.d.ts +200 -0
- package/dist/views/BulkRunsHub/bucket-grouping.js +344 -0
- package/dist/views/BulkRunsHub/bucketFailureSummary.d.ts +9 -0
- package/dist/views/BulkRunsHub/bucketFailureSummary.js +36 -0
- package/dist/views/BulkRunsHub/dedupedStatusFetch.d.ts +5 -0
- package/dist/views/BulkRunsHub/dedupedStatusFetch.js +45 -0
- package/dist/views/BulkRunsHub/index.d.ts +17 -0
- package/dist/views/BulkRunsHub/index.js +80 -0
- package/dist/views/BulkRunsHub/urlFilters.d.ts +14 -0
- package/dist/views/BulkRunsHub/urlFilters.js +50 -0
- package/dist/views/BulkRunsHub/useBulkRunsList.d.ts +26 -0
- package/dist/views/BulkRunsHub/useBulkRunsList.js +204 -0
- package/dist/views/BulkRunsHub/useUrlFilters.d.ts +10 -0
- package/dist/views/BulkRunsHub/useUrlFilters.js +88 -0
- package/dist/views/TranslationHub/ActiveJobs.d.ts +6 -0
- package/dist/views/TranslationHub/ActiveJobs.js +320 -0
- package/dist/views/TranslationHub/AdvancedPanel.d.ts +17 -0
- package/dist/views/TranslationHub/AdvancedPanel.js +996 -0
- package/dist/views/TranslationHub/AlertBanner.d.ts +6 -0
- package/dist/views/TranslationHub/AlertBanner.js +568 -0
- package/dist/views/TranslationHub/AuditPanel.d.ts +6 -0
- package/dist/views/TranslationHub/AuditPanel.helpers.d.ts +44 -0
- package/dist/views/TranslationHub/AuditPanel.helpers.js +71 -0
- package/dist/views/TranslationHub/AuditPanel.js +1367 -0
- package/dist/views/TranslationHub/BulkTranslate.types.d.ts +242 -0
- package/dist/views/TranslationHub/BulkTranslate.types.js +36 -0
- package/dist/views/TranslationHub/BulkTranslateFailureDrawer.d.ts +19 -0
- package/dist/views/TranslationHub/BulkTranslateFailureDrawer.js +332 -0
- package/dist/views/TranslationHub/BulkTranslateMonitor.d.ts +28 -0
- package/dist/views/TranslationHub/BulkTranslateMonitor.js +305 -0
- package/dist/views/TranslationHub/BulkTranslateNarrowViewportBanner.d.ts +3 -0
- package/dist/views/TranslationHub/BulkTranslateNarrowViewportBanner.js +42 -0
- package/dist/views/TranslationHub/BulkTranslatePostEnqueueTransition.d.ts +26 -0
- package/dist/views/TranslationHub/BulkTranslatePostEnqueueTransition.js +95 -0
- package/dist/views/TranslationHub/BulkTranslatePreflightModal.d.ts +22 -0
- package/dist/views/TranslationHub/BulkTranslatePreflightModal.js +879 -0
- package/dist/views/TranslationHub/BulkTranslateTerminalCard.d.ts +29 -0
- package/dist/views/TranslationHub/BulkTranslateTerminalCard.js +445 -0
- package/dist/views/TranslationHub/BulkTranslateTrigger.d.ts +66 -0
- package/dist/views/TranslationHub/BulkTranslateTrigger.js +161 -0
- package/dist/views/TranslationHub/EditorRecentRunsPanel.d.ts +33 -0
- package/dist/views/TranslationHub/EditorRecentRunsPanel.js +290 -0
- package/dist/views/TranslationHub/Hub.client.d.ts +74 -0
- package/dist/views/TranslationHub/Hub.client.js +357 -0
- package/dist/views/TranslationHub/ModelCombobox.d.ts +14 -0
- package/dist/views/TranslationHub/ModelCombobox.js +415 -0
- package/dist/views/TranslationHub/PerCollectionConfig.d.ts +10 -0
- package/dist/views/TranslationHub/PerCollectionConfig.helpers.d.ts +16 -0
- package/dist/views/TranslationHub/PerCollectionConfig.helpers.js +19 -0
- package/dist/views/TranslationHub/PerCollectionConfig.js +759 -0
- package/dist/views/TranslationHub/SettingsRail.d.ts +11 -0
- package/dist/views/TranslationHub/SettingsRail.js +382 -0
- package/dist/views/TranslationHub/StatusStrip.d.ts +6 -0
- package/dist/views/TranslationHub/StatusStrip.js +451 -0
- package/dist/views/TranslationHub/UsageTable.d.ts +6 -0
- package/dist/views/TranslationHub/UsageTable.helpers.d.ts +69 -0
- package/dist/views/TranslationHub/UsageTable.helpers.js +49 -0
- package/dist/views/TranslationHub/UsageTable.js +1240 -0
- package/dist/views/TranslationHub/alertGrouping.d.ts +70 -0
- package/dist/views/TranslationHub/alertGrouping.js +99 -0
- package/dist/views/TranslationHub/index.d.ts +20 -0
- package/dist/views/TranslationHub/index.js +109 -0
- package/dist/views/TranslationHub/tabNavigation.d.ts +53 -0
- package/dist/views/TranslationHub/tabNavigation.js +74 -0
- package/dist/views/TranslationHub/terminalBannerVisibility.d.ts +33 -0
- package/dist/views/TranslationHub/terminalBannerVisibility.js +124 -0
- package/dist/views/TranslationHub/useBulkTranslateActive.d.ts +49 -0
- package/dist/views/TranslationHub/useBulkTranslateActive.js +251 -0
- package/dist/views/TranslationHub/useFocusTrap.d.ts +6 -0
- package/dist/views/TranslationHub/useFocusTrap.js +81 -0
- package/dist/views/TranslationHub/useTranslationHubUsageSummary.d.ts +77 -0
- package/dist/views/TranslationHub/useTranslationHubUsageSummary.js +267 -0
- package/dist/views/shared/EditorError.d.ts +97 -0
- package/dist/views/shared/EditorError.js +205 -0
- package/dist/views/shared/ModelCell.d.ts +18 -0
- package/dist/views/shared/ModelCell.js +31 -0
- package/dist/views/shared/docHref.d.ts +16 -0
- package/dist/views/shared/docHref.js +26 -0
- package/dist/views/shared/fetch-error-body.d.ts +25 -0
- package/dist/views/shared/fetch-error-body.js +42 -0
- package/dist/views/shared/filterPillStyle.d.ts +35 -0
- package/dist/views/shared/filterPillStyle.js +40 -0
- package/dist/views/shared/format.d.ts +75 -0
- package/dist/views/shared/format.js +131 -0
- package/package.json +141 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Access, CollectionConfig } from 'payload';
|
|
2
|
+
export declare const DEFAULT_TRANSLATION_DAILY_SPEND_COLLECTION_SLUG = "translation-daily-spend";
|
|
3
|
+
/**
|
|
4
|
+
* One row per (date, consumerKey) tracking aggregate USD spent on
|
|
5
|
+
* translation that day. Backs the daily-cap enforcement gate
|
|
6
|
+
* (Decision #14 + F-BIZ-08 + F-SEC-TOTP-BYPASS in
|
|
7
|
+
* Design-2026-05-27-bulk-translate.md).
|
|
8
|
+
*
|
|
9
|
+
* Rows are upserted atomically by the cap utility before each
|
|
10
|
+
* translation enqueue (bulk + per-doc retry + plugin coalesce paths
|
|
11
|
+
* per Decision #13 v2). Multi-process safe via SQL `INSERT ... ON
|
|
12
|
+
* CONFLICT DO UPDATE`, which is required because the totp + bulk
|
|
13
|
+
* endpoints fan out across Vercel function invocations that each
|
|
14
|
+
* have isolated in-memory state.
|
|
15
|
+
*
|
|
16
|
+
* `consumerKey` is the plugin's `options.usageTracking?.consumerKey
|
|
17
|
+
* ?? 'default'` and lets a shared-DB deployment with multiple
|
|
18
|
+
* plugin consumers (rare) keep separate caps per consumer. Default
|
|
19
|
+
* is `'default'` which works for single-consumer DBs.
|
|
20
|
+
*
|
|
21
|
+
* Read access is admin-only. Writes happen only via the cap
|
|
22
|
+
* utility (Payload `find` + raw SQL upsert via drizzle).
|
|
23
|
+
*/
|
|
24
|
+
export declare function createTranslationDailySpendCollection(readAccess?: Access, slug?: string): CollectionConfig;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
export const DEFAULT_TRANSLATION_DAILY_SPEND_COLLECTION_SLUG = 'translation-daily-spend';
|
|
2
|
+
/**
|
|
3
|
+
* One row per (date, consumerKey) tracking aggregate USD spent on
|
|
4
|
+
* translation that day. Backs the daily-cap enforcement gate
|
|
5
|
+
* (Decision #14 + F-BIZ-08 + F-SEC-TOTP-BYPASS in
|
|
6
|
+
* Design-2026-05-27-bulk-translate.md).
|
|
7
|
+
*
|
|
8
|
+
* Rows are upserted atomically by the cap utility before each
|
|
9
|
+
* translation enqueue (bulk + per-doc retry + plugin coalesce paths
|
|
10
|
+
* per Decision #13 v2). Multi-process safe via SQL `INSERT ... ON
|
|
11
|
+
* CONFLICT DO UPDATE`, which is required because the totp + bulk
|
|
12
|
+
* endpoints fan out across Vercel function invocations that each
|
|
13
|
+
* have isolated in-memory state.
|
|
14
|
+
*
|
|
15
|
+
* `consumerKey` is the plugin's `options.usageTracking?.consumerKey
|
|
16
|
+
* ?? 'default'` and lets a shared-DB deployment with multiple
|
|
17
|
+
* plugin consumers (rare) keep separate caps per consumer. Default
|
|
18
|
+
* is `'default'` which works for single-consumer DBs.
|
|
19
|
+
*
|
|
20
|
+
* Read access is admin-only. Writes happen only via the cap
|
|
21
|
+
* utility (Payload `find` + raw SQL upsert via drizzle).
|
|
22
|
+
*/ export function createTranslationDailySpendCollection(readAccess, slug = DEFAULT_TRANSLATION_DAILY_SPEND_COLLECTION_SLUG) {
|
|
23
|
+
const isAdminDefault = ({ req })=>{
|
|
24
|
+
const user = req.user;
|
|
25
|
+
const roles = user?.roles;
|
|
26
|
+
if (Array.isArray(roles) && roles.includes('admin')) return true;
|
|
27
|
+
return false;
|
|
28
|
+
};
|
|
29
|
+
return {
|
|
30
|
+
slug,
|
|
31
|
+
// System rows are never edited in the admin document view, so
|
|
32
|
+
// Payload's document-locking buys nothing here — and its lock check
|
|
33
|
+
// costs a second pool connection inside every update transaction
|
|
34
|
+
// (core's checkDocumentLockStatus runs a find without `req`). Under
|
|
35
|
+
// concurrent updates (e.g. dismiss-all on alerts) that exhausted the
|
|
36
|
+
// pool and deadlocked a consumer in prod on 2026-06-10.
|
|
37
|
+
lockDocuments: false,
|
|
38
|
+
labels: {
|
|
39
|
+
singular: 'Translation Daily Spend',
|
|
40
|
+
plural: 'Translation Daily Spend Counters'
|
|
41
|
+
},
|
|
42
|
+
admin: {
|
|
43
|
+
group: 'Translation',
|
|
44
|
+
useAsTitle: 'date',
|
|
45
|
+
defaultColumns: [
|
|
46
|
+
'date',
|
|
47
|
+
'consumerKey',
|
|
48
|
+
'spendUsd',
|
|
49
|
+
'capUsd',
|
|
50
|
+
'requestCount'
|
|
51
|
+
],
|
|
52
|
+
hidden: ({ user })=>{
|
|
53
|
+
const roles = user?.roles;
|
|
54
|
+
return !Array.isArray(roles) || !roles.includes('admin');
|
|
55
|
+
},
|
|
56
|
+
description: 'Daily aggregate spend counter for the bulk + per-doc translation cost cap. One row per (UTC date, consumer). Updated atomically before each translation enqueue. Read-only in admin; the cap utility owns writes.'
|
|
57
|
+
},
|
|
58
|
+
access: {
|
|
59
|
+
read: readAccess ?? isAdminDefault,
|
|
60
|
+
create: ()=>false,
|
|
61
|
+
update: ()=>false,
|
|
62
|
+
delete: isAdminDefault
|
|
63
|
+
},
|
|
64
|
+
fields: [
|
|
65
|
+
// ISO date string `YYYY-MM-DD` keyed in UTC. Storing as text
|
|
66
|
+
// (not Date) keeps the equality check trivial for the upsert
|
|
67
|
+
// and avoids time-zone drift across function invocations.
|
|
68
|
+
{
|
|
69
|
+
name: 'date',
|
|
70
|
+
type: 'text',
|
|
71
|
+
required: true,
|
|
72
|
+
index: true,
|
|
73
|
+
admin: {
|
|
74
|
+
readOnly: true
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'consumerKey',
|
|
79
|
+
type: 'text',
|
|
80
|
+
required: true,
|
|
81
|
+
index: true,
|
|
82
|
+
defaultValue: 'default',
|
|
83
|
+
admin: {
|
|
84
|
+
readOnly: true
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: 'spendUsd',
|
|
89
|
+
type: 'number',
|
|
90
|
+
required: true,
|
|
91
|
+
defaultValue: 0,
|
|
92
|
+
admin: {
|
|
93
|
+
readOnly: true
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: 'capUsd',
|
|
98
|
+
type: 'number',
|
|
99
|
+
admin: {
|
|
100
|
+
readOnly: true,
|
|
101
|
+
description: 'Cap value snapshotted at row creation. If the env-var cap changes after a day has started, the snapshotted value still applies for that day so admins can predict whether further requests will be allowed.'
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: 'requestCount',
|
|
106
|
+
type: 'number',
|
|
107
|
+
defaultValue: 0,
|
|
108
|
+
admin: {
|
|
109
|
+
readOnly: true
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: 'lastRequestAt',
|
|
114
|
+
type: 'date',
|
|
115
|
+
admin: {
|
|
116
|
+
readOnly: true
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
],
|
|
120
|
+
indexes: [
|
|
121
|
+
// Composite uniqueness for the atomic upsert. Hand-written
|
|
122
|
+
// migration also adds a Postgres UNIQUE constraint on
|
|
123
|
+
// (date, consumer_key) to enforce at the DB level.
|
|
124
|
+
{
|
|
125
|
+
fields: [
|
|
126
|
+
'date',
|
|
127
|
+
'consumerKey'
|
|
128
|
+
],
|
|
129
|
+
unique: true
|
|
130
|
+
}
|
|
131
|
+
]
|
|
132
|
+
};
|
|
133
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Access, CollectionConfig } from 'payload';
|
|
2
|
+
export declare const DEFAULT_TRANSLATION_RATE_LIMITS_COLLECTION_SLUG = "translation-rate-limits";
|
|
3
|
+
/**
|
|
4
|
+
* One row per (provider, model) tracking a Postgres-backed leaky
|
|
5
|
+
* token bucket against upstream LLM RPM caps. Backs the bulk +
|
|
6
|
+
* per-doc + coalesce translate paths per Risk R8 + architect §4 in
|
|
7
|
+
* Design-2026-05-27-bulk-translate.md.
|
|
8
|
+
*
|
|
9
|
+
* The legacy in-memory limiter (`lib/rate-limiter.ts`) is per-process
|
|
10
|
+
* and invisible across Vercel function invocations and server-driven
|
|
11
|
+
* job workers — multiple instances each get their own bucket, so the
|
|
12
|
+
* aggregate burst against the upstream provider can be N× the
|
|
13
|
+
* per-process cap. This collection moves the bucket into Postgres so
|
|
14
|
+
* every entry point shares the same state.
|
|
15
|
+
*
|
|
16
|
+
* Rows are lazily created on first `acquireToken` call for a given
|
|
17
|
+
* (provider, model). Defaults: capacity=60, refillRatePerSec=1.0
|
|
18
|
+
* (60 RPM), tunable per-row by admins post-creation. Env-var
|
|
19
|
+
* overrides (`TRANSLATION_RATE_LIMIT_CAPACITY`,
|
|
20
|
+
* `TRANSLATION_RATE_LIMIT_REFILL_PER_SEC`) seed the defaults at
|
|
21
|
+
* lazy-create time only — mid-life rate changes are admin-edits on
|
|
22
|
+
* the row itself.
|
|
23
|
+
*
|
|
24
|
+
* Read access is admin-only. Writes happen only via the token-bucket
|
|
25
|
+
* utility (Payload `find` + `update` / `create`). Mutability on the
|
|
26
|
+
* `tokens` / `lastRefillAt` fields is intentional — admins can edit
|
|
27
|
+
* `capacity` and `refillRatePerSec` to tune limits, but the bucket
|
|
28
|
+
* state itself is owned by the utility.
|
|
29
|
+
*/
|
|
30
|
+
export declare function createTranslationRateLimitsCollection(readAccess?: Access, slug?: string): CollectionConfig;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
export const DEFAULT_TRANSLATION_RATE_LIMITS_COLLECTION_SLUG = 'translation-rate-limits';
|
|
2
|
+
/**
|
|
3
|
+
* One row per (provider, model) tracking a Postgres-backed leaky
|
|
4
|
+
* token bucket against upstream LLM RPM caps. Backs the bulk +
|
|
5
|
+
* per-doc + coalesce translate paths per Risk R8 + architect §4 in
|
|
6
|
+
* Design-2026-05-27-bulk-translate.md.
|
|
7
|
+
*
|
|
8
|
+
* The legacy in-memory limiter (`lib/rate-limiter.ts`) is per-process
|
|
9
|
+
* and invisible across Vercel function invocations and server-driven
|
|
10
|
+
* job workers — multiple instances each get their own bucket, so the
|
|
11
|
+
* aggregate burst against the upstream provider can be N× the
|
|
12
|
+
* per-process cap. This collection moves the bucket into Postgres so
|
|
13
|
+
* every entry point shares the same state.
|
|
14
|
+
*
|
|
15
|
+
* Rows are lazily created on first `acquireToken` call for a given
|
|
16
|
+
* (provider, model). Defaults: capacity=60, refillRatePerSec=1.0
|
|
17
|
+
* (60 RPM), tunable per-row by admins post-creation. Env-var
|
|
18
|
+
* overrides (`TRANSLATION_RATE_LIMIT_CAPACITY`,
|
|
19
|
+
* `TRANSLATION_RATE_LIMIT_REFILL_PER_SEC`) seed the defaults at
|
|
20
|
+
* lazy-create time only — mid-life rate changes are admin-edits on
|
|
21
|
+
* the row itself.
|
|
22
|
+
*
|
|
23
|
+
* Read access is admin-only. Writes happen only via the token-bucket
|
|
24
|
+
* utility (Payload `find` + `update` / `create`). Mutability on the
|
|
25
|
+
* `tokens` / `lastRefillAt` fields is intentional — admins can edit
|
|
26
|
+
* `capacity` and `refillRatePerSec` to tune limits, but the bucket
|
|
27
|
+
* state itself is owned by the utility.
|
|
28
|
+
*/ export function createTranslationRateLimitsCollection(readAccess, slug = DEFAULT_TRANSLATION_RATE_LIMITS_COLLECTION_SLUG) {
|
|
29
|
+
const isAdminDefault = ({ req })=>{
|
|
30
|
+
const user = req.user;
|
|
31
|
+
const roles = user?.roles;
|
|
32
|
+
if (Array.isArray(roles) && roles.includes('admin')) return true;
|
|
33
|
+
return false;
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
slug,
|
|
37
|
+
// System rows are never edited in the admin document view, so
|
|
38
|
+
// Payload's document-locking buys nothing here — and its lock check
|
|
39
|
+
// costs a second pool connection inside every update transaction
|
|
40
|
+
// (core's checkDocumentLockStatus runs a find without `req`). Under
|
|
41
|
+
// concurrent updates (e.g. dismiss-all on alerts) that exhausted the
|
|
42
|
+
// pool and deadlocked a consumer in prod on 2026-06-10.
|
|
43
|
+
lockDocuments: false,
|
|
44
|
+
labels: {
|
|
45
|
+
singular: 'Translation Rate Limit',
|
|
46
|
+
plural: 'Translation Rate Limits'
|
|
47
|
+
},
|
|
48
|
+
admin: {
|
|
49
|
+
group: 'Translation',
|
|
50
|
+
useAsTitle: 'model',
|
|
51
|
+
defaultColumns: [
|
|
52
|
+
'provider',
|
|
53
|
+
'model',
|
|
54
|
+
'tokens',
|
|
55
|
+
'capacity',
|
|
56
|
+
'refillRatePerSec',
|
|
57
|
+
'lastRefillAt'
|
|
58
|
+
],
|
|
59
|
+
hidden: ({ user })=>{
|
|
60
|
+
const roles = user?.roles;
|
|
61
|
+
return !Array.isArray(roles) || !roles.includes('admin');
|
|
62
|
+
},
|
|
63
|
+
description: 'Postgres-backed leaky token bucket per (provider, model). Throttles bulk + per-doc + coalesce translation paths against upstream LLM RPM caps. Rows are created lazily on first request; admins may tune capacity / refillRatePerSec to match provider plan limits.'
|
|
64
|
+
},
|
|
65
|
+
access: {
|
|
66
|
+
read: readAccess ?? isAdminDefault,
|
|
67
|
+
create: ()=>false,
|
|
68
|
+
update: ()=>false,
|
|
69
|
+
delete: isAdminDefault
|
|
70
|
+
},
|
|
71
|
+
fields: [
|
|
72
|
+
{
|
|
73
|
+
name: 'provider',
|
|
74
|
+
type: 'text',
|
|
75
|
+
required: true,
|
|
76
|
+
index: true,
|
|
77
|
+
admin: {
|
|
78
|
+
readOnly: true,
|
|
79
|
+
description: 'Provider key — e.g. "openrouter", "anthropic", "openai". Matches the plugin provider id.'
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'model',
|
|
84
|
+
type: 'text',
|
|
85
|
+
required: true,
|
|
86
|
+
index: true,
|
|
87
|
+
admin: {
|
|
88
|
+
readOnly: true,
|
|
89
|
+
description: 'Model id within the provider — e.g. "google/gemini-2.5-flash". The (provider, model) pair is the bucket key.'
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: 'tokens',
|
|
94
|
+
type: 'number',
|
|
95
|
+
required: true,
|
|
96
|
+
defaultValue: 0,
|
|
97
|
+
admin: {
|
|
98
|
+
readOnly: true,
|
|
99
|
+
description: 'Current remaining tokens in the bucket. Refilled on each acquire based on (now - lastRefillAt) * refillRatePerSec, capped at capacity. Owned by the token-bucket utility — do not hand-edit.'
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: 'capacity',
|
|
104
|
+
type: 'number',
|
|
105
|
+
required: true,
|
|
106
|
+
defaultValue: 60,
|
|
107
|
+
admin: {
|
|
108
|
+
description: 'Maximum bucket size. Tune to match the provider plan RPM cap (e.g. 60 for a 60 RPM plan, 600 for a 600 RPM plan). Admins may edit; the utility respects the current value.'
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: 'refillRatePerSec',
|
|
113
|
+
type: 'number',
|
|
114
|
+
required: true,
|
|
115
|
+
defaultValue: 1,
|
|
116
|
+
admin: {
|
|
117
|
+
description: 'Tokens added per second. For a 60 RPM cap use 1.0; for 600 RPM use 10.0. Admins may edit; the utility respects the current value.'
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: 'lastRefillAt',
|
|
122
|
+
type: 'date',
|
|
123
|
+
required: true,
|
|
124
|
+
admin: {
|
|
125
|
+
readOnly: true,
|
|
126
|
+
description: 'Timestamp of the last refill calculation. Used by the utility to compute elapsed time since the last acquire. Owned by the utility.'
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
],
|
|
130
|
+
indexes: [
|
|
131
|
+
// Composite uniqueness for the (provider, model) bucket key.
|
|
132
|
+
// Hand-written migration should also add a Postgres UNIQUE
|
|
133
|
+
// constraint at the DB level to enforce concurrent-insert
|
|
134
|
+
// safety.
|
|
135
|
+
{
|
|
136
|
+
fields: [
|
|
137
|
+
'provider',
|
|
138
|
+
'model'
|
|
139
|
+
],
|
|
140
|
+
unique: true
|
|
141
|
+
}
|
|
142
|
+
]
|
|
143
|
+
};
|
|
144
|
+
}
|