@aglyn/plugins-ai 1.0.0-beta.230 → 1.0.0-beta.232
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/package.json +17 -17
- package/src/lib/activity/ai-activity-actions.js +8 -4
- package/src/lib/activity/ai-activity-actions.js.map +1 -1
- package/src/lib/components/ai-assist-provider-on-host.component.d.ts +66 -0
- package/src/lib/components/ai-assist-provider-on-host.component.js +78 -0
- package/src/lib/components/ai-assist-provider-on-host.component.js.map +1 -0
- package/src/lib/components/ai-assist-provider.component.js +9 -10
- package/src/lib/components/ai-assist-provider.component.js.map +1 -1
- package/src/lib/components/ai-job-follow.component.js +1 -0
- package/src/lib/components/ai-job-follow.component.js.map +1 -1
- package/src/lib/components/ai-job-links.js +13 -0
- package/src/lib/components/ai-job-links.js.map +1 -1
- package/src/lib/components/ai-job-plan.component.js +3 -1
- package/src/lib/components/ai-job-plan.component.js.map +1 -1
- package/src/lib/components/ai-jobs-list-page.component.js +1 -0
- package/src/lib/components/ai-jobs-list-page.component.js.map +1 -1
- package/src/lib/components/ai-media-create.component.d.ts +20 -7
- package/src/lib/components/ai-media-create.component.js +107 -55
- package/src/lib/components/ai-media-create.component.js.map +1 -1
- package/src/lib/components/ai-permissions-on-host.component.d.ts +3 -46
- package/src/lib/components/ai-permissions-on-host.component.js +5 -53
- package/src/lib/components/ai-permissions-on-host.component.js.map +1 -1
- package/src/lib/components/ai-seo-fields-card.component.js +1 -0
- package/src/lib/components/ai-seo-fields-card.component.js.map +1 -1
- package/src/lib/components/ai-site-build-page.component.js +20 -8
- package/src/lib/components/ai-site-build-page.component.js.map +1 -1
- package/src/lib/components/ai-site-memory-card.component.d.ts +32 -0
- package/src/lib/components/ai-site-memory-card.component.js +187 -0
- package/src/lib/components/ai-site-memory-card.component.js.map +1 -0
- package/src/lib/components/ai-site-start-card.component.js +80 -77
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/ai-theme-proposal-card.component.js +4 -0
- package/src/lib/components/ai-theme-proposal-card.component.js.map +1 -1
- package/src/lib/components/ai-usage-strip.component.js +1 -0
- package/src/lib/components/ai-usage-strip.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.d.ts +10 -0
- package/src/lib/components/assist-panel.component.js +119 -9
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/components/assist-signals-page.component.js +2 -0
- package/src/lib/components/assist-signals-page.component.js.map +1 -1
- package/src/lib/components/billing-ai-allotments.component.js +3 -0
- package/src/lib/components/billing-ai-allotments.component.js.map +1 -1
- package/src/lib/components/billing-ai-top-users.component.js +1 -0
- package/src/lib/components/billing-ai-top-users.component.js.map +1 -1
- package/src/lib/components/billing-assist-overage-card.component.js +2 -0
- package/src/lib/components/billing-assist-overage-card.component.js.map +1 -1
- package/src/lib/components/member-ai-usage-card.component.js +10 -0
- package/src/lib/components/member-ai-usage-card.component.js.map +1 -1
- package/src/lib/components/staff-org-ai-card.component.js +1 -0
- package/src/lib/components/staff-org-ai-card.component.js.map +1 -1
- package/src/lib/components/staff-org-ai-conversations.component.d.ts +23 -0
- package/src/lib/components/staff-org-ai-conversations.component.js +346 -0
- package/src/lib/components/staff-org-ai-conversations.component.js.map +1 -0
- package/src/lib/jobs/ai-build-capabilities.d.ts +13 -1
- package/src/lib/jobs/ai-build-capabilities.js +101 -7
- package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
- package/src/lib/jobs/ai-build-unit-outcome.js +4 -2
- package/src/lib/jobs/ai-build-unit-outcome.js.map +1 -1
- package/src/lib/jobs/ai-business-profile-prefill.d.ts +29 -0
- package/src/lib/jobs/ai-business-profile-prefill.js +85 -0
- package/src/lib/jobs/ai-business-profile-prefill.js.map +1 -0
- package/src/lib/jobs/ai-job-auto-confirm.d.ts +8 -0
- package/src/lib/jobs/ai-job-auto-confirm.js +30 -6
- package/src/lib/jobs/ai-job-auto-confirm.js.map +1 -1
- package/src/lib/jobs/ai-job-build-step.js +14 -1
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- package/src/lib/jobs/ai-job-edit-step.d.ts +225 -0
- package/src/lib/jobs/ai-job-edit-step.js +631 -0
- package/src/lib/jobs/ai-job-edit-step.js.map +1 -0
- package/src/lib/jobs/ai-job-generation.d.ts +8 -0
- package/src/lib/jobs/ai-job-generation.js +13 -2
- package/src/lib/jobs/ai-job-generation.js.map +1 -1
- package/src/lib/jobs/ai-job-insight-step.js +29 -5
- package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.d.ts +78 -0
- package/src/lib/jobs/ai-job-layout-language.js +200 -0
- package/src/lib/jobs/ai-job-layout-language.js.map +1 -0
- package/src/lib/jobs/ai-job-layout-site-pages.d.ts +13 -2
- package/src/lib/jobs/ai-job-layout-site-pages.js +18 -2
- package/src/lib/jobs/ai-job-layout-site-pages.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.d.ts +5 -0
- package/src/lib/jobs/ai-job-layout-step.js +107 -6
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-logic-step.d.ts +20 -0
- package/src/lib/jobs/ai-job-logic-step.js +87 -6
- package/src/lib/jobs/ai-job-logic-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +194 -0
- package/src/lib/jobs/ai-job-page-language.js +418 -0
- package/src/lib/jobs/ai-job-page-language.js.map +1 -0
- package/src/lib/jobs/ai-job-page-sections.d.ts +37 -1
- package/src/lib/jobs/ai-job-page-sections.js +93 -11
- package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.d.ts +4 -4
- package/src/lib/jobs/ai-job-page-step.js +115 -14
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +20 -6
- package/src/lib/jobs/ai-job-plan-step.js +62 -22
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-content.d.ts +163 -0
- package/src/lib/jobs/ai-job-site-content.js +443 -0
- package/src/lib/jobs/ai-job-site-content.js.map +1 -0
- package/src/lib/jobs/ai-job-site-look.d.ts +112 -0
- package/src/lib/jobs/ai-job-site-look.js +322 -0
- package/src/lib/jobs/ai-job-site-look.js.map +1 -0
- package/src/lib/jobs/ai-job-site-step.d.ts +30 -5
- package/src/lib/jobs/ai-job-site-step.js +183 -27
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-workflow-step.js +2 -2
- package/src/lib/jobs/ai-job-workflow-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs-notify.d.ts +6 -1
- package/src/lib/jobs/ai-jobs-notify.js +15 -3
- package/src/lib/jobs/ai-jobs-notify.js.map +1 -1
- package/src/lib/jobs/ai-jobs.d.ts +1 -1
- package/src/lib/jobs/ai-jobs.js +17 -8
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-layout-full-height.d.ts +40 -0
- package/src/lib/jobs/ai-layout-full-height.js +82 -0
- package/src/lib/jobs/ai-layout-full-height.js.map +1 -0
- package/src/lib/jobs/ai-layout-site-facts.d.ts +54 -0
- package/src/lib/jobs/ai-layout-site-facts.js +153 -0
- package/src/lib/jobs/ai-layout-site-facts.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.d.ts +154 -0
- package/src/lib/layout-language/ai-layout-compiler.js +1235 -0
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -0
- package/src/lib/layout-language/ai-layout-copy.d.ts +73 -0
- package/src/lib/layout-language/ai-layout-copy.js +137 -0
- package/src/lib/layout-language/ai-layout-copy.js.map +1 -0
- package/src/lib/layout-language/ai-layout-frame.d.ts +52 -0
- package/src/lib/layout-language/ai-layout-frame.js +458 -0
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -0
- package/src/lib/layout-language/ai-layout-gaps.d.ts +63 -0
- package/src/lib/layout-language/ai-layout-gaps.js +345 -0
- package/src/lib/layout-language/ai-layout-gaps.js.map +1 -0
- package/src/lib/layout-language/ai-layout-language.d.ts +168 -0
- package/src/lib/layout-language/ai-layout-language.js +529 -0
- package/src/lib/layout-language/ai-layout-language.js.map +1 -0
- package/src/lib/layout-language/ai-layout-links.d.ts +80 -0
- package/src/lib/layout-language/ai-layout-links.js +109 -0
- package/src/lib/layout-language/ai-layout-links.js.map +1 -0
- package/src/lib/layout-language/ai-layout-pictures.d.ts +144 -0
- package/src/lib/layout-language/ai-layout-pictures.js +214 -0
- package/src/lib/layout-language/ai-layout-pictures.js.map +1 -0
- package/src/lib/layout-language/ai-layout-store.d.ts +41 -0
- package/src/lib/layout-language/ai-layout-store.js +75 -0
- package/src/lib/layout-language/ai-layout-store.js.map +1 -0
- package/src/lib/layout-language/ai-layout-tree.d.ts +59 -0
- package/src/lib/layout-language/ai-layout-tree.js +84 -0
- package/src/lib/layout-language/ai-layout-tree.js.map +1 -0
- package/src/lib/lazy-widget.d.ts +45 -0
- package/src/lib/lazy-widget.js +53 -0
- package/src/lib/lazy-widget.js.map +1 -0
- package/src/lib/model/ai-build-job.js +2 -1
- package/src/lib/model/ai-build-job.js.map +1 -1
- package/src/lib/model/ai-build-progress.d.ts +20 -3
- package/src/lib/model/ai-build-progress.js +52 -7
- package/src/lib/model/ai-build-progress.js.map +1 -1
- package/src/lib/model/ai-edit-job.d.ts +109 -0
- package/src/lib/model/ai-edit-job.js +157 -0
- package/src/lib/model/ai-edit-job.js.map +1 -0
- package/src/lib/model/ai-host-publish-context.d.ts +51 -0
- package/src/lib/model/ai-host-publish-context.js +104 -0
- package/src/lib/model/ai-host-publish-context.js.map +1 -0
- package/src/lib/model/ai-job-activity.d.ts +22 -0
- package/src/lib/model/ai-job-activity.js +23 -1
- package/src/lib/model/ai-job-activity.js.map +1 -1
- package/src/lib/model/ai-job-failure-copy.d.ts +38 -1
- package/src/lib/model/ai-job-failure-copy.js +56 -1
- package/src/lib/model/ai-job-failure-copy.js.map +1 -1
- package/src/lib/model/ai-job-notice.d.ts +3 -1
- package/src/lib/model/ai-job-notice.js +31 -9
- package/src/lib/model/ai-job-notice.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +22 -1
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-logic-job.d.ts +18 -1
- package/src/lib/model/ai-logic-job.js +24 -3
- package/src/lib/model/ai-logic-job.js.map +1 -1
- package/src/lib/model/ai-media-credits.d.ts +8 -4
- package/src/lib/model/ai-media-credits.js +11 -4
- package/src/lib/model/ai-media-credits.js.map +1 -1
- package/src/lib/model/ai-media-kinds.d.ts +68 -0
- package/src/lib/model/ai-media-kinds.js +98 -0
- package/src/lib/model/ai-media-kinds.js.map +1 -0
- package/src/lib/model/ai-site-build-progress.d.ts +9 -1
- package/src/lib/model/ai-site-build-progress.js +54 -14
- package/src/lib/model/ai-site-build-progress.js.map +1 -1
- package/src/lib/model/ai-site-context.d.ts +111 -0
- package/src/lib/model/ai-site-context.js +222 -0
- package/src/lib/model/ai-site-context.js.map +1 -0
- package/src/lib/model/ai-site-job.d.ts +18 -2
- package/src/lib/model/ai-site-job.js +21 -5
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-site-kinds.d.ts +164 -0
- package/src/lib/model/ai-site-kinds.js +1942 -0
- package/src/lib/model/ai-site-kinds.js.map +1 -0
- package/src/lib/model/ai-site-look.d.ts +150 -0
- package/src/lib/model/ai-site-look.js +1236 -0
- package/src/lib/model/ai-site-look.js.map +1 -0
- package/src/lib/model/ai-site-memory.d.ts +80 -0
- package/src/lib/model/ai-site-memory.js +171 -0
- package/src/lib/model/ai-site-memory.js.map +1 -0
- package/src/lib/model/ai-site-start-seo.d.ts +1 -1
- package/src/lib/model/ai-site-start.d.ts +15 -3
- package/src/lib/model/ai-site-start.js +21 -4
- package/src/lib/model/ai-site-start.js.map +1 -1
- package/src/lib/model/assist-follow-up.d.ts +92 -0
- package/src/lib/model/assist-follow-up.js +128 -0
- package/src/lib/model/assist-follow-up.js.map +1 -0
- package/src/lib/plugin-config.js +2 -0
- package/src/lib/plugin-config.js.map +1 -1
- package/src/lib/plugin.js +99 -48
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/providers/anthropic.d.ts +9 -1
- package/src/lib/providers/anthropic.js +143 -9
- package/src/lib/providers/anthropic.js.map +1 -1
- package/src/lib/providers/catalog.d.ts +38 -8
- package/src/lib/providers/catalog.js +106 -14
- package/src/lib/providers/catalog.js.map +1 -1
- package/src/lib/providers/contract.d.ts +29 -0
- package/src/lib/providers/contract.js.map +1 -1
- package/src/lib/providers/image-contract.d.ts +22 -1
- package/src/lib/providers/image-contract.js +40 -1
- package/src/lib/providers/image-contract.js.map +1 -1
- package/src/lib/providers/image-endpoints.d.ts +24 -0
- package/src/lib/providers/image-endpoints.js +28 -0
- package/src/lib/providers/image-endpoints.js.map +1 -0
- package/src/lib/providers/model-choice.js +23 -2
- package/src/lib/providers/model-choice.js.map +1 -1
- package/src/lib/providers/routing.js +18 -0
- package/src/lib/providers/routing.js.map +1 -1
- package/src/lib/providers/vertex-image-endpoint.d.ts +48 -0
- package/src/lib/providers/vertex-image-endpoint.js +80 -0
- package/src/lib/providers/vertex-image-endpoint.js.map +1 -0
- package/src/lib/providers/vertex-image.d.ts +13 -27
- package/src/lib/providers/vertex-image.js +15 -42
- package/src/lib/providers/vertex-image.js.map +1 -1
- package/src/lib/runtime/ai-blog-post-generation.d.ts +62 -0
- package/src/lib/runtime/ai-blog-post-generation.js +89 -0
- package/src/lib/runtime/ai-blog-post-generation.js.map +1 -0
- package/src/lib/runtime/ai-dev-env.d.ts +53 -0
- package/src/lib/runtime/ai-dev-env.js +72 -0
- package/src/lib/runtime/ai-dev-env.js.map +1 -0
- package/src/lib/runtime/ai-dev-replay.d.ts +116 -0
- package/src/lib/runtime/ai-dev-replay.js +313 -0
- package/src/lib/runtime/ai-dev-replay.js.map +1 -0
- package/src/lib/runtime/ai-doctrine-validators.d.ts +82 -0
- package/src/lib/runtime/ai-doctrine-validators.js +436 -4
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +16 -3
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-eval-live.js +4 -0
- package/src/lib/runtime/ai-eval-live.js.map +1 -1
- package/src/lib/runtime/ai-eval.d.ts +13 -1
- package/src/lib/runtime/ai-eval.js +84 -1
- package/src/lib/runtime/ai-eval.js.map +1 -1
- package/src/lib/runtime/ai-icon-library.d.ts +257 -0
- package/src/lib/runtime/ai-icon-library.js +254 -0
- package/src/lib/runtime/ai-icon-library.js.map +1 -0
- package/src/lib/runtime/ai-live-batch.d.ts +45 -0
- package/src/lib/runtime/ai-live-batch.js +89 -0
- package/src/lib/runtime/ai-live-batch.js.map +1 -0
- package/src/lib/runtime/ai-node-tree.d.ts +9 -0
- package/src/lib/runtime/ai-node-tree.js +30 -4
- package/src/lib/runtime/ai-node-tree.js.map +1 -1
- package/src/lib/runtime/ai-palette.d.ts +6 -0
- package/src/lib/runtime/ai-palette.generated.js +70 -0
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/runtime/ai-palette.js.map +1 -1
- package/src/lib/runtime/ai-runtime.js +46 -2
- package/src/lib/runtime/ai-runtime.js.map +1 -1
- package/src/lib/runtime/site-context.d.ts +58 -0
- package/src/lib/runtime/site-context.js +127 -0
- package/src/lib/runtime/site-context.js.map +1 -0
- package/src/lib/server/ai-admin-conversations.d.ts +22 -0
- package/src/lib/server/ai-admin-conversations.js +197 -0
- package/src/lib/server/ai-admin-conversations.js.map +1 -0
- package/src/lib/server/ai-media-image.d.ts +5 -5
- package/src/lib/server/ai-media-image.js +25 -11
- package/src/lib/server/ai-media-image.js.map +1 -1
- package/src/lib/server/ai-media-raster-prompt.d.ts +34 -0
- package/src/lib/server/ai-media-raster-prompt.js +53 -0
- package/src/lib/server/ai-media-raster-prompt.js.map +1 -0
- package/src/lib/server/assist-chat.d.ts +11 -0
- package/src/lib/server/assist-chat.js +57 -18
- package/src/lib/server/assist-chat.js.map +1 -1
- package/src/lib/server/assist-edit-applied.js +16 -3
- package/src/lib/server/assist-edit-applied.js.map +1 -1
- package/src/lib/server/assist-view-context.d.ts +19 -1
- package/src/lib/server/assist-view-context.js +38 -1
- package/src/lib/server/assist-view-context.js.map +1 -1
- package/src/lib/server.js +10 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/subprocessors.js +17 -1
- package/src/lib/subprocessors.js.map +1 -1
- package/src/lib/tools/ai-blog-post-tool.d.ts +60 -0
- package/src/lib/tools/ai-blog-post-tool.js +175 -0
- package/src/lib/tools/ai-blog-post-tool.js.map +1 -0
- package/src/lib/tools/ai-workflow-tool.d.ts +6 -2
- package/src/lib/tools/ai-workflow-tool.js +16 -14
- package/src/lib/tools/ai-workflow-tool.js.map +1 -1
- package/src/lib/usage/assist-usage.d.ts +3 -0
- package/src/lib/usage/assist-usage.js +4 -2
- package/src/lib/usage/assist-usage.js.map +1 -1
- package/src/lib/usage/staff-org-ai-conversations.d.ts +103 -0
- package/src/lib/usage/staff-org-ai-conversations.js +152 -0
- package/src/lib/usage/staff-org-ai-conversations.js.map +1 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/providers/image-contract.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { AiUsage } from './contract'\n\n/**\n * The image-generation contract (AGL-3602): the one shape a door that makes\n * pictures speaks, beside the text contract in `contract.ts` rather than\n * inside it. A text provider answers tokens and is priced per token; an image\n * provider answers pictures and is priced per picture, and folding the two\n * into one interface would hand every text adapter a method it cannot\n * implement and every image adapter four token counts it does not have.\n *\n * Browser-safe: the constants and types here are what the Media dialog reads\n * too. The adapter that speaks a vendor's wire lives in its own module.\n */\n\n/** The shapes a picture may be asked for, as the dialog offers them. */\nexport const AI_IMAGE_ASPECT_RATIOS = ['1:1', '4:3', '3:4', '16:9', '9:16'] as const\nexport type AiImageAspectRatio = (typeof AI_IMAGE_ASPECT_RATIOS)[number]\n\n/** The most pictures one request makes. */\nexport const AI_IMAGE_MAX_COUNT = 4\n\n/** The longest description a request carries, in characters. */\nexport const AI_IMAGE_PROMPT_MAX_CHARS = 1_000\n\n/**\n * The two ways the Media dialog makes a picture (AGL-3602): a `photo` from\n * the image provider, and an `illustration` drawn as SVG by the text\n * provider every other AI door already uses.\n */\nexport const AI_IMAGE_MODES = ['photo', 'illustration'] as const\nexport type AiImageMode = (typeof AI_IMAGE_MODES)[number]\n\n/** What an illustration is: a scene, an icon, a tileable pattern or a simple mark. */\nexport const AI_SVG_STYLES = ['illustration', 'icon', 'pattern', 'logo'] as const\nexport type AiSvgStyle = (typeof AI_SVG_STYLES)[number]\n\n/** The most colors a person may name for an illustration. */\nexport const AI_SVG_MAX_COLORS = 6\n\n/**\n * Whether this deployment offers the photo mode, as its build says: a\n * deployment that configured an image provider sets\n * `NEXT_PUBLIC_AI_IMAGE_PHOTOS=on` beside it. Read where the dialog renders,\n * so no request is made to decide what to draw; the door still refuses a\n * photo on a deployment with no provider.\n */\nexport function aiImagePhotosOffered(): boolean {\n return process.env.NEXT_PUBLIC_AI_IMAGE_PHOTOS === 'on'\n}\n\nexport function isAiImageAspectRatio(value: unknown): value is AiImageAspectRatio {\n return (AI_IMAGE_ASPECT_RATIOS as readonly unknown[]).includes(value)\n}\n\n/** One request to an image provider. */\nexport interface AiImageRequest {\n /** The model id, from the image catalog. */\n model: string\n prompt: string\n aspectRatio: AiImageAspectRatio\n /** 1 to `AI_IMAGE_MAX_COUNT`. */\n count: number\n}\n\n/** One picture the provider returned. */\nexport interface AiGeneratedImage {\n /** The bytes, base64-encoded, as the provider sent them. */\n base64: string\n /** The type the bytes are in: `image/jpeg` or `image/png`. */\n mimeType: string\n}\n\n/** What one request made. */\nexport interface AiImageResult {\n model: string\n images: AiGeneratedImage[]\n /**\n * The tokens the requests spent beside the pictures themselves — the\n * prompt, and any thinking — which the meter prices at the image row's\n * token rates. The pictures are priced per picture, never as tokens here.\n */\n usage: AiUsage\n /**\n * How many of the requested pictures the provider's safety filter held\n * back. A picture held back is not returned and is never billed.\n */\n filtered: number\n}\n\n/**\n * The provider's safety filter declined the whole request: the description,\n * or every picture made from it. Nothing came back, so nothing is billed and\n * the reservation is handed back. `message` is the sentence a person reads.\n */\nexport class AiImageSafetyRefusal extends Error {\n override readonly name = 'AiImageSafetyRefusal'\n /** The provider's own reason codes, for the log and never for a reader. */\n readonly reasons: readonly string[]\n\n constructor(reasons: readonly string[] = []) {\n super(\n 'The image service declined this description. Try describing the scene differently, without real people, brands or anything unsafe.',\n )\n this.reasons = reasons\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\n/**\n * An image provider: one vendor's wire behind the contract. It reports\n * whether this deployment configured it, so a door can answer \"not here\"\n * before it reserves anything, and it maps its vendor's failures onto\n * `AiUpstreamError` and its declines onto `AiImageSafetyRefusal`.\n */\nexport interface AiImageProvider {\n readonly id: string\n readonly label: string\n /** The model a request runs on when nothing else names one. */\n defaultModel(): string\n /** True when this deployment's environment turns the provider on. */\n configured(): boolean\n /** The host requests go to, for the operator and the subprocessor record. */\n endpointHost(): string\n generate(request: AiImageRequest): Promise<AiImageResult>\n}\n"],"names":["AI_IMAGE_ASPECT_RATIOS","AI_IMAGE_MAX_COUNT","AI_IMAGE_PROMPT_MAX_CHARS","AI_IMAGE_MODES","AI_SVG_STYLES","AI_SVG_MAX_COLORS","aiImagePhotosOffered","process","env","NEXT_PUBLIC_AI_IMAGE_PHOTOS","isAiImageAspectRatio","
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/providers/image-contract.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { AiUsage } from './contract'\n\n/**\n * The image-generation contract (AGL-3602): the one shape a door that makes\n * pictures speaks, beside the text contract in `contract.ts` rather than\n * inside it. A text provider answers tokens and is priced per token; an image\n * provider answers pictures and is priced per picture, and folding the two\n * into one interface would hand every text adapter a method it cannot\n * implement and every image adapter four token counts it does not have.\n *\n * Browser-safe: the constants and types here are what the Media dialog reads\n * too. The adapter that speaks a vendor's wire lives in its own module.\n */\n\n/** The shapes a picture may be asked for, as the dialog offers them. */\nexport const AI_IMAGE_ASPECT_RATIOS = ['1:1', '4:3', '3:4', '16:9', '9:16'] as const\nexport type AiImageAspectRatio = (typeof AI_IMAGE_ASPECT_RATIOS)[number]\n\n/**\n * The sizes a picture is made at (AGL-3602): 512 px on the Free plan and 1K\n * on every paid plan. The door chooses from the workspace's plan\n * (`aiImageSizeForPlan`); nothing a request sends can choose it.\n */\nexport const AI_IMAGE_SIZES = ['512', '1K'] as const\nexport type AiImageSize = (typeof AI_IMAGE_SIZES)[number]\n\n/** The size a picture is made at for a workspace on the Free plan, or on a paid one. */\nexport function aiImageSizeForPlan(free: boolean): AiImageSize {\n return free ? '512' : '1K'\n}\n\n/** The most pictures one request makes. */\nexport const AI_IMAGE_MAX_COUNT = 4\n\n/** The longest description a request carries, in characters. */\nexport const AI_IMAGE_PROMPT_MAX_CHARS = 1_000\n\n/**\n * The two ways the Media dialog makes a picture (AGL-3602): a `photo` from\n * the image provider, and an `illustration` drawn as SVG by the text\n * provider every other AI door already uses. `photo` is the image provider's\n * mode whatever the kind — a watercolor is made there too — and is stored on\n * every picture's record, so the value is never renamed.\n */\nexport const AI_IMAGE_MODES = ['photo', 'illustration'] as const\nexport type AiImageMode = (typeof AI_IMAGE_MODES)[number]\n\n/** What an illustration is: a scene, an icon, a tileable pattern or a simple mark. */\nexport const AI_SVG_STYLES = ['illustration', 'icon', 'pattern', 'logo'] as const\nexport type AiSvgStyle = (typeof AI_SVG_STYLES)[number]\n\n/**\n * What a picture from the image provider is (AGL-3602): `photo` leaves the\n * style to the description, and every other kind adds its own fixed style\n * wording to it — a photographic genre, an art style or a design asset.\n */\nexport const AI_RASTER_STYLES = [\n 'photo',\n 'natural',\n 'product',\n 'lifestyle',\n 'architecture',\n 'food',\n 'aerial',\n 'render-3d',\n 'watercolor',\n 'oil-painting',\n 'flat-art',\n 'line-drawing',\n 'cartoon',\n 'background',\n 'banner',\n 'social',\n 'mockup',\n] as const\nexport type AiRasterStyle = (typeof AI_RASTER_STYLES)[number]\n\nexport function isAiRasterStyle(value: unknown): value is AiRasterStyle {\n return (AI_RASTER_STYLES as readonly unknown[]).includes(value)\n}\n\n/** The most colors a person may name for an illustration. */\nexport const AI_SVG_MAX_COLORS = 6\n\n/**\n * Whether this deployment offers the photo mode, as its build says: a\n * deployment that configured an image provider sets\n * `NEXT_PUBLIC_AI_IMAGE_PHOTOS=on` beside it. Read where the dialog renders,\n * so no request is made to decide what to draw; the door still refuses a\n * photo on a deployment with no provider.\n */\nexport function aiImagePhotosOffered(): boolean {\n return process.env.NEXT_PUBLIC_AI_IMAGE_PHOTOS === 'on'\n}\n\nexport function isAiImageAspectRatio(value: unknown): value is AiImageAspectRatio {\n return (AI_IMAGE_ASPECT_RATIOS as readonly unknown[]).includes(value)\n}\n\n/** One request to an image provider. */\nexport interface AiImageRequest {\n /** The model id, from the image catalog. */\n model: string\n prompt: string\n aspectRatio: AiImageAspectRatio\n /** The size, chosen by the door from the workspace's plan. */\n size: AiImageSize\n /** 1 to `AI_IMAGE_MAX_COUNT`. */\n count: number\n}\n\n/** One picture the provider returned. */\nexport interface AiGeneratedImage {\n /** The bytes, base64-encoded, as the provider sent them. */\n base64: string\n /** The type the bytes are in: `image/jpeg` or `image/png`. */\n mimeType: string\n}\n\n/** What one request made. */\nexport interface AiImageResult {\n model: string\n images: AiGeneratedImage[]\n /**\n * The tokens the requests spent beside the pictures themselves — the\n * prompt, and any thinking — which the meter prices at the image row's\n * token rates. The pictures are priced per picture, never as tokens here.\n */\n usage: AiUsage\n /**\n * How many of the requested pictures the provider's safety filter held\n * back. A picture held back is not returned and is never billed.\n */\n filtered: number\n}\n\n/**\n * The provider's safety filter declined the whole request: the description,\n * or every picture made from it. Nothing came back, so nothing is billed and\n * the reservation is handed back. `message` is the sentence a person reads.\n */\nexport class AiImageSafetyRefusal extends Error {\n override readonly name = 'AiImageSafetyRefusal'\n /** The provider's own reason codes, for the log and never for a reader. */\n readonly reasons: readonly string[]\n\n constructor(reasons: readonly string[] = []) {\n super(\n 'The image service declined this description. Try describing the scene differently, without real people, brands or anything unsafe.',\n )\n this.reasons = reasons\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\n/**\n * An image provider: one vendor's wire behind the contract. It reports\n * whether this deployment configured it, so a door can answer \"not here\"\n * before it reserves anything, and it maps its vendor's failures onto\n * `AiUpstreamError` and its declines onto `AiImageSafetyRefusal`.\n */\nexport interface AiImageProvider {\n readonly id: string\n readonly label: string\n /** The model a request runs on when nothing else names one. */\n defaultModel(): string\n /** True when this deployment's environment turns the provider on. */\n configured(): boolean\n /** The host requests go to, for the operator and the subprocessor record. */\n endpointHost(): string\n generate(request: AiImageRequest): Promise<AiImageResult>\n}\n"],"names":["AI_IMAGE_ASPECT_RATIOS","AI_IMAGE_SIZES","aiImageSizeForPlan","free","AI_IMAGE_MAX_COUNT","AI_IMAGE_PROMPT_MAX_CHARS","AI_IMAGE_MODES","AI_SVG_STYLES","AI_RASTER_STYLES","isAiRasterStyle","value","includes","AI_SVG_MAX_COLORS","aiImagePhotosOffered","process","env","NEXT_PUBLIC_AI_IMAGE_PHOTOS","isAiImageAspectRatio","AiImageSafetyRefusal","Error","reasons","name","Object","setPrototypeOf","prototype"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAID;;;;;;;;;;CAUC,GAED,sEAAsE,GACtE,OAAO,MAAMA,yBAAyB;IAAC;IAAO;IAAO;IAAO;IAAQ;CAAO,CAAS;AAGpF;;;;CAIC,GACD,OAAO,MAAMC,iBAAiB;IAAC;IAAO;CAAK,CAAS;AAGpD,sFAAsF,GACtF,OAAO,SAASC,mBAAmBC,IAAa;IAC9C,OAAOA,OAAO,QAAQ;AACxB;AAEA,yCAAyC,GACzC,OAAO,MAAMC,qBAAqB,EAAC;AAEnC,8DAA8D,GAC9D,OAAO,MAAMC,4BAA4B,KAAK;AAE9C;;;;;;CAMC,GACD,OAAO,MAAMC,iBAAiB;IAAC;IAAS;CAAe,CAAS;AAGhE,oFAAoF,GACpF,OAAO,MAAMC,gBAAgB;IAAC;IAAgB;IAAQ;IAAW;CAAO,CAAS;AAGjF;;;;CAIC,GACD,OAAO,MAAMC,mBAAmB;IAC9B;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACD,CAAS;AAGV,OAAO,SAASC,gBAAgBC,KAAc;IAC5C,OAAO,AAACF,iBAAwCG,QAAQ,CAACD;AAC3D;AAEA,2DAA2D,GAC3D,OAAO,MAAME,oBAAoB,EAAC;AAElC;;;;;;CAMC,GACD,OAAO,SAASC;IACd,OAAOC,QAAQC,GAAG,CAACC,2BAA2B,KAAK;AACrD;AAEA,OAAO,SAASC,qBAAqBP,KAAc;IACjD,OAAO,AAACV,uBAA8CW,QAAQ,CAACD;AACjE;AAuCA;;;;CAIC,GACD,OAAO,MAAMQ,6BAA6BC;IAKxC,YAAYC,UAA6B,EAAE,CAAE;QAC3C,KAAK,CACH,4IANcC,OAAO;QAQvB,IAAI,CAACD,OAAO,GAAGA;QACfE,OAAOC,cAAc,CAAC,IAAI,EAAE,WAAWC,SAAS;IAClD;AACF"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* The image providers' published hosts (AGL-3602), by provider id: what the
|
|
19
|
+
* subprocessor declarations pair with each `AI_IMAGE_CATALOG_PROVIDERS` row.
|
|
20
|
+
* Read from each adapter's endpoint module rather than the adapter itself,
|
|
21
|
+
* which loads the platform's service-account credential and has no business
|
|
22
|
+
* in the manifest generator that reads this.
|
|
23
|
+
*/
|
|
24
|
+
export declare const AI_IMAGE_PROVIDER_HOSTS: Readonly<Record<string, readonly string[]>>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import { AI_IMAGE_VERTEX_PROVIDER_ID } from "./catalog.js";
|
|
17
|
+
import { VERTEX_IMAGE_PUBLISHED_HOSTS } from "./vertex-image-endpoint.js";
|
|
18
|
+
/**
|
|
19
|
+
* The image providers' published hosts (AGL-3602), by provider id: what the
|
|
20
|
+
* subprocessor declarations pair with each `AI_IMAGE_CATALOG_PROVIDERS` row.
|
|
21
|
+
* Read from each adapter's endpoint module rather than the adapter itself,
|
|
22
|
+
* which loads the platform's service-account credential and has no business
|
|
23
|
+
* in the manifest generator that reads this.
|
|
24
|
+
*/ export const AI_IMAGE_PROVIDER_HOSTS = {
|
|
25
|
+
[AI_IMAGE_VERTEX_PROVIDER_ID]: VERTEX_IMAGE_PUBLISHED_HOSTS
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
//# sourceMappingURL=image-endpoints.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/providers/image-endpoints.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AI_IMAGE_VERTEX_PROVIDER_ID } from './catalog'\nimport { VERTEX_IMAGE_PUBLISHED_HOSTS } from './vertex-image-endpoint'\n\n/**\n * The image providers' published hosts (AGL-3602), by provider id: what the\n * subprocessor declarations pair with each `AI_IMAGE_CATALOG_PROVIDERS` row.\n * Read from each adapter's endpoint module rather than the adapter itself,\n * which loads the platform's service-account credential and has no business\n * in the manifest generator that reads this.\n */\nexport const AI_IMAGE_PROVIDER_HOSTS: Readonly<Record<string, readonly string[]>> = {\n [AI_IMAGE_VERTEX_PROVIDER_ID]: VERTEX_IMAGE_PUBLISHED_HOSTS,\n}\n"],"names":["AI_IMAGE_VERTEX_PROVIDER_ID","VERTEX_IMAGE_PUBLISHED_HOSTS","AI_IMAGE_PROVIDER_HOSTS"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,2BAA2B,QAAQ,eAAW;AACvD,SAASC,4BAA4B,QAAQ,6BAAyB;AAEtE;;;;;;CAMC,GACD,OAAO,MAAMC,0BAAuE;IAClF,CAACF,4BAA4B,EAAEC;AACjC,EAAC"}
|
|
@@ -182,9 +182,13 @@ import { resolveAiRoute } from "./routing.js";
|
|
|
182
182
|
cacheReadTokens: 0,
|
|
183
183
|
cacheWriteTokens: 0
|
|
184
184
|
},
|
|
185
|
+
// A layout: a tree layout's 1,000 output tokens until AGL-3660, now a
|
|
186
|
+
// site's header and footer in the layout language — 442 to 583 output
|
|
187
|
+
// tokens live on 2026-10-07 — with up to 1,500 more for the adaptive
|
|
188
|
+
// thinking it is asked with, an estimate until a workspace measures it.
|
|
185
189
|
'job.layout': {
|
|
186
190
|
inputTokens: 700,
|
|
187
|
-
outputTokens:
|
|
191
|
+
outputTokens: 2000,
|
|
188
192
|
cacheReadTokens: 5600,
|
|
189
193
|
cacheWriteTokens: 0
|
|
190
194
|
},
|
|
@@ -200,9 +204,14 @@ import { resolveAiRoute } from "./routing.js";
|
|
|
200
204
|
// the site inventory (711 on the median golden site), the brief, the plan
|
|
201
205
|
// and the section line ride uncached; and a golden section answer is 901
|
|
202
206
|
// characters at the median, asked for with no thinking.
|
|
207
|
+
// Since AGL-3660 a site's and a build's pages are one answer a page in
|
|
208
|
+
// the layout language: 683 to 1,745 output tokens live on 2026-10-07, about
|
|
209
|
+
// 1,150 at the median, with up to 1,500 more for the adaptive thinking it
|
|
210
|
+
// is asked with — an estimate until a workspace measures it, read high so
|
|
211
|
+
// the quote beside a model never undersells the exchange.
|
|
203
212
|
'job.page': {
|
|
204
213
|
inputTokens: 600,
|
|
205
|
-
outputTokens:
|
|
214
|
+
outputTokens: 2650,
|
|
206
215
|
cacheReadTokens: 4400,
|
|
207
216
|
cacheWriteTokens: 0
|
|
208
217
|
},
|
|
@@ -307,6 +316,18 @@ import { resolveAiRoute } from "./routing.js";
|
|
|
307
316
|
outputTokens: 900,
|
|
308
317
|
cacheReadTokens: 1500,
|
|
309
318
|
cacheWriteTokens: 0
|
|
319
|
+
},
|
|
320
|
+
// The edit step (AGL-3616): the document doctrine, the job's rules and the
|
|
321
|
+
// edit rung's protocol and catalog for the document's kind are the cached
|
|
322
|
+
// prefix (5,970 tokens for a page, as `runtime/ai-prompt-cache.spec.ts`
|
|
323
|
+
// measures it); the outline of up to sixty elements with their settings
|
|
324
|
+
// and the request ride uncached, and a change of a few operations is about
|
|
325
|
+
// 1,500 characters of tool call with as much again to think in.
|
|
326
|
+
'job.edit': {
|
|
327
|
+
inputTokens: 2500,
|
|
328
|
+
outputTokens: 1000,
|
|
329
|
+
cacheReadTokens: 6000,
|
|
330
|
+
cacheWriteTokens: 0
|
|
310
331
|
}
|
|
311
332
|
};
|
|
312
333
|
/** The fewest measured exchanges a median is taken over. */ export const AI_MEASURED_MEDIAN_MIN_SAMPLES = 3;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/providers/model-choice.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { assistCreditsFromUsd } from '../usage/assist-credits'\nimport type { OrgPlan } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport {\n AI_MODEL_CATALOG,\n AI_STEP_TIERS,\n aiCatalogEntry,\n aiBilledRatesForModel,\n estimateAiBilledUsd,\n type AiCatalogEntry,\n type AiStepKind,\n} from './catalog'\nimport type { AiUsage } from './contract'\nimport { resolveAiRoute, type AiPluginSettings } from './routing'\n\n/**\n * THE MODEL SWITCH (AGL-2942): what a person may pick, what \"Auto\" means,\n * and what a pick costs.\n *\n * ## Auto is the routing table\n *\n * A request that names no model — or names one it may not have — runs on\n * `resolveAiRoute(kind)`, the table that maps a step kind to a catalog model\n * (AGL-2937). Picking a model is an override of that one row for one request,\n * never a second table.\n *\n * ## Three bounds, one intersection\n *\n * A manual pick must be served by the active provider, in a tier the org's\n * plan offers (`AI_PLAN_MODEL_TIERS`), on the org's own restriction when it\n * set one, and on the allotment allowlists that apply to the request. The\n * two allowlists bound Auto too: when the routing table's answer is not on\n * them, Auto runs on the allowed model of the same tier, else the cheapest\n * allowed one. The plan bounds only a pick — Auto is the platform's choice,\n * made for the step, and a plan never makes it worse.\n *\n * An allowlist that leaves nothing this deployment serves is not a way to\n * switch AI off by mistake: the choice falls back to the routing table, and\n * the permission keys are how AI is switched off on purpose.\n *\n * ## The price beside each option\n *\n * Credits per typical request at each model's list rates, over the step's\n * measured median exchange when the workspace has enough of them, and over\n * `AI_STEP_NOMINAL_USAGE` until it does — with the multiple of what Auto\n * would cost for the same request. Every figure is credits, never dollars.\n */\n\n/** The value a request sends for \"let the platform route it\". */\nexport const AI_MODEL_AUTO = 'auto'\n\n/**\n * The catalog tiers a MANUAL pick may name, per plan. Free runs on Auto only:\n * its taste is a wall, and a pick there would spend the account's credits at\n * a rate nobody chose on purpose. The dearest tier opens from Scale up, where\n * the band is large enough that a slower, deeper model is a real choice\n * rather than a fast way to end the month.\n */\nexport const AI_PLAN_MODEL_TIERS: Record<OrgPlan, readonly AiCatalogEntry['tier'][]> = {\n free: [],\n starter: ['fast', 'balanced'],\n pro: ['fast', 'balanced'],\n business: ['fast', 'balanced'],\n scale: ['fast', 'balanced', 'deep'],\n advanced: ['fast', 'balanced', 'deep'],\n agency: ['fast', 'balanced', 'deep'],\n enterprise: ['fast', 'balanced', 'deep'],\n}\n\n/** The plans whose admins may restrict the model set org-wide. */\nexport const AI_MODEL_RESTRICTION_PLANS: readonly OrgPlan[] = ['agency', 'enterprise']\n\n/**\n * A typical exchange per step kind, before a workspace has measured one: the\n * doors' own prompt sizes and output ceilings, read conservatively. Replaced\n * by the workspace's measured median as soon as `aiMedianUsage` has enough\n * samples, and only ever used for the price beside an option.\n */\nexport const AI_STEP_NOMINAL_USAGE: Record<AiStepKind, AiUsage> = {\n 'assist.chat': { inputTokens: 900, outputTokens: 450, cacheReadTokens: 1_050, cacheWriteTokens: 0 },\n 'copy.element': { inputTokens: 350, outputTokens: 120, cacheReadTokens: 0, cacheWriteTokens: 0 },\n 'copy.section': { inputTokens: 700, outputTokens: 1_400, cacheReadTokens: 0, cacheWriteTokens: 0 },\n 'copy.blog': { inputTokens: 600, outputTokens: 1_100, cacheReadTokens: 0, cacheWriteTokens: 0 },\n 'generate.section': { inputTokens: 700, outputTokens: 1_400, cacheReadTokens: 0, cacheWriteTokens: 0 },\n // The text step's rules are about 500 characters — far under the balanced\n // tier's cacheable minimum, so they are billed as input every time rather\n // than read from a cache (`runtime/ai-prompt-cache.spec.ts` measures it).\n 'job.text': { inputTokens: 450, outputTokens: 500, cacheReadTokens: 0, cacheWriteTokens: 0 },\n // The theme step: the tool's schema and both system blocks (about 7,500\n // characters) are the cached prefix, the site's inventory and the brief ride\n // uncached, and a full-palette answer is about 1,300 characters of JSON with\n // as much again to think in.\n 'job.theme': { inputTokens: 500, outputTokens: 850, cacheReadTokens: 2_500, cacheWriteTokens: 0 },\n // The plan step: the tool's schema, the doctrine and the plan instructions\n // (about 10,000 characters) are the cached prefix, the site inventory and the\n // brief ride uncached, and a plan answer is about 600 characters of JSON with\n // as much again to think in.\n 'job.plan': { inputTokens: 500, outputTokens: 400, cacheReadTokens: 3_600, cacheWriteTokens: 0 },\n // The component step: the doctrine, the component instructions, the\n // component surface's catalog and the tool are the cached prefix, the site\n // inventory, the brief and the confirmed plan ride uncached, and a card-sized\n // answer — its tree and declared props as JSON — is about 1,700 characters.\n // The component step's spec measures the prefix and the answer against this.\n 'job.component': { inputTokens: 700, outputTokens: 600, cacheReadTokens: 5_800, cacheWriteTokens: 0 },\n // The CRM step's exchange (AGL-2917). NOTHING is cached: the step runs on\n // the fast tier, whose cacheable minimum is far above what its prompt\n // reaches — the doctrine's field rules, the CRM rules and the tool come to\n // 650 to 913 tokens, as `runtime/ai-prompt-cache.spec.ts` measures — so\n // every byte is billed as input. A record's facts ride with them, a\n // timeline cut to its newest twelve entries, and the typical answer is a\n // summary and a next step of a few sentences, with no thinking.\n 'job.crm': { inputTokens: 1_800, outputTokens: 250, cacheReadTokens: 0, cacheWriteTokens: 0 },\n 'job.layout': { inputTokens: 700, outputTokens: 1_000, cacheReadTokens: 5_600, cacheWriteTokens: 0 },\n 'job.template': { inputTokens: 1_200, outputTokens: 700, cacheReadTokens: 6_400, cacheWriteTokens: 0 },\n // One page section's exchange (AGL-2907), from the modules' own text: the\n // doctrine (6,281 characters), the page instructions (1,524), the screen\n // palette catalog (8,353) and the section tool (484) are the cached prefix;\n // the site inventory (711 on the median golden site), the brief, the plan\n // and the section line ride uncached; and a golden section answer is 901\n // characters at the median, asked for with no thinking.\n 'job.page': { inputTokens: 600, outputTokens: 300, cacheReadTokens: 4_400, cacheWriteTokens: 0 },\n // One product's copy (AGL-2916), the exchange a bulk job makes once a\n // product: the doctrine, the copy rules and the tool (about 9,500 characters)\n // are the cached prefix; the product's facts, the site's categories and a\n // photo at most 768 pixels on its long edge (about 790 tokens) ride\n // uncached; and the answer is a description with its search listing, tags,\n // categories and option names, about 1,300 characters of JSON.\n 'job.products': { inputTokens: 1_500, outputTokens: 450, cacheReadTokens: 2_400, cacheWriteTokens: 0 },\n // The SEO step's listing exchange, which a page's or a product's listing is\n // exactly one of. NOTHING is cached here, whatever the breakpoints say: the\n // step runs on the fast tier, whose cacheable minimum is four times what\n // this prompt reaches, so every static byte is billed as input on every\n // attempt — the doctrine's block in its field scope, the listing rules and\n // the tool's schema, about 760 tokens together. The page text, capped at\n // 2,000 characters, rides with the current listing and a dozen other titles\n // for about another 750. The largest answer the tool accepts is about 600\n // characters of JSON, and the request asks for no thinking. An audit pass\n // is heavier: eight pages' findings and text, about 9,000 characters, with\n // up to about 8,000 back.\n 'job.seo': { inputTokens: 1_500, outputTokens: 250, cacheReadTokens: 0, cacheWriteTokens: 0 },\n // The experiment step (AGL-2914). NOTHING is cached: the A/B test rules\n // and the acceptable-use block come to about 2,300 characters, under the\n // balanced tier's cacheable minimum, so they are billed as input every\n // time. The copy under test, or a test's arms with its verdict, rides with\n // them, and a four-variant answer is about 800 characters of JSON.\n 'job.experiment': { inputTokens: 900, outputTokens: 300, cacheReadTokens: 0, cacheWriteTokens: 0 },\n // The form step: the doctrine, the form instructions, the form surface's\n // catalog (about 150 tokens) and the tool's schema are the cached prefix, the\n // site inventory and the brief ride uncached, and a form's answer is 200 to\n // 400 tokens of JSON with no extended thinking.\n 'job.form': { inputTokens: 700, outputTokens: 450, cacheReadTokens: 3_900, cacheWriteTokens: 0 },\n // An insight (AGL-2915): two exchanges, and NOTHING cached — the rules and\n // the acceptable-use block are under the balanced tier's minimum, so they\n // are billed as input on both. The read call carries the question and the\n // readers' catalog (about 2,000 characters) and answers a few reads; the\n // answer call carries four tables of about ten rows each (about 4,000\n // characters) and answers three to five insights as JSON.\n 'job.insight': { inputTokens: 2_800, outputTokens: 650, cacheReadTokens: 0, cacheWriteTokens: 0 },\n // The email step, and the campaign step that shares its generation: the\n // doctrine, the email instructions, the email palette catalog and the\n // tool's schema (about 10,300 characters) are the cached prefix, the site\n // inventory and the brief ride uncached, and the answer is one email's\n // node map with three subject lines and three preheaders.\n 'job.email': { inputTokens: 600, outputTokens: 1_500, cacheReadTokens: 3_600, cacheWriteTokens: 0 },\n 'job.campaign': { inputTokens: 600, outputTokens: 1_500, cacheReadTokens: 3_600, cacheWriteTokens: 0 },\n // The workflow step's draft (AGL-2919): the doctrine, the automation\n // vocabulary and the tool's schema are the cached prefix (4,564 tokens, as\n // `runtime/ai-prompt-cache.spec.ts` measures it); what the workspace can\n // run, the site's forms and datasets and the description ride uncached; and\n // an automation of a few steps is about 1,300 characters of JSON with as\n // much again to think in. An explanation caches half as much.\n 'job.workflow': { inputTokens: 500, outputTokens: 1_000, cacheReadTokens: 4_500, cacheWriteTokens: 0 },\n // The logic step (AGL-3603): the fields doctrine, one rules block and the\n // tool's schema are the cached prefix (1,556 tokens for a function, as\n // `runtime/ai-prompt-cache.spec.ts` measures it); the site's variables and\n // the request ride uncached, and a function of a few operations is about\n // 1,200 characters of JSON with as much again to think in.\n 'job.logic': { inputTokens: 700, outputTokens: 900, cacheReadTokens: 1_500, cacheWriteTokens: 0 },\n}\n\n/** The fewest measured exchanges a median is taken over. */\nexport const AI_MEASURED_MEDIAN_MIN_SAMPLES = 3\n\nexport interface AiModelBounds {\n plan: OrgPlan\n /** The allotment allowlists that apply, intersected; `null` for none. */\n allotmentModels?: readonly string[] | null\n /** The org-wide restriction; `null` for none. */\n orgModels?: readonly string[] | null\n}\n\nfunction onLists(entry: AiCatalogEntry, bounds: AiModelBounds): boolean {\n if (bounds.orgModels?.length && !bounds.orgModels.includes(entry.id)) return false\n if (bounds.allotmentModels && !bounds.allotmentModels.includes(entry.id)) return false\n return true\n}\n\n/** The catalog models a manual pick may name on a provider under these bounds. */\nexport function aiModelsSelectable(\n providerId: string,\n bounds: AiModelBounds,\n): AiCatalogEntry[] {\n const tiers = AI_PLAN_MODEL_TIERS[bounds.plan] ?? []\n return AI_MODEL_CATALOG.filter(\n (entry) =>\n entry.provider === providerId && tiers.includes(entry.tier) && onLists(entry, bounds),\n )\n}\n\n/**\n * A model's blended BILLED rate, for \"the cheapest allowed\" and nothing\n * else.\n *\n * Billed and not provider (AGL-3015): the substitution below happens when a\n * workspace asked for a model its bounds do not allow, and the party it\n * falls back for is the one whose credits are drawn. \"Cheapest\" has to mean\n * the same thing here as in the selector this substitution stands in for,\n * which sorts on `creditsPerRequest`.\n */\nconst blendedRate = (id: string): number => {\n const rates = aiBilledRatesForModel(id)\n return rates.inputPerToken + rates.outputPerToken\n}\n\nexport interface AiModelChoice {\n /** The catalog id the request runs, and is metered, on. */\n model: string\n /** True when the routing table decided: Auto, or a pick that was declined. */\n auto: boolean\n /** A pick that was asked for and is not allowed, for the envelope. */\n declined: string | null\n}\n\n/**\n * The model one request runs on, or `null` when no provider is registered —\n * which every door has refused before it asks.\n */\nexport function resolveAiModelChoice(\n kind: AiStepKind,\n requested: unknown,\n bounds: AiModelBounds,\n settings?: AiPluginSettings,\n): AiModelChoice | null {\n const route = resolveAiRoute(kind, settings)\n if (!route) return null\n const wanted =\n typeof requested === 'string' && requested.trim() && requested.trim() !== AI_MODEL_AUTO\n ? requested.trim()\n : null\n if (wanted && aiModelsSelectable(route.provider.id, bounds).some((entry) => entry.id === wanted)) {\n return { model: wanted, auto: false, declined: null }\n }\n const restricted = Boolean(bounds.orgModels?.length) || Boolean(bounds.allotmentModels)\n let model = route.model\n if (restricted) {\n const allowed = AI_MODEL_CATALOG.filter(\n (entry) => entry.provider === route.provider.id && onLists(entry, bounds),\n )\n if (allowed.length && !allowed.some((entry) => entry.id === model)) {\n const tier = aiCatalogEntry(model)?.tier ?? AI_STEP_TIERS[kind]\n const sameTier = allowed.find((entry) => entry.tier === tier)\n const cheapest = [...allowed].sort((a, b) => blendedRate(a.id) - blendedRate(b.id))[0]\n model = (sameTier ?? cheapest).id\n }\n }\n return { model, auto: true, declined: wanted }\n}\n\n/** The median exchange over measured samples, or `null` below the minimum. */\nexport function aiMedianUsage(samples: readonly AiUsage[]): AiUsage | null {\n if (samples.length < AI_MEASURED_MEDIAN_MIN_SAMPLES) return null\n const median = (field: keyof AiUsage): number => {\n const values = samples\n .map((sample) => Number(sample[field] ?? 0))\n .filter((value) => Number.isFinite(value) && value >= 0)\n .sort((a, b) => a - b)\n if (!values.length) return 0\n const middle = Math.floor(values.length / 2)\n return values.length % 2\n ? values[middle]\n : Math.round((values[middle - 1] + values[middle]) / 2)\n }\n return {\n inputTokens: median('inputTokens'),\n outputTokens: median('outputTokens'),\n cacheReadTokens: median('cacheReadTokens'),\n cacheWriteTokens: median('cacheWriteTokens'),\n }\n}\n\nexport interface AiModelOption {\n /** A catalog id, or `AI_MODEL_AUTO`. */\n id: string\n label: string\n tier: AiCatalogEntry['tier']\n /** Credits a typical request of this kind costs on the option. */\n creditsPerRequest: number\n /** That cost as a multiple of Auto's, to one decimal. */\n multiplier: number\n}\n\nexport interface AiModelOptions {\n /** Auto, labeled with the model it routes this kind to under these bounds. */\n auto: AiModelOption & { model: string }\n /** The manual picks, cheapest first; empty where the plan offers none. */\n options: AiModelOption[]\n /** Whether the price came from the workspace's own measured exchanges. */\n measured: boolean\n}\n\n/** What the selector lists for one step kind, or `null` without a provider. */\nexport function aiModelOptions(\n kind: AiStepKind,\n bounds: AiModelBounds,\n measured: AiUsage | null,\n settings?: AiPluginSettings,\n): AiModelOptions | null {\n const route = resolveAiRoute(kind, settings)\n const auto = resolveAiModelChoice(kind, null, bounds, settings)\n if (!route || !auto) return null\n const typical = measured ?? AI_STEP_NOMINAL_USAGE[kind]\n // Billed rates throughout (AGL-3015): every figure below is what the\n // workspace will be charged for picking this model — credits per request\n // and the multiple of Auto those credits come to. Our own cost for the\n // same request is not this selector's subject and would misstate both.\n const autoCost = estimateAiBilledUsd(typical, auto.model)\n const priced = (entry: AiCatalogEntry): AiModelOption => {\n const cost = estimateAiBilledUsd(typical, entry.id)\n return {\n id: entry.id,\n label: entry.label,\n tier: entry.tier,\n creditsPerRequest: assistCreditsFromUsd(cost),\n multiplier: autoCost > 0 ? Math.round((cost / autoCost) * 10) / 10 : 1,\n }\n }\n const autoEntry = aiCatalogEntry(auto.model)\n return {\n auto: {\n id: AI_MODEL_AUTO,\n label: autoEntry ? `Auto (${autoEntry.label})` : 'Auto',\n tier: autoEntry?.tier ?? AI_STEP_TIERS[kind],\n creditsPerRequest: assistCreditsFromUsd(autoCost),\n multiplier: 1,\n model: auto.model,\n },\n options: aiModelsSelectable(route.provider.id, bounds)\n .map(priced)\n .sort((a, b) => a.creditsPerRequest - b.creditsPerRequest || a.label.localeCompare(b.label)),\n measured: measured !== null,\n }\n}\n"],"names":["assistCreditsFromUsd","AI_MODEL_CATALOG","AI_STEP_TIERS","aiCatalogEntry","aiBilledRatesForModel","estimateAiBilledUsd","resolveAiRoute","AI_MODEL_AUTO","AI_PLAN_MODEL_TIERS","free","starter","pro","business","scale","advanced","agency","enterprise","AI_MODEL_RESTRICTION_PLANS","AI_STEP_NOMINAL_USAGE","inputTokens","outputTokens","cacheReadTokens","cacheWriteTokens","AI_MEASURED_MEDIAN_MIN_SAMPLES","onLists","entry","bounds","orgModels","length","includes","id","allotmentModels","aiModelsSelectable","providerId","tiers","plan","filter","provider","tier","blendedRate","rates","inputPerToken","outputPerToken","resolveAiModelChoice","kind","requested","settings","route","wanted","trim","some","model","auto","declined","restricted","Boolean","allowed","sameTier","find","cheapest","sort","a","b","aiMedianUsage","samples","median","field","values","map","sample","Number","value","isFinite","middle","Math","floor","round","aiModelOptions","measured","typical","autoCost","priced","cost","label","creditsPerRequest","multiplier","autoEntry","options","localeCompare"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/providers/model-choice.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { assistCreditsFromUsd } from '../usage/assist-credits'\nimport type { OrgPlan } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport {\n AI_MODEL_CATALOG,\n AI_STEP_TIERS,\n aiCatalogEntry,\n aiBilledRatesForModel,\n estimateAiBilledUsd,\n type AiCatalogEntry,\n type AiStepKind,\n} from './catalog'\nimport type { AiUsage } from './contract'\nimport { resolveAiRoute, type AiPluginSettings } from './routing'\n\n/**\n * THE MODEL SWITCH (AGL-2942): what a person may pick, what \"Auto\" means,\n * and what a pick costs.\n *\n * ## Auto is the routing table\n *\n * A request that names no model — or names one it may not have — runs on\n * `resolveAiRoute(kind)`, the table that maps a step kind to a catalog model\n * (AGL-2937). Picking a model is an override of that one row for one request,\n * never a second table.\n *\n * ## Three bounds, one intersection\n *\n * A manual pick must be served by the active provider, in a tier the org's\n * plan offers (`AI_PLAN_MODEL_TIERS`), on the org's own restriction when it\n * set one, and on the allotment allowlists that apply to the request. The\n * two allowlists bound Auto too: when the routing table's answer is not on\n * them, Auto runs on the allowed model of the same tier, else the cheapest\n * allowed one. The plan bounds only a pick — Auto is the platform's choice,\n * made for the step, and a plan never makes it worse.\n *\n * An allowlist that leaves nothing this deployment serves is not a way to\n * switch AI off by mistake: the choice falls back to the routing table, and\n * the permission keys are how AI is switched off on purpose.\n *\n * ## The price beside each option\n *\n * Credits per typical request at each model's list rates, over the step's\n * measured median exchange when the workspace has enough of them, and over\n * `AI_STEP_NOMINAL_USAGE` until it does — with the multiple of what Auto\n * would cost for the same request. Every figure is credits, never dollars.\n */\n\n/** The value a request sends for \"let the platform route it\". */\nexport const AI_MODEL_AUTO = 'auto'\n\n/**\n * The catalog tiers a MANUAL pick may name, per plan. Free runs on Auto only:\n * its taste is a wall, and a pick there would spend the account's credits at\n * a rate nobody chose on purpose. The dearest tier opens from Scale up, where\n * the band is large enough that a slower, deeper model is a real choice\n * rather than a fast way to end the month.\n */\nexport const AI_PLAN_MODEL_TIERS: Record<OrgPlan, readonly AiCatalogEntry['tier'][]> = {\n free: [],\n starter: ['fast', 'balanced'],\n pro: ['fast', 'balanced'],\n business: ['fast', 'balanced'],\n scale: ['fast', 'balanced', 'deep'],\n advanced: ['fast', 'balanced', 'deep'],\n agency: ['fast', 'balanced', 'deep'],\n enterprise: ['fast', 'balanced', 'deep'],\n}\n\n/** The plans whose admins may restrict the model set org-wide. */\nexport const AI_MODEL_RESTRICTION_PLANS: readonly OrgPlan[] = ['agency', 'enterprise']\n\n/**\n * A typical exchange per step kind, before a workspace has measured one: the\n * doors' own prompt sizes and output ceilings, read conservatively. Replaced\n * by the workspace's measured median as soon as `aiMedianUsage` has enough\n * samples, and only ever used for the price beside an option.\n */\nexport const AI_STEP_NOMINAL_USAGE: Record<AiStepKind, AiUsage> = {\n 'assist.chat': { inputTokens: 900, outputTokens: 450, cacheReadTokens: 1_050, cacheWriteTokens: 0 },\n 'copy.element': { inputTokens: 350, outputTokens: 120, cacheReadTokens: 0, cacheWriteTokens: 0 },\n 'copy.section': { inputTokens: 700, outputTokens: 1_400, cacheReadTokens: 0, cacheWriteTokens: 0 },\n 'copy.blog': { inputTokens: 600, outputTokens: 1_100, cacheReadTokens: 0, cacheWriteTokens: 0 },\n 'generate.section': { inputTokens: 700, outputTokens: 1_400, cacheReadTokens: 0, cacheWriteTokens: 0 },\n // The text step's rules are about 500 characters — far under the balanced\n // tier's cacheable minimum, so they are billed as input every time rather\n // than read from a cache (`runtime/ai-prompt-cache.spec.ts` measures it).\n 'job.text': { inputTokens: 450, outputTokens: 500, cacheReadTokens: 0, cacheWriteTokens: 0 },\n // The theme step: the tool's schema and both system blocks (about 7,500\n // characters) are the cached prefix, the site's inventory and the brief ride\n // uncached, and a full-palette answer is about 1,300 characters of JSON with\n // as much again to think in.\n 'job.theme': { inputTokens: 500, outputTokens: 850, cacheReadTokens: 2_500, cacheWriteTokens: 0 },\n // The plan step: the tool's schema, the doctrine and the plan instructions\n // (about 10,000 characters) are the cached prefix, the site inventory and the\n // brief ride uncached, and a plan answer is about 600 characters of JSON with\n // as much again to think in.\n 'job.plan': { inputTokens: 500, outputTokens: 400, cacheReadTokens: 3_600, cacheWriteTokens: 0 },\n // The component step: the doctrine, the component instructions, the\n // component surface's catalog and the tool are the cached prefix, the site\n // inventory, the brief and the confirmed plan ride uncached, and a card-sized\n // answer — its tree and declared props as JSON — is about 1,700 characters.\n // The component step's spec measures the prefix and the answer against this.\n 'job.component': { inputTokens: 700, outputTokens: 600, cacheReadTokens: 5_800, cacheWriteTokens: 0 },\n // The CRM step's exchange (AGL-2917). NOTHING is cached: the step runs on\n // the fast tier, whose cacheable minimum is far above what its prompt\n // reaches — the doctrine's field rules, the CRM rules and the tool come to\n // 650 to 913 tokens, as `runtime/ai-prompt-cache.spec.ts` measures — so\n // every byte is billed as input. A record's facts ride with them, a\n // timeline cut to its newest twelve entries, and the typical answer is a\n // summary and a next step of a few sentences, with no thinking.\n 'job.crm': { inputTokens: 1_800, outputTokens: 250, cacheReadTokens: 0, cacheWriteTokens: 0 },\n // A layout: a tree layout's 1,000 output tokens until AGL-3660, now a\n // site's header and footer in the layout language — 442 to 583 output\n // tokens live on 2026-10-07 — with up to 1,500 more for the adaptive\n // thinking it is asked with, an estimate until a workspace measures it.\n 'job.layout': { inputTokens: 700, outputTokens: 2_000, cacheReadTokens: 5_600, cacheWriteTokens: 0 },\n 'job.template': { inputTokens: 1_200, outputTokens: 700, cacheReadTokens: 6_400, cacheWriteTokens: 0 },\n // One page section's exchange (AGL-2907), from the modules' own text: the\n // doctrine (6,281 characters), the page instructions (1,524), the screen\n // palette catalog (8,353) and the section tool (484) are the cached prefix;\n // the site inventory (711 on the median golden site), the brief, the plan\n // and the section line ride uncached; and a golden section answer is 901\n // characters at the median, asked for with no thinking.\n // Since AGL-3660 a site's and a build's pages are one answer a page in\n // the layout language: 683 to 1,745 output tokens live on 2026-10-07, about\n // 1,150 at the median, with up to 1,500 more for the adaptive thinking it\n // is asked with — an estimate until a workspace measures it, read high so\n // the quote beside a model never undersells the exchange.\n 'job.page': { inputTokens: 600, outputTokens: 2_650, cacheReadTokens: 4_400, cacheWriteTokens: 0 },\n // One product's copy (AGL-2916), the exchange a bulk job makes once a\n // product: the doctrine, the copy rules and the tool (about 9,500 characters)\n // are the cached prefix; the product's facts, the site's categories and a\n // photo at most 768 pixels on its long edge (about 790 tokens) ride\n // uncached; and the answer is a description with its search listing, tags,\n // categories and option names, about 1,300 characters of JSON.\n 'job.products': { inputTokens: 1_500, outputTokens: 450, cacheReadTokens: 2_400, cacheWriteTokens: 0 },\n // The SEO step's listing exchange, which a page's or a product's listing is\n // exactly one of. NOTHING is cached here, whatever the breakpoints say: the\n // step runs on the fast tier, whose cacheable minimum is four times what\n // this prompt reaches, so every static byte is billed as input on every\n // attempt — the doctrine's block in its field scope, the listing rules and\n // the tool's schema, about 760 tokens together. The page text, capped at\n // 2,000 characters, rides with the current listing and a dozen other titles\n // for about another 750. The largest answer the tool accepts is about 600\n // characters of JSON, and the request asks for no thinking. An audit pass\n // is heavier: eight pages' findings and text, about 9,000 characters, with\n // up to about 8,000 back.\n 'job.seo': { inputTokens: 1_500, outputTokens: 250, cacheReadTokens: 0, cacheWriteTokens: 0 },\n // The experiment step (AGL-2914). NOTHING is cached: the A/B test rules\n // and the acceptable-use block come to about 2,300 characters, under the\n // balanced tier's cacheable minimum, so they are billed as input every\n // time. The copy under test, or a test's arms with its verdict, rides with\n // them, and a four-variant answer is about 800 characters of JSON.\n 'job.experiment': { inputTokens: 900, outputTokens: 300, cacheReadTokens: 0, cacheWriteTokens: 0 },\n // The form step: the doctrine, the form instructions, the form surface's\n // catalog (about 150 tokens) and the tool's schema are the cached prefix, the\n // site inventory and the brief ride uncached, and a form's answer is 200 to\n // 400 tokens of JSON with no extended thinking.\n 'job.form': { inputTokens: 700, outputTokens: 450, cacheReadTokens: 3_900, cacheWriteTokens: 0 },\n // An insight (AGL-2915): two exchanges, and NOTHING cached — the rules and\n // the acceptable-use block are under the balanced tier's minimum, so they\n // are billed as input on both. The read call carries the question and the\n // readers' catalog (about 2,000 characters) and answers a few reads; the\n // answer call carries four tables of about ten rows each (about 4,000\n // characters) and answers three to five insights as JSON.\n 'job.insight': { inputTokens: 2_800, outputTokens: 650, cacheReadTokens: 0, cacheWriteTokens: 0 },\n // The email step, and the campaign step that shares its generation: the\n // doctrine, the email instructions, the email palette catalog and the\n // tool's schema (about 10,300 characters) are the cached prefix, the site\n // inventory and the brief ride uncached, and the answer is one email's\n // node map with three subject lines and three preheaders.\n 'job.email': { inputTokens: 600, outputTokens: 1_500, cacheReadTokens: 3_600, cacheWriteTokens: 0 },\n 'job.campaign': { inputTokens: 600, outputTokens: 1_500, cacheReadTokens: 3_600, cacheWriteTokens: 0 },\n // The workflow step's draft (AGL-2919): the doctrine, the automation\n // vocabulary and the tool's schema are the cached prefix (4,564 tokens, as\n // `runtime/ai-prompt-cache.spec.ts` measures it); what the workspace can\n // run, the site's forms and datasets and the description ride uncached; and\n // an automation of a few steps is about 1,300 characters of JSON with as\n // much again to think in. An explanation caches half as much.\n 'job.workflow': { inputTokens: 500, outputTokens: 1_000, cacheReadTokens: 4_500, cacheWriteTokens: 0 },\n // The logic step (AGL-3603): the fields doctrine, one rules block and the\n // tool's schema are the cached prefix (1,556 tokens for a function, as\n // `runtime/ai-prompt-cache.spec.ts` measures it); the site's variables and\n // the request ride uncached, and a function of a few operations is about\n // 1,200 characters of JSON with as much again to think in.\n 'job.logic': { inputTokens: 700, outputTokens: 900, cacheReadTokens: 1_500, cacheWriteTokens: 0 },\n // The edit step (AGL-3616): the document doctrine, the job's rules and the\n // edit rung's protocol and catalog for the document's kind are the cached\n // prefix (5,970 tokens for a page, as `runtime/ai-prompt-cache.spec.ts`\n // measures it); the outline of up to sixty elements with their settings\n // and the request ride uncached, and a change of a few operations is about\n // 1,500 characters of tool call with as much again to think in.\n 'job.edit': { inputTokens: 2_500, outputTokens: 1_000, cacheReadTokens: 6_000, cacheWriteTokens: 0 },\n}\n\n/** The fewest measured exchanges a median is taken over. */\nexport const AI_MEASURED_MEDIAN_MIN_SAMPLES = 3\n\nexport interface AiModelBounds {\n plan: OrgPlan\n /** The allotment allowlists that apply, intersected; `null` for none. */\n allotmentModels?: readonly string[] | null\n /** The org-wide restriction; `null` for none. */\n orgModels?: readonly string[] | null\n}\n\nfunction onLists(entry: AiCatalogEntry, bounds: AiModelBounds): boolean {\n if (bounds.orgModels?.length && !bounds.orgModels.includes(entry.id)) return false\n if (bounds.allotmentModels && !bounds.allotmentModels.includes(entry.id)) return false\n return true\n}\n\n/** The catalog models a manual pick may name on a provider under these bounds. */\nexport function aiModelsSelectable(\n providerId: string,\n bounds: AiModelBounds,\n): AiCatalogEntry[] {\n const tiers = AI_PLAN_MODEL_TIERS[bounds.plan] ?? []\n return AI_MODEL_CATALOG.filter(\n (entry) =>\n entry.provider === providerId && tiers.includes(entry.tier) && onLists(entry, bounds),\n )\n}\n\n/**\n * A model's blended BILLED rate, for \"the cheapest allowed\" and nothing\n * else.\n *\n * Billed and not provider (AGL-3015): the substitution below happens when a\n * workspace asked for a model its bounds do not allow, and the party it\n * falls back for is the one whose credits are drawn. \"Cheapest\" has to mean\n * the same thing here as in the selector this substitution stands in for,\n * which sorts on `creditsPerRequest`.\n */\nconst blendedRate = (id: string): number => {\n const rates = aiBilledRatesForModel(id)\n return rates.inputPerToken + rates.outputPerToken\n}\n\nexport interface AiModelChoice {\n /** The catalog id the request runs, and is metered, on. */\n model: string\n /** True when the routing table decided: Auto, or a pick that was declined. */\n auto: boolean\n /** A pick that was asked for and is not allowed, for the envelope. */\n declined: string | null\n}\n\n/**\n * The model one request runs on, or `null` when no provider is registered —\n * which every door has refused before it asks.\n */\nexport function resolveAiModelChoice(\n kind: AiStepKind,\n requested: unknown,\n bounds: AiModelBounds,\n settings?: AiPluginSettings,\n): AiModelChoice | null {\n const route = resolveAiRoute(kind, settings)\n if (!route) return null\n const wanted =\n typeof requested === 'string' && requested.trim() && requested.trim() !== AI_MODEL_AUTO\n ? requested.trim()\n : null\n if (wanted && aiModelsSelectable(route.provider.id, bounds).some((entry) => entry.id === wanted)) {\n return { model: wanted, auto: false, declined: null }\n }\n const restricted = Boolean(bounds.orgModels?.length) || Boolean(bounds.allotmentModels)\n let model = route.model\n if (restricted) {\n const allowed = AI_MODEL_CATALOG.filter(\n (entry) => entry.provider === route.provider.id && onLists(entry, bounds),\n )\n if (allowed.length && !allowed.some((entry) => entry.id === model)) {\n const tier = aiCatalogEntry(model)?.tier ?? AI_STEP_TIERS[kind]\n const sameTier = allowed.find((entry) => entry.tier === tier)\n const cheapest = [...allowed].sort((a, b) => blendedRate(a.id) - blendedRate(b.id))[0]\n model = (sameTier ?? cheapest).id\n }\n }\n return { model, auto: true, declined: wanted }\n}\n\n/** The median exchange over measured samples, or `null` below the minimum. */\nexport function aiMedianUsage(samples: readonly AiUsage[]): AiUsage | null {\n if (samples.length < AI_MEASURED_MEDIAN_MIN_SAMPLES) return null\n const median = (field: keyof AiUsage): number => {\n const values = samples\n .map((sample) => Number(sample[field] ?? 0))\n .filter((value) => Number.isFinite(value) && value >= 0)\n .sort((a, b) => a - b)\n if (!values.length) return 0\n const middle = Math.floor(values.length / 2)\n return values.length % 2\n ? values[middle]\n : Math.round((values[middle - 1] + values[middle]) / 2)\n }\n return {\n inputTokens: median('inputTokens'),\n outputTokens: median('outputTokens'),\n cacheReadTokens: median('cacheReadTokens'),\n cacheWriteTokens: median('cacheWriteTokens'),\n }\n}\n\nexport interface AiModelOption {\n /** A catalog id, or `AI_MODEL_AUTO`. */\n id: string\n label: string\n tier: AiCatalogEntry['tier']\n /** Credits a typical request of this kind costs on the option. */\n creditsPerRequest: number\n /** That cost as a multiple of Auto's, to one decimal. */\n multiplier: number\n}\n\nexport interface AiModelOptions {\n /** Auto, labeled with the model it routes this kind to under these bounds. */\n auto: AiModelOption & { model: string }\n /** The manual picks, cheapest first; empty where the plan offers none. */\n options: AiModelOption[]\n /** Whether the price came from the workspace's own measured exchanges. */\n measured: boolean\n}\n\n/** What the selector lists for one step kind, or `null` without a provider. */\nexport function aiModelOptions(\n kind: AiStepKind,\n bounds: AiModelBounds,\n measured: AiUsage | null,\n settings?: AiPluginSettings,\n): AiModelOptions | null {\n const route = resolveAiRoute(kind, settings)\n const auto = resolveAiModelChoice(kind, null, bounds, settings)\n if (!route || !auto) return null\n const typical = measured ?? AI_STEP_NOMINAL_USAGE[kind]\n // Billed rates throughout (AGL-3015): every figure below is what the\n // workspace will be charged for picking this model — credits per request\n // and the multiple of Auto those credits come to. Our own cost for the\n // same request is not this selector's subject and would misstate both.\n const autoCost = estimateAiBilledUsd(typical, auto.model)\n const priced = (entry: AiCatalogEntry): AiModelOption => {\n const cost = estimateAiBilledUsd(typical, entry.id)\n return {\n id: entry.id,\n label: entry.label,\n tier: entry.tier,\n creditsPerRequest: assistCreditsFromUsd(cost),\n multiplier: autoCost > 0 ? Math.round((cost / autoCost) * 10) / 10 : 1,\n }\n }\n const autoEntry = aiCatalogEntry(auto.model)\n return {\n auto: {\n id: AI_MODEL_AUTO,\n label: autoEntry ? `Auto (${autoEntry.label})` : 'Auto',\n tier: autoEntry?.tier ?? AI_STEP_TIERS[kind],\n creditsPerRequest: assistCreditsFromUsd(autoCost),\n multiplier: 1,\n model: auto.model,\n },\n options: aiModelsSelectable(route.provider.id, bounds)\n .map(priced)\n .sort((a, b) => a.creditsPerRequest - b.creditsPerRequest || a.label.localeCompare(b.label)),\n measured: measured !== null,\n }\n}\n"],"names":["assistCreditsFromUsd","AI_MODEL_CATALOG","AI_STEP_TIERS","aiCatalogEntry","aiBilledRatesForModel","estimateAiBilledUsd","resolveAiRoute","AI_MODEL_AUTO","AI_PLAN_MODEL_TIERS","free","starter","pro","business","scale","advanced","agency","enterprise","AI_MODEL_RESTRICTION_PLANS","AI_STEP_NOMINAL_USAGE","inputTokens","outputTokens","cacheReadTokens","cacheWriteTokens","AI_MEASURED_MEDIAN_MIN_SAMPLES","onLists","entry","bounds","orgModels","length","includes","id","allotmentModels","aiModelsSelectable","providerId","tiers","plan","filter","provider","tier","blendedRate","rates","inputPerToken","outputPerToken","resolveAiModelChoice","kind","requested","settings","route","wanted","trim","some","model","auto","declined","restricted","Boolean","allowed","sameTier","find","cheapest","sort","a","b","aiMedianUsage","samples","median","field","values","map","sample","Number","value","isFinite","middle","Math","floor","round","aiModelOptions","measured","typical","autoCost","priced","cost","label","creditsPerRequest","multiplier","autoEntry","options","localeCompare"],"mappings":"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"}
|
|
@@ -390,6 +390,24 @@ import { aiProviderById, defaultAiProvider } from "./registry.js";
|
|
|
390
390
|
meanScore: 1
|
|
391
391
|
}
|
|
392
392
|
},
|
|
393
|
+
'job.edit': {
|
|
394
|
+
// A change to a page or a layout the site has (AGL-3616): which elements
|
|
395
|
+
// a request means, on an outline with no selection, is the judgment the
|
|
396
|
+
// step sells, so it thinks before it answers.
|
|
397
|
+
thinking: 'adaptive',
|
|
398
|
+
effort: null,
|
|
399
|
+
maxTokens: 6144,
|
|
400
|
+
maxTokensBasis: "the Assist edit rung's own ceiling for the proposal's tool call (ASSIST_EDIT_MAX_OUTPUT_TOKENS, 4,096: forty new elements written as name/value pairs), with half as much again to think in, as ai-job-edit-step.spec.ts holds it; the whole of it twice, as a re-ask may, still fits one beat on the balanced tier",
|
|
401
|
+
eval: {
|
|
402
|
+
kinds: [
|
|
403
|
+
'edit'
|
|
404
|
+
],
|
|
405
|
+
scores: 'answers',
|
|
406
|
+
source: 'authored',
|
|
407
|
+
passRate: 1,
|
|
408
|
+
meanScore: 1
|
|
409
|
+
}
|
|
410
|
+
},
|
|
393
411
|
'job.seo': {
|
|
394
412
|
thinking: null,
|
|
395
413
|
effort: null,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/providers/routing.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AI_SETTING_PLATFORM, AI_STEP_MODEL_SETTING } from '../plugin-config'\nimport {\n AI_STEP_TIERS,\n aiCatalogEntry,\n aiDefaultModelFor,\n type AiStepKind,\n} from './catalog'\nimport type { AiEvalKind } from '../runtime/ai-eval'\nimport type { AiEffort, AiProvider, AiThinking } from './contract'\nimport { aiProviderById, defaultAiProvider } from './registry'\n\n/**\n * The routing table (AGL-2937, AGL-2939): step kind → provider and model,\n * through the catalog and never a vendor literal.\n *\n * Precedence, dearest override first:\n *\n * 1. the org's `pluginSettings/ai` — a provider, and a model per step kind\n * that must be one the provider serves;\n * 2. the deployment's environment — `AI_PROVIDER` for the provider,\n * `AI_DEFAULT_MODEL` for every step, and `ASSIST_MODEL` for the\n * assistant alone (the incident-response override the chat door has\n * always honored);\n * 3. the catalog's tier for the step kind on the resolved provider.\n */\n\n/** The environment override for every step kind. */\nexport const AI_DEFAULT_MODEL_ENV = 'AI_DEFAULT_MODEL'\n/** The assistant's own override, honored before the general one. */\nexport const ASSIST_MODEL_ENV = 'ASSIST_MODEL'\n\n/** The org's resolved `pluginSettings/ai` values, as `getPluginConfig` merges them. */\nexport type AiPluginSettings = Record<string, unknown>\n\nexport interface AiRoute {\n provider: AiProvider\n model: string\n}\n\n/** A settings value that names a real choice, or `undefined` for the platform default. */\nfunction chosen(settings: AiPluginSettings | undefined, key: string): string | undefined {\n const value = settings?.[key]\n return typeof value === 'string' && value && value !== AI_SETTING_PLATFORM\n ? value\n : undefined\n}\n\n/** The provider a workspace runs on: its own choice when registered, else the platform's. */\nexport function resolveAiProvider(settings?: AiPluginSettings): AiProvider | undefined {\n const wanted = chosen(settings, 'provider')\n if (wanted) {\n const named = aiProviderById(wanted)\n if (named) return named\n }\n return defaultAiProvider()\n}\n\n/**\n * The model a step kind runs on for a workspace, on the resolved provider.\n * A chosen model on another provider is not served — the provider decides\n * what it can run — and falls back to the provider's default for the tier.\n */\nexport function resolveAiRoute(\n kind: AiStepKind,\n settings?: AiPluginSettings,\n): AiRoute | undefined {\n const provider = resolveAiProvider(settings)\n if (!provider) return undefined\n const servedBy = (modelId: string | undefined): string | undefined =>\n modelId && aiCatalogEntry(modelId)?.provider === provider.id ? modelId : undefined\n const model =\n servedBy(chosen(settings, AI_STEP_MODEL_SETTING[kind])) ??\n (kind === 'assist.chat' ? servedBy(process.env[ASSIST_MODEL_ENV]?.trim()) : undefined) ??\n servedBy(process.env[AI_DEFAULT_MODEL_ENV]?.trim()) ??\n aiDefaultModelFor(provider.id, AI_STEP_TIERS[kind])\n return model ? { provider, model } : undefined\n}\n\n/**\n * What one step kind asks of its model, beside the tier it is served from\n * (`AI_STEP_TIERS` in the catalog): whether it thinks and how hard, its\n * answer ceiling, and the eval score that tier holds on the step's golden\n * briefs (AGL-2937).\n */\nexport interface AiRoutingRow {\n /** `null` sends no setting: the model's own default, and all a fast-tier model accepts. */\n thinking: AiThinking | null\n effort: AiEffort | null\n /** The answer ceiling, thinking included. */\n maxTokens: number\n /** What the ceiling is sized from. */\n maxTokensBasis: string\n eval: AiRoutingEval\n}\n\nexport interface AiRoutingEval {\n /** The golden-brief kinds the step answers. */\n kinds: readonly AiEvalKind[]\n /** `answers` scores the step's answers; `plans` scores the plans those kinds' answers carry. */\n scores: 'answers' | 'plans'\n /** `authored` while the score is the hand-written references'; `recorded` once a live run records answers. */\n source: 'authored' | 'recorded'\n passRate: number\n /** The mean answer score; `null` for a plan score, which passes or fails. */\n meanScore: number | null\n}\n\n/**\n * THE ROUTING TABLE (AGL-2937): what each step kind asks of the model, in one\n * place, with the evidence beside it.\n *\n * A step runs on the smallest tier that holds its eval floor. A row moves to a\n * cheaper tier, or a lower effort, only when a recorded eval\n * (`npm run eval:ai-live`) holds the floor there; while a row's score is the\n * authored references', it says so, and the row stays where production has\n * run it. `runtime/ai-eval.spec.ts` recomputes every row's score from the\n * harness, so a row cannot claim a score its briefs do not hold.\n *\n * A ceiling is sized from the p95 output the staff signals page reports for\n * the kind once production has served it; until then `maxTokensBasis` names\n * what it is sized from. The same spec holds every reference answer under its\n * row's ceiling at three characters a token, a reading that errs high for a\n * tokenizer that splits finer than four.\n */\nexport const AI_ROUTING_TABLE: Readonly<Record<AiStepKind, AiRoutingRow>> = {\n 'assist.chat': {\n // A scoped, latency-sensitive chat turn: no thinking, the low effort rung.\n thinking: 'off',\n effort: 'low',\n maxTokens: 1024,\n maxTokensBasis:\n 'a docs-grounded answer of a few short paragraphs; the edit rung raises it for its tool call',\n eval: { kinds: ['chat'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9688 },\n },\n 'copy.element': {\n thinking: null,\n effort: null,\n maxTokens: 1024,\n maxTokensBasis: 'one element of copy, about the length of the text it replaces',\n eval: { kinds: ['element'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9688 },\n },\n 'copy.section': {\n thinking: null,\n effort: null,\n maxTokens: 3000,\n maxTokensBasis: 'a section of four to twelve nodes written as JSON',\n eval: { kinds: ['section'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9688 },\n },\n 'copy.blog': {\n thinking: null,\n effort: null,\n maxTokens: 2048,\n maxTokensBasis: 'one blog post body in markdown-lite',\n eval: { kinds: ['blog'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9688 },\n },\n 'generate.section': {\n thinking: null,\n effort: null,\n maxTokens: 3000,\n maxTokensBasis: 'a section of four to twelve nodes written as JSON',\n eval: { kinds: ['section'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9688 },\n },\n 'job.layout': {\n // The plan was reasoned out and confirmed before the step, so it writes without thinking.\n thinking: 'off',\n effort: null,\n maxTokens: 8000,\n maxTokensBasis:\n \"tighter than the doctrine's default for a layout, so one step and its re-ask fit the job beat's budget; the tree is held to its rule 17 budget either way\",\n eval: { kinds: ['layout'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9833 },\n },\n 'job.template': {\n thinking: 'off',\n effort: null,\n maxTokens: 8000,\n maxTokensBasis:\n \"tighter than the doctrine's default for a template, so one step and its re-ask fit the job beat's budget; the tree is held to its rule 17 budget either way\",\n eval: { kinds: ['template'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9833 },\n },\n 'job.component': {\n // A component's tree and the properties it declares, in one answer, from a\n // plan reasoned out and confirmed before the step (AGL-2908).\n thinking: 'off',\n effort: null,\n maxTokens: 8000,\n maxTokensBasis:\n \"the ceiling the layout and template steps keep, so one step and its re-ask fit the job beat's budget; the tree is held to its rule 17 budget either way\",\n eval: { kinds: ['component'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9833 },\n },\n 'job.crm': {\n // CRM by AI (AGL-2917): short answers through a strict tool, written from\n // the facts the CRM reports about the record the member opened, with no\n // thinking. The ceiling fits an inline door, so a member is answered in\n // the request that asked.\n thinking: null,\n effort: null,\n maxTokens: 700,\n maxTokensBasis:\n \"the largest answer a CRM tool accepts: sixty columns matched to fields by number, or an email draft at its length limits, at three characters a token with room, as ai-job-crm-step.spec.ts measures it\",\n eval: { kinds: ['crm'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9938 },\n },\n 'job.experiment': {\n // Variants of one piece of copy, or a result explained against a verdict\n // code reached before the request was made (AGL-2914). The judgment is\n // already made either way, so it writes without thinking.\n thinking: 'off',\n effort: null,\n maxTokens: 1500,\n maxTokensBasis:\n \"four variants at the tool's own lengths — a name, a headline, a body and a rationale each — written as JSON, about 3,000 characters at three characters a token with room; an explanation is a headline, four sentences and a next step, well under it, as ai-job-experiment-step.spec.ts measures both\",\n eval: { kinds: ['experiment'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 1 },\n },\n 'job.form': {\n // A form's two halves in one answer: the field declaration and the design\n // that renders it, as JSON, with no extended thinking (AGL-2913). The step\n // asks this ceiling, which is the doctrine's own for a form, and registers\n // the time it takes (AGL-3035).\n thinking: 'off',\n effort: null,\n maxTokens: 6000,\n maxTokensBasis:\n \"the largest form the doctrine's output budget admits, a form's fields, its consent and routing declaration and the design that renders them written as JSON at the wordiest golden's characters a stored byte, under it with room; ai-job-form-step.spec.ts measures it\",\n eval: { kinds: ['form'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9833 },\n },\n 'job.insight': {\n // Readers chosen, then their tables phrased (AGL-2915): short sentences\n // over figures code computed, each held to the rows it cites, so no\n // extended thinking. The read call before the answer asks for at most\n // `AI_INSIGHT_READ_MAX_TOKENS` of the same allowance.\n thinking: 'off',\n effort: null,\n maxTokens: 1500,\n maxTokensBasis:\n 'five insights at their 280-character bound, each citing three tables of ten rows, and the gap sentence, written as JSON: about 2,700 characters, under 1,000 tokens at three characters a token, with room',\n eval: { kinds: ['insight'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9688 },\n },\n 'job.page': {\n // One section of a page, written against a plan a member already\n // confirmed, so it writes without thinking (AGL-2907). The ceiling here\n // is the MOST a pass may ask for; each pass sizes its own down from it to\n // what fits the time the job beat can give the step, which on the\n // balanced tier is about half of this.\n thinking: 'off',\n effort: null,\n maxTokens: 2000,\n maxTokensBasis:\n \"a section of a page written as JSON, about forty elements at the step's measured 45 tokens an element; a pass lowers it to the worst case that fits AI_JOB_PAGE_STEP_MINIMUM_MS on the model it runs\",\n eval: { kinds: ['page'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9833 },\n },\n 'job.products': {\n // A product's copy from its name, text and photo, or a store's first\n // products, categories and discounts from a brief (AGL-2916): answers held\n // to the storefront claim rules, written with no extended thinking.\n thinking: 'off',\n effort: null,\n maxTokens: 8000,\n maxTokensBasis:\n \"the largest catalog the tool accepts, twelve proposed products at every bound written as JSON, which the balanced tier asks as much of as fits a beat; one product's copy asks 1,500 and categories with discounts 2,000, as ai-job-products-step.spec.ts measures them\",\n eval: {\n kinds: ['product', 'catalog', 'categories'],\n scores: 'answers',\n source: 'authored',\n passRate: 1,\n meanScore: 0.9922,\n },\n },\n 'job.email': {\n // One email design from the email palette, written against the brief with\n // no extended thinking (AGL-2912). A campaign job runs the same\n // generation, so it carries the same ceiling.\n thinking: 'off',\n effort: null,\n maxTokens: 6000,\n maxTokensBasis:\n \"an email's node map written as JSON, with three subject lines and three preheaders beside it\",\n eval: { kinds: ['email'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9688 },\n },\n 'job.campaign': {\n // The campaign job generates its email through the email step, so it is\n // routed identically; the draft campaign it also writes asks nothing of\n // the model (AGL-2912).\n thinking: 'off',\n effort: null,\n maxTokens: 6000,\n maxTokensBasis:\n \"an email's node map written as JSON, with three subject lines and three preheaders beside it\",\n eval: { kinds: ['email'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9688 },\n },\n 'job.workflow': {\n // An automation drafted from a description, or one explained (AGL-2919):\n // which trigger and which steps a description means is the judgment the\n // step sells, so it thinks before it answers.\n thinking: 'adaptive',\n effort: null,\n maxTokens: 4000,\n maxTokensBasis:\n 'the largest automation the tool accepts, 6,000 characters written out as JSON, at three characters a token with as much again to think in, as ai-job-workflow-step.spec.ts measures it',\n eval: { kinds: ['workflow'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9688 },\n },\n 'job.logic': {\n // A site function or variable proposed, or a function explained\n // (AGL-3603): which conditions and arithmetic a description means is the\n // judgment the step sells, so it thinks before it answers.\n thinking: 'adaptive',\n effort: null,\n maxTokens: 3000,\n maxTokensBasis:\n 'the largest function the tool accepts — twelve parameters, twenty-four operations — is far past any description; a function of eight operations is about 2,400 characters of JSON, at three characters a token with as much again to think in, as ai-job-logic-step.spec.ts measures it',\n eval: { kinds: ['logic'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 1 },\n },\n 'job.seo': {\n thinking: null,\n effort: null,\n maxTokens: 1024,\n maxTokensBasis:\n \"the largest listing the tool accepts, at three characters a token with a quarter again, as ai-job-seo-step.spec.ts measures it; an audit's site proposal and fix batches keep ceilings of their own, measured the same way\",\n eval: { kinds: ['seo'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 1 },\n },\n 'job.text': {\n thinking: 'adaptive',\n effort: null,\n maxTokens: 1024,\n maxTokensBasis: 'a few paragraphs of copy; a brief asking for more gets a draft to extend',\n eval: { kinds: ['text'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 1 },\n },\n 'job.theme': {\n thinking: 'adaptive',\n effort: null,\n maxTokens: 8000,\n maxTokensBasis:\n 'the largest answer the theme tool accepts, about 2,500 tokens of JSON as ai-job-theme-step.spec.ts measures it, with as much again to think in',\n eval: { kinds: ['theme'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9688 },\n },\n 'job.plan': {\n thinking: 'adaptive',\n effort: null,\n maxTokens: 8000,\n maxTokensBasis: 'a plan of up to twelve screens with sixteen sections each, with room to think',\n eval: {\n kinds: ['page', 'template', 'component', 'layout', 'form'],\n scores: 'plans',\n source: 'authored',\n passRate: 1,\n meanScore: null,\n },\n },\n}\n\n/** The model id alone — what a door records on the meter and the job. */\nexport function aiModelForStep(kind: AiStepKind, settings?: AiPluginSettings): string {\n const route = resolveAiRoute(kind, settings)\n if (!route) {\n // Every door gates on the provider being configured before it asks\n // for a model, so this is a wiring fault rather than a customer path.\n throw new Error('no AI provider is registered')\n }\n return route.model\n}\n"],"names":["AI_SETTING_PLATFORM","AI_STEP_MODEL_SETTING","AI_STEP_TIERS","aiCatalogEntry","aiDefaultModelFor","aiProviderById","defaultAiProvider","AI_DEFAULT_MODEL_ENV","ASSIST_MODEL_ENV","chosen","settings","key","value","undefined","resolveAiProvider","wanted","named","resolveAiRoute","kind","servedBy","process","provider","modelId","id","model","env","trim","AI_ROUTING_TABLE","thinking","effort","maxTokens","maxTokensBasis","eval","kinds","scores","source","passRate","meanScore","aiModelForStep","route","Error"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/providers/routing.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AI_SETTING_PLATFORM, AI_STEP_MODEL_SETTING } from '../plugin-config'\nimport {\n AI_STEP_TIERS,\n aiCatalogEntry,\n aiDefaultModelFor,\n type AiStepKind,\n} from './catalog'\nimport type { AiEvalKind } from '../runtime/ai-eval'\nimport type { AiEffort, AiProvider, AiThinking } from './contract'\nimport { aiProviderById, defaultAiProvider } from './registry'\n\n/**\n * The routing table (AGL-2937, AGL-2939): step kind → provider and model,\n * through the catalog and never a vendor literal.\n *\n * Precedence, dearest override first:\n *\n * 1. the org's `pluginSettings/ai` — a provider, and a model per step kind\n * that must be one the provider serves;\n * 2. the deployment's environment — `AI_PROVIDER` for the provider,\n * `AI_DEFAULT_MODEL` for every step, and `ASSIST_MODEL` for the\n * assistant alone (the incident-response override the chat door has\n * always honored);\n * 3. the catalog's tier for the step kind on the resolved provider.\n */\n\n/** The environment override for every step kind. */\nexport const AI_DEFAULT_MODEL_ENV = 'AI_DEFAULT_MODEL'\n/** The assistant's own override, honored before the general one. */\nexport const ASSIST_MODEL_ENV = 'ASSIST_MODEL'\n\n/** The org's resolved `pluginSettings/ai` values, as `getPluginConfig` merges them. */\nexport type AiPluginSettings = Record<string, unknown>\n\nexport interface AiRoute {\n provider: AiProvider\n model: string\n}\n\n/** A settings value that names a real choice, or `undefined` for the platform default. */\nfunction chosen(settings: AiPluginSettings | undefined, key: string): string | undefined {\n const value = settings?.[key]\n return typeof value === 'string' && value && value !== AI_SETTING_PLATFORM\n ? value\n : undefined\n}\n\n/** The provider a workspace runs on: its own choice when registered, else the platform's. */\nexport function resolveAiProvider(settings?: AiPluginSettings): AiProvider | undefined {\n const wanted = chosen(settings, 'provider')\n if (wanted) {\n const named = aiProviderById(wanted)\n if (named) return named\n }\n return defaultAiProvider()\n}\n\n/**\n * The model a step kind runs on for a workspace, on the resolved provider.\n * A chosen model on another provider is not served — the provider decides\n * what it can run — and falls back to the provider's default for the tier.\n */\nexport function resolveAiRoute(\n kind: AiStepKind,\n settings?: AiPluginSettings,\n): AiRoute | undefined {\n const provider = resolveAiProvider(settings)\n if (!provider) return undefined\n const servedBy = (modelId: string | undefined): string | undefined =>\n modelId && aiCatalogEntry(modelId)?.provider === provider.id ? modelId : undefined\n const model =\n servedBy(chosen(settings, AI_STEP_MODEL_SETTING[kind])) ??\n (kind === 'assist.chat' ? servedBy(process.env[ASSIST_MODEL_ENV]?.trim()) : undefined) ??\n servedBy(process.env[AI_DEFAULT_MODEL_ENV]?.trim()) ??\n aiDefaultModelFor(provider.id, AI_STEP_TIERS[kind])\n return model ? { provider, model } : undefined\n}\n\n/**\n * What one step kind asks of its model, beside the tier it is served from\n * (`AI_STEP_TIERS` in the catalog): whether it thinks and how hard, its\n * answer ceiling, and the eval score that tier holds on the step's golden\n * briefs (AGL-2937).\n */\nexport interface AiRoutingRow {\n /** `null` sends no setting: the model's own default, and all a fast-tier model accepts. */\n thinking: AiThinking | null\n effort: AiEffort | null\n /** The answer ceiling, thinking included. */\n maxTokens: number\n /** What the ceiling is sized from. */\n maxTokensBasis: string\n eval: AiRoutingEval\n}\n\nexport interface AiRoutingEval {\n /** The golden-brief kinds the step answers. */\n kinds: readonly AiEvalKind[]\n /** `answers` scores the step's answers; `plans` scores the plans those kinds' answers carry. */\n scores: 'answers' | 'plans'\n /** `authored` while the score is the hand-written references'; `recorded` once a live run records answers. */\n source: 'authored' | 'recorded'\n passRate: number\n /** The mean answer score; `null` for a plan score, which passes or fails. */\n meanScore: number | null\n}\n\n/**\n * THE ROUTING TABLE (AGL-2937): what each step kind asks of the model, in one\n * place, with the evidence beside it.\n *\n * A step runs on the smallest tier that holds its eval floor. A row moves to a\n * cheaper tier, or a lower effort, only when a recorded eval\n * (`npm run eval:ai-live`) holds the floor there; while a row's score is the\n * authored references', it says so, and the row stays where production has\n * run it. `runtime/ai-eval.spec.ts` recomputes every row's score from the\n * harness, so a row cannot claim a score its briefs do not hold.\n *\n * A ceiling is sized from the p95 output the staff signals page reports for\n * the kind once production has served it; until then `maxTokensBasis` names\n * what it is sized from. The same spec holds every reference answer under its\n * row's ceiling at three characters a token, a reading that errs high for a\n * tokenizer that splits finer than four.\n */\nexport const AI_ROUTING_TABLE: Readonly<Record<AiStepKind, AiRoutingRow>> = {\n 'assist.chat': {\n // A scoped, latency-sensitive chat turn: no thinking, the low effort rung.\n thinking: 'off',\n effort: 'low',\n maxTokens: 1024,\n maxTokensBasis:\n 'a docs-grounded answer of a few short paragraphs; the edit rung raises it for its tool call',\n eval: { kinds: ['chat'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9688 },\n },\n 'copy.element': {\n thinking: null,\n effort: null,\n maxTokens: 1024,\n maxTokensBasis: 'one element of copy, about the length of the text it replaces',\n eval: { kinds: ['element'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9688 },\n },\n 'copy.section': {\n thinking: null,\n effort: null,\n maxTokens: 3000,\n maxTokensBasis: 'a section of four to twelve nodes written as JSON',\n eval: { kinds: ['section'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9688 },\n },\n 'copy.blog': {\n thinking: null,\n effort: null,\n maxTokens: 2048,\n maxTokensBasis: 'one blog post body in markdown-lite',\n eval: { kinds: ['blog'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9688 },\n },\n 'generate.section': {\n thinking: null,\n effort: null,\n maxTokens: 3000,\n maxTokensBasis: 'a section of four to twelve nodes written as JSON',\n eval: { kinds: ['section'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9688 },\n },\n 'job.layout': {\n // The plan was reasoned out and confirmed before the step, so it writes without thinking.\n thinking: 'off',\n effort: null,\n maxTokens: 8000,\n maxTokensBasis:\n \"tighter than the doctrine's default for a layout, so one step and its re-ask fit the job beat's budget; the tree is held to its rule 17 budget either way\",\n eval: { kinds: ['layout'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9833 },\n },\n 'job.template': {\n thinking: 'off',\n effort: null,\n maxTokens: 8000,\n maxTokensBasis:\n \"tighter than the doctrine's default for a template, so one step and its re-ask fit the job beat's budget; the tree is held to its rule 17 budget either way\",\n eval: { kinds: ['template'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9833 },\n },\n 'job.component': {\n // A component's tree and the properties it declares, in one answer, from a\n // plan reasoned out and confirmed before the step (AGL-2908).\n thinking: 'off',\n effort: null,\n maxTokens: 8000,\n maxTokensBasis:\n \"the ceiling the layout and template steps keep, so one step and its re-ask fit the job beat's budget; the tree is held to its rule 17 budget either way\",\n eval: { kinds: ['component'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9833 },\n },\n 'job.crm': {\n // CRM by AI (AGL-2917): short answers through a strict tool, written from\n // the facts the CRM reports about the record the member opened, with no\n // thinking. The ceiling fits an inline door, so a member is answered in\n // the request that asked.\n thinking: null,\n effort: null,\n maxTokens: 700,\n maxTokensBasis:\n \"the largest answer a CRM tool accepts: sixty columns matched to fields by number, or an email draft at its length limits, at three characters a token with room, as ai-job-crm-step.spec.ts measures it\",\n eval: { kinds: ['crm'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9938 },\n },\n 'job.experiment': {\n // Variants of one piece of copy, or a result explained against a verdict\n // code reached before the request was made (AGL-2914). The judgment is\n // already made either way, so it writes without thinking.\n thinking: 'off',\n effort: null,\n maxTokens: 1500,\n maxTokensBasis:\n \"four variants at the tool's own lengths — a name, a headline, a body and a rationale each — written as JSON, about 3,000 characters at three characters a token with room; an explanation is a headline, four sentences and a next step, well under it, as ai-job-experiment-step.spec.ts measures both\",\n eval: { kinds: ['experiment'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 1 },\n },\n 'job.form': {\n // A form's two halves in one answer: the field declaration and the design\n // that renders it, as JSON, with no extended thinking (AGL-2913). The step\n // asks this ceiling, which is the doctrine's own for a form, and registers\n // the time it takes (AGL-3035).\n thinking: 'off',\n effort: null,\n maxTokens: 6000,\n maxTokensBasis:\n \"the largest form the doctrine's output budget admits, a form's fields, its consent and routing declaration and the design that renders them written as JSON at the wordiest golden's characters a stored byte, under it with room; ai-job-form-step.spec.ts measures it\",\n eval: { kinds: ['form'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9833 },\n },\n 'job.insight': {\n // Readers chosen, then their tables phrased (AGL-2915): short sentences\n // over figures code computed, each held to the rows it cites, so no\n // extended thinking. The read call before the answer asks for at most\n // `AI_INSIGHT_READ_MAX_TOKENS` of the same allowance.\n thinking: 'off',\n effort: null,\n maxTokens: 1500,\n maxTokensBasis:\n 'five insights at their 280-character bound, each citing three tables of ten rows, and the gap sentence, written as JSON: about 2,700 characters, under 1,000 tokens at three characters a token, with room',\n eval: { kinds: ['insight'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9688 },\n },\n 'job.page': {\n // One section of a page, written against a plan a member already\n // confirmed, so it writes without thinking (AGL-2907). The ceiling here\n // is the MOST a pass may ask for; each pass sizes its own down from it to\n // what fits the time the job beat can give the step, which on the\n // balanced tier is about half of this.\n thinking: 'off',\n effort: null,\n maxTokens: 2000,\n maxTokensBasis:\n \"a section of a page written as JSON, about forty elements at the step's measured 45 tokens an element; a pass lowers it to the worst case that fits AI_JOB_PAGE_STEP_MINIMUM_MS on the model it runs\",\n eval: { kinds: ['page'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9833 },\n },\n 'job.products': {\n // A product's copy from its name, text and photo, or a store's first\n // products, categories and discounts from a brief (AGL-2916): answers held\n // to the storefront claim rules, written with no extended thinking.\n thinking: 'off',\n effort: null,\n maxTokens: 8000,\n maxTokensBasis:\n \"the largest catalog the tool accepts, twelve proposed products at every bound written as JSON, which the balanced tier asks as much of as fits a beat; one product's copy asks 1,500 and categories with discounts 2,000, as ai-job-products-step.spec.ts measures them\",\n eval: {\n kinds: ['product', 'catalog', 'categories'],\n scores: 'answers',\n source: 'authored',\n passRate: 1,\n meanScore: 0.9922,\n },\n },\n 'job.email': {\n // One email design from the email palette, written against the brief with\n // no extended thinking (AGL-2912). A campaign job runs the same\n // generation, so it carries the same ceiling.\n thinking: 'off',\n effort: null,\n maxTokens: 6000,\n maxTokensBasis:\n \"an email's node map written as JSON, with three subject lines and three preheaders beside it\",\n eval: { kinds: ['email'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9688 },\n },\n 'job.campaign': {\n // The campaign job generates its email through the email step, so it is\n // routed identically; the draft campaign it also writes asks nothing of\n // the model (AGL-2912).\n thinking: 'off',\n effort: null,\n maxTokens: 6000,\n maxTokensBasis:\n \"an email's node map written as JSON, with three subject lines and three preheaders beside it\",\n eval: { kinds: ['email'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9688 },\n },\n 'job.workflow': {\n // An automation drafted from a description, or one explained (AGL-2919):\n // which trigger and which steps a description means is the judgment the\n // step sells, so it thinks before it answers.\n thinking: 'adaptive',\n effort: null,\n maxTokens: 4000,\n maxTokensBasis:\n 'the largest automation the tool accepts, 6,000 characters written out as JSON, at three characters a token with as much again to think in, as ai-job-workflow-step.spec.ts measures it',\n eval: { kinds: ['workflow'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9688 },\n },\n 'job.logic': {\n // A site function or variable proposed, or a function explained\n // (AGL-3603): which conditions and arithmetic a description means is the\n // judgment the step sells, so it thinks before it answers.\n thinking: 'adaptive',\n effort: null,\n maxTokens: 3000,\n maxTokensBasis:\n 'the largest function the tool accepts — twelve parameters, twenty-four operations — is far past any description; a function of eight operations is about 2,400 characters of JSON, at three characters a token with as much again to think in, as ai-job-logic-step.spec.ts measures it',\n eval: { kinds: ['logic'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 1 },\n },\n 'job.edit': {\n // A change to a page or a layout the site has (AGL-3616): which elements\n // a request means, on an outline with no selection, is the judgment the\n // step sells, so it thinks before it answers.\n thinking: 'adaptive',\n effort: null,\n maxTokens: 6144,\n maxTokensBasis:\n \"the Assist edit rung's own ceiling for the proposal's tool call (ASSIST_EDIT_MAX_OUTPUT_TOKENS, 4,096: forty new elements written as name/value pairs), with half as much again to think in, as ai-job-edit-step.spec.ts holds it; the whole of it twice, as a re-ask may, still fits one beat on the balanced tier\",\n eval: { kinds: ['edit'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 1 },\n },\n 'job.seo': {\n thinking: null,\n effort: null,\n maxTokens: 1024,\n maxTokensBasis:\n \"the largest listing the tool accepts, at three characters a token with a quarter again, as ai-job-seo-step.spec.ts measures it; an audit's site proposal and fix batches keep ceilings of their own, measured the same way\",\n eval: { kinds: ['seo'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 1 },\n },\n 'job.text': {\n thinking: 'adaptive',\n effort: null,\n maxTokens: 1024,\n maxTokensBasis: 'a few paragraphs of copy; a brief asking for more gets a draft to extend',\n eval: { kinds: ['text'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 1 },\n },\n 'job.theme': {\n thinking: 'adaptive',\n effort: null,\n maxTokens: 8000,\n maxTokensBasis:\n 'the largest answer the theme tool accepts, about 2,500 tokens of JSON as ai-job-theme-step.spec.ts measures it, with as much again to think in',\n eval: { kinds: ['theme'], scores: 'answers', source: 'authored', passRate: 1, meanScore: 0.9688 },\n },\n 'job.plan': {\n thinking: 'adaptive',\n effort: null,\n maxTokens: 8000,\n maxTokensBasis: 'a plan of up to twelve screens with sixteen sections each, with room to think',\n eval: {\n kinds: ['page', 'template', 'component', 'layout', 'form'],\n scores: 'plans',\n source: 'authored',\n passRate: 1,\n meanScore: null,\n },\n },\n}\n\n/** The model id alone — what a door records on the meter and the job. */\nexport function aiModelForStep(kind: AiStepKind, settings?: AiPluginSettings): string {\n const route = resolveAiRoute(kind, settings)\n if (!route) {\n // Every door gates on the provider being configured before it asks\n // for a model, so this is a wiring fault rather than a customer path.\n throw new Error('no AI provider is registered')\n }\n return route.model\n}\n"],"names":["AI_SETTING_PLATFORM","AI_STEP_MODEL_SETTING","AI_STEP_TIERS","aiCatalogEntry","aiDefaultModelFor","aiProviderById","defaultAiProvider","AI_DEFAULT_MODEL_ENV","ASSIST_MODEL_ENV","chosen","settings","key","value","undefined","resolveAiProvider","wanted","named","resolveAiRoute","kind","servedBy","process","provider","modelId","id","model","env","trim","AI_ROUTING_TABLE","thinking","effort","maxTokens","maxTokensBasis","eval","kinds","scores","source","passRate","meanScore","aiModelForStep","route","Error"],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Where a Vertex AI image request goes (AGL-3602): the operator's settings
|
|
19
|
+
* and the address they make, with nothing that authenticates or sends. Kept
|
|
20
|
+
* apart from the adapter so the subprocessor declarations, which the manifest
|
|
21
|
+
* generator loads outside any app, can name the host without loading the
|
|
22
|
+
* platform's service-account credential.
|
|
23
|
+
*/
|
|
24
|
+
export declare const VERTEX_IMAGE_PROJECT_ENV = "AI_IMAGE_VERTEX_PROJECT";
|
|
25
|
+
export declare const VERTEX_IMAGE_LOCATION_ENV = "AI_IMAGE_VERTEX_LOCATION";
|
|
26
|
+
export declare const VERTEX_IMAGE_MODEL_ENV = "AI_IMAGE_MODEL";
|
|
27
|
+
/** The location requests go to when the operator names none: Google's global endpoint. */
|
|
28
|
+
export declare const VERTEX_IMAGE_DEFAULT_LOCATION = "global";
|
|
29
|
+
/** The model a request runs on when the operator names none. */
|
|
30
|
+
export declare const VERTEX_IMAGE_DEFAULT_MODEL = "gemini-3.1-flash-image";
|
|
31
|
+
/** The configured project, or `null` when the provider is off. */
|
|
32
|
+
export declare function vertexImageProject(): string | null;
|
|
33
|
+
/** The configured location; the global endpoint when unset or malformed. */
|
|
34
|
+
export declare function vertexImageLocation(): string;
|
|
35
|
+
/** The configured model; the default when unset or malformed. */
|
|
36
|
+
export declare function vertexImageModel(): string;
|
|
37
|
+
/** The host a request goes to: the global endpoint's, a multi-region's, or a region's. */
|
|
38
|
+
export declare function vertexImageHost(location?: string): string;
|
|
39
|
+
/** The `:generateContent` address for a model. */
|
|
40
|
+
export declare function vertexImageUrl(project: string, location: string, model: string): string;
|
|
41
|
+
/**
|
|
42
|
+
* The hosts the published subprocessor row covers: the global endpoint's,
|
|
43
|
+
* which production uses, and the United States and European Union
|
|
44
|
+
* multi-regions', which an operator may choose and which fall inside a row
|
|
45
|
+
* whose location Google selects. A single region is a host the row does not
|
|
46
|
+
* name.
|
|
47
|
+
*/
|
|
48
|
+
export declare const VERTEX_IMAGE_PUBLISHED_HOSTS: readonly string[];
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import { AI_IMAGE_DEFAULT_MODEL } from "./catalog.js";
|
|
17
|
+
/**
|
|
18
|
+
* Where a Vertex AI image request goes (AGL-3602): the operator's settings
|
|
19
|
+
* and the address they make, with nothing that authenticates or sends. Kept
|
|
20
|
+
* apart from the adapter so the subprocessor declarations, which the manifest
|
|
21
|
+
* generator loads outside any app, can name the host without loading the
|
|
22
|
+
* platform's service-account credential.
|
|
23
|
+
*/ export const VERTEX_IMAGE_PROJECT_ENV = 'AI_IMAGE_VERTEX_PROJECT';
|
|
24
|
+
export const VERTEX_IMAGE_LOCATION_ENV = 'AI_IMAGE_VERTEX_LOCATION';
|
|
25
|
+
export const VERTEX_IMAGE_MODEL_ENV = 'AI_IMAGE_MODEL';
|
|
26
|
+
/** The location requests go to when the operator names none: Google's global endpoint. */ export const VERTEX_IMAGE_DEFAULT_LOCATION = 'global';
|
|
27
|
+
/** The model a request runs on when the operator names none. */ export const VERTEX_IMAGE_DEFAULT_MODEL = AI_IMAGE_DEFAULT_MODEL;
|
|
28
|
+
/** A Google Cloud project id: 6–30 lowercase letters, digits and hyphens. */ const PROJECT_ID = /^[a-z][a-z0-9-]{4,28}[a-z0-9]$/;
|
|
29
|
+
/**
|
|
30
|
+
* `global`; a multi-region, `us` or `eu`; or a Vertex AI region such as
|
|
31
|
+
* `us-central1`.
|
|
32
|
+
*/ const LOCATION = /^(global|us|eu|[a-z]+-[a-z]+[0-9]+)$/;
|
|
33
|
+
/** A publisher model id, `gemini-3.1-flash-image`. */ const MODEL_ID = /^[a-z0-9][a-z0-9.-]{1,62}$/;
|
|
34
|
+
function envValue(name) {
|
|
35
|
+
var _ref;
|
|
36
|
+
var _process_env_name;
|
|
37
|
+
return (_ref = (_process_env_name = process.env[name]) == null ? void 0 : _process_env_name.trim()) != null ? _ref : '';
|
|
38
|
+
}
|
|
39
|
+
/** The configured project, or `null` when the provider is off. */ export function vertexImageProject() {
|
|
40
|
+
const project = envValue(VERTEX_IMAGE_PROJECT_ENV);
|
|
41
|
+
return PROJECT_ID.test(project) ? project : null;
|
|
42
|
+
}
|
|
43
|
+
/** The configured location; the global endpoint when unset or malformed. */ export function vertexImageLocation() {
|
|
44
|
+
const location = envValue(VERTEX_IMAGE_LOCATION_ENV);
|
|
45
|
+
return LOCATION.test(location) ? location : VERTEX_IMAGE_DEFAULT_LOCATION;
|
|
46
|
+
}
|
|
47
|
+
/** The configured model; the default when unset or malformed. */ export function vertexImageModel() {
|
|
48
|
+
const model = envValue(VERTEX_IMAGE_MODEL_ENV);
|
|
49
|
+
return MODEL_ID.test(model) ? model : VERTEX_IMAGE_DEFAULT_MODEL;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The origins of the global endpoint and the two multi-region endpoints,
|
|
53
|
+
* written as URLs so the subprocessor inventory's sweep of outbound hosts
|
|
54
|
+
* sees every host this module may reach.
|
|
55
|
+
*
|
|
56
|
+
* The difference matters to the published row. Google's global endpoint
|
|
57
|
+
* gives no guarantee where a request is processed; a multi-region endpoint
|
|
58
|
+
* keeps machine-learning processing inside the United States (`us`) or the
|
|
59
|
+
* European Union (`eu`), for the models that offer one.
|
|
60
|
+
*/ const VERTEX_IMAGE_ORIGINS = {
|
|
61
|
+
global: 'https://aiplatform.googleapis.com',
|
|
62
|
+
us: 'https://aiplatform.us.rep.googleapis.com',
|
|
63
|
+
eu: 'https://aiplatform.eu.rep.googleapis.com'
|
|
64
|
+
};
|
|
65
|
+
/** The host a request goes to: the global endpoint's, a multi-region's, or a region's. */ export function vertexImageHost(location = vertexImageLocation()) {
|
|
66
|
+
const origin = VERTEX_IMAGE_ORIGINS[location];
|
|
67
|
+
return origin ? new URL(origin).host : `${location}-aiplatform.googleapis.com`;
|
|
68
|
+
}
|
|
69
|
+
/** The `:generateContent` address for a model. */ export function vertexImageUrl(project, location, model) {
|
|
70
|
+
return `https://${vertexImageHost(location)}/v1/projects/${project}/locations/${location}` + `/publishers/google/models/${model}:generateContent`;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The hosts the published subprocessor row covers: the global endpoint's,
|
|
74
|
+
* which production uses, and the United States and European Union
|
|
75
|
+
* multi-regions', which an operator may choose and which fall inside a row
|
|
76
|
+
* whose location Google selects. A single region is a host the row does not
|
|
77
|
+
* name.
|
|
78
|
+
*/ export const VERTEX_IMAGE_PUBLISHED_HOSTS = Object.keys(VERTEX_IMAGE_ORIGINS).map((location)=>vertexImageHost(location));
|
|
79
|
+
|
|
80
|
+
//# sourceMappingURL=vertex-image-endpoint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/providers/vertex-image-endpoint.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AI_IMAGE_DEFAULT_MODEL } from './catalog'\n\n/**\n * Where a Vertex AI image request goes (AGL-3602): the operator's settings\n * and the address they make, with nothing that authenticates or sends. Kept\n * apart from the adapter so the subprocessor declarations, which the manifest\n * generator loads outside any app, can name the host without loading the\n * platform's service-account credential.\n */\n\nexport const VERTEX_IMAGE_PROJECT_ENV = 'AI_IMAGE_VERTEX_PROJECT'\nexport const VERTEX_IMAGE_LOCATION_ENV = 'AI_IMAGE_VERTEX_LOCATION'\nexport const VERTEX_IMAGE_MODEL_ENV = 'AI_IMAGE_MODEL'\n\n/** The location requests go to when the operator names none: Google's global endpoint. */\nexport const VERTEX_IMAGE_DEFAULT_LOCATION = 'global'\n\n/** The model a request runs on when the operator names none. */\nexport const VERTEX_IMAGE_DEFAULT_MODEL = AI_IMAGE_DEFAULT_MODEL\n\n/** A Google Cloud project id: 6–30 lowercase letters, digits and hyphens. */\nconst PROJECT_ID = /^[a-z][a-z0-9-]{4,28}[a-z0-9]$/\n/**\n * `global`; a multi-region, `us` or `eu`; or a Vertex AI region such as\n * `us-central1`.\n */\nconst LOCATION = /^(global|us|eu|[a-z]+-[a-z]+[0-9]+)$/\n/** A publisher model id, `gemini-3.1-flash-image`. */\nconst MODEL_ID = /^[a-z0-9][a-z0-9.-]{1,62}$/\n\nfunction envValue(name: string): string {\n return process.env[name]?.trim() ?? ''\n}\n\n/** The configured project, or `null` when the provider is off. */\nexport function vertexImageProject(): string | null {\n const project = envValue(VERTEX_IMAGE_PROJECT_ENV)\n return PROJECT_ID.test(project) ? project : null\n}\n\n/** The configured location; the global endpoint when unset or malformed. */\nexport function vertexImageLocation(): string {\n const location = envValue(VERTEX_IMAGE_LOCATION_ENV)\n return LOCATION.test(location) ? location : VERTEX_IMAGE_DEFAULT_LOCATION\n}\n\n/** The configured model; the default when unset or malformed. */\nexport function vertexImageModel(): string {\n const model = envValue(VERTEX_IMAGE_MODEL_ENV)\n return MODEL_ID.test(model) ? model : VERTEX_IMAGE_DEFAULT_MODEL\n}\n\n/**\n * The origins of the global endpoint and the two multi-region endpoints,\n * written as URLs so the subprocessor inventory's sweep of outbound hosts\n * sees every host this module may reach.\n *\n * The difference matters to the published row. Google's global endpoint\n * gives no guarantee where a request is processed; a multi-region endpoint\n * keeps machine-learning processing inside the United States (`us`) or the\n * European Union (`eu`), for the models that offer one.\n */\nconst VERTEX_IMAGE_ORIGINS: Readonly<Record<string, string>> = {\n global: 'https://aiplatform.googleapis.com',\n us: 'https://aiplatform.us.rep.googleapis.com',\n eu: 'https://aiplatform.eu.rep.googleapis.com',\n}\n\n/** The host a request goes to: the global endpoint's, a multi-region's, or a region's. */\nexport function vertexImageHost(location = vertexImageLocation()): string {\n const origin = VERTEX_IMAGE_ORIGINS[location]\n return origin ? new URL(origin).host : `${location}-aiplatform.googleapis.com`\n}\n\n/** The `:generateContent` address for a model. */\nexport function vertexImageUrl(project: string, location: string, model: string): string {\n return (\n `https://${vertexImageHost(location)}/v1/projects/${project}/locations/${location}` +\n `/publishers/google/models/${model}:generateContent`\n )\n}\n\n/**\n * The hosts the published subprocessor row covers: the global endpoint's,\n * which production uses, and the United States and European Union\n * multi-regions', which an operator may choose and which fall inside a row\n * whose location Google selects. A single region is a host the row does not\n * name.\n */\nexport const VERTEX_IMAGE_PUBLISHED_HOSTS: readonly string[] = Object.keys(VERTEX_IMAGE_ORIGINS).map(\n (location) => vertexImageHost(location),\n)\n"],"names":["AI_IMAGE_DEFAULT_MODEL","VERTEX_IMAGE_PROJECT_ENV","VERTEX_IMAGE_LOCATION_ENV","VERTEX_IMAGE_MODEL_ENV","VERTEX_IMAGE_DEFAULT_LOCATION","VERTEX_IMAGE_DEFAULT_MODEL","PROJECT_ID","LOCATION","MODEL_ID","envValue","name","process","env","trim","vertexImageProject","project","test","vertexImageLocation","location","vertexImageModel","model","VERTEX_IMAGE_ORIGINS","global","us","eu","vertexImageHost","origin","URL","host","vertexImageUrl","VERTEX_IMAGE_PUBLISHED_HOSTS","Object","keys","map"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,sBAAsB,QAAQ,eAAW;AAElD;;;;;;CAMC,GAED,OAAO,MAAMC,2BAA2B,0BAAyB;AACjE,OAAO,MAAMC,4BAA4B,2BAA0B;AACnE,OAAO,MAAMC,yBAAyB,iBAAgB;AAEtD,wFAAwF,GACxF,OAAO,MAAMC,gCAAgC,SAAQ;AAErD,8DAA8D,GAC9D,OAAO,MAAMC,6BAA6BL,uBAAsB;AAEhE,2EAA2E,GAC3E,MAAMM,aAAa;AACnB;;;CAGC,GACD,MAAMC,WAAW;AACjB,oDAAoD,GACpD,MAAMC,WAAW;AAEjB,SAASC,SAASC,IAAY;;QACrBC;IAAP,gBAAOA,oBAAAA,QAAQC,GAAG,CAACF,KAAK,qBAAjBC,kBAAmBE,IAAI,qBAAM;AACtC;AAEA,gEAAgE,GAChE,OAAO,SAASC;IACd,MAAMC,UAAUN,SAASR;IACzB,OAAOK,WAAWU,IAAI,CAACD,WAAWA,UAAU;AAC9C;AAEA,0EAA0E,GAC1E,OAAO,SAASE;IACd,MAAMC,WAAWT,SAASP;IAC1B,OAAOK,SAASS,IAAI,CAACE,YAAYA,WAAWd;AAC9C;AAEA,+DAA+D,GAC/D,OAAO,SAASe;IACd,MAAMC,QAAQX,SAASN;IACvB,OAAOK,SAASQ,IAAI,CAACI,SAASA,QAAQf;AACxC;AAEA;;;;;;;;;CASC,GACD,MAAMgB,uBAAyD;IAC7DC,QAAQ;IACRC,IAAI;IACJC,IAAI;AACN;AAEA,wFAAwF,GACxF,OAAO,SAASC,gBAAgBP,WAAWD,qBAAqB;IAC9D,MAAMS,SAASL,oBAAoB,CAACH,SAAS;IAC7C,OAAOQ,SAAS,IAAIC,IAAID,QAAQE,IAAI,GAAG,GAAGV,SAAS,0BAA0B,CAAC;AAChF;AAEA,gDAAgD,GAChD,OAAO,SAASW,eAAed,OAAe,EAAEG,QAAgB,EAAEE,KAAa;IAC7E,OACE,CAAC,QAAQ,EAAEK,gBAAgBP,UAAU,aAAa,EAAEH,QAAQ,WAAW,EAAEG,UAAU,GACnF,CAAC,0BAA0B,EAAEE,MAAM,gBAAgB,CAAC;AAExD;AAEA;;;;;;CAMC,GACD,OAAO,MAAMU,+BAAkDC,OAAOC,IAAI,CAACX,sBAAsBY,GAAG,CAClG,CAACf,WAAaO,gBAAgBP,WAC/B"}
|