@elevasis/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +435 -0
- package/dist/index.js +403 -0
- package/dist/organization-model/index.d.ts +435 -0
- package/dist/organization-model/index.js +403 -0
- package/package.json +62 -0
- package/src/README.md +34 -0
- package/src/__tests__/observability-exports.test.ts +36 -0
- package/src/__tests__/publish.test.ts +18 -0
- package/src/__tests__/template-foundations-compatibility.test.ts +34 -0
- package/src/auth/index.ts +8 -0
- package/src/auth/multi-tenancy/credentials/README.md +38 -0
- package/src/auth/multi-tenancy/credentials/__tests__/encryption.test.ts +216 -0
- package/src/auth/multi-tenancy/credentials/__tests__/service.test.ts +174 -0
- package/src/auth/multi-tenancy/credentials/index.ts +6 -0
- package/src/auth/multi-tenancy/credentials/server/encryption.ts +39 -0
- package/src/auth/multi-tenancy/credentials/server/service.ts +60 -0
- package/src/auth/multi-tenancy/index.ts +17 -0
- package/src/auth/multi-tenancy/invitations/__tests__/invitation.test.ts +237 -0
- package/src/auth/multi-tenancy/invitations/api-schemas.ts +107 -0
- package/src/auth/multi-tenancy/invitations/index.ts +38 -0
- package/src/auth/multi-tenancy/invitations/invitation.ts +86 -0
- package/src/auth/multi-tenancy/invitations/server/index.ts +25 -0
- package/src/auth/multi-tenancy/invitations/server/transforms.ts +24 -0
- package/src/auth/multi-tenancy/invitations/server/workos.ts +24 -0
- package/src/auth/multi-tenancy/invitations/supabase.ts +50 -0
- package/src/auth/multi-tenancy/memberships/__tests__/membership.test.ts +227 -0
- package/src/auth/multi-tenancy/memberships/__tests__/supabase-transforms.test.ts +88 -0
- package/src/auth/multi-tenancy/memberships/__tests__/workos-transforms.test.ts +139 -0
- package/src/auth/multi-tenancy/memberships/api-schemas.ts +126 -0
- package/src/auth/multi-tenancy/memberships/index.ts +22 -0
- package/src/auth/multi-tenancy/memberships/membership.ts +138 -0
- package/src/auth/multi-tenancy/memberships/server/index.ts +15 -0
- package/src/auth/multi-tenancy/memberships/server/transforms.ts +32 -0
- package/src/auth/multi-tenancy/memberships/server/workos.ts +21 -0
- package/src/auth/multi-tenancy/memberships/supabase.ts +46 -0
- package/src/auth/multi-tenancy/organizations/__tests__/organization.test.ts +249 -0
- package/src/auth/multi-tenancy/organizations/api-schemas.ts +128 -0
- package/src/auth/multi-tenancy/organizations/index.ts +23 -0
- package/src/auth/multi-tenancy/organizations/organization.ts +25 -0
- package/src/auth/multi-tenancy/organizations/server/index.ts +10 -0
- package/src/auth/multi-tenancy/organizations/server/transforms.ts +35 -0
- package/src/auth/multi-tenancy/organizations/server/workos.ts +20 -0
- package/src/auth/multi-tenancy/types.ts +89 -0
- package/src/auth/multi-tenancy/users/__tests__/user.test.ts +208 -0
- package/src/auth/multi-tenancy/users/api-schemas.ts +194 -0
- package/src/auth/multi-tenancy/users/index.ts +28 -0
- package/src/auth/multi-tenancy/users/server/index.ts +19 -0
- package/src/auth/multi-tenancy/users/server/transforms.ts +21 -0
- package/src/auth/multi-tenancy/users/server/workos.ts +16 -0
- package/src/auth/multi-tenancy/users/user.ts +65 -0
- package/src/business/acquisition/api-schemas.ts +759 -0
- package/src/business/acquisition/index.ts +109 -0
- package/src/business/acquisition/types.ts +400 -0
- package/src/business/crm/api-schemas.ts +75 -0
- package/src/business/delivery/index.ts +1 -0
- package/src/business/delivery/types.ts +89 -0
- package/src/business/index.ts +12 -0
- package/src/business/pdf/assets/ElevasisLogo.png +0 -0
- package/src/business/pdf/browser/image-utils.ts +74 -0
- package/src/business/pdf/browser/index.ts +16 -0
- package/src/business/pdf/browser/pdfmake-browser.ts +229 -0
- package/src/business/pdf/index.ts +10 -0
- package/src/business/pdf/sections/acceptance.ts +112 -0
- package/src/business/pdf/sections/automation.ts +56 -0
- package/src/business/pdf/sections/cover.ts +51 -0
- package/src/business/pdf/sections/index.ts +57 -0
- package/src/business/pdf/sections/investment.ts +69 -0
- package/src/business/pdf/sections/proposal-document.ts +200 -0
- package/src/business/pdf/sections/summary-investment.ts +124 -0
- package/src/business/pdf/sections/summary.ts +55 -0
- package/src/business/pdf/sections/table-summary.ts +59 -0
- package/src/business/pdf/sections/types.ts +124 -0
- package/src/business/pdf/server/__tests__/pdfmake-test.ts +219 -0
- package/src/business/pdf/server/index.ts +21 -0
- package/src/business/pdf/server/pdfmake-service.ts +237 -0
- package/src/business/pdf/server/themes/default.ts +8 -0
- package/src/business/pdf/server/themes/index.ts +9 -0
- package/src/business/pdf/server/themes/types.ts +8 -0
- package/src/business/pdf/shared/convert.ts +514 -0
- package/src/business/pdf/shared/index.ts +12 -0
- package/src/business/pdf/themes.ts +78 -0
- package/src/business/pdf/types.ts +272 -0
- package/src/business/seo/__tests__/linking.test.ts +549 -0
- package/src/business/seo/__tests__/types.test.ts +404 -0
- package/src/business/seo/index.ts +2 -0
- package/src/business/seo/linking.ts +281 -0
- package/src/business/seo/types.ts +199 -0
- package/src/commands/index.ts +8 -0
- package/src/commands/queue/index.ts +3 -0
- package/src/commands/queue/schemas.test.ts +593 -0
- package/src/commands/queue/schemas.ts +125 -0
- package/src/commands/queue/sse-events.ts +61 -0
- package/src/commands/queue/types/action.ts +52 -0
- package/src/commands/queue/types/checkpoint.ts +44 -0
- package/src/commands/queue/types/index.ts +7 -0
- package/src/commands/queue/types/task.ts +116 -0
- package/src/commands/queue/types.ts +14 -0
- package/src/content/distribution-metadata.ts +61 -0
- package/src/content/index.ts +10 -0
- package/src/deployments/index.ts +22 -0
- package/src/execution/calibration/__tests__/schemas.test.ts +320 -0
- package/src/execution/calibration/index.ts +3 -0
- package/src/execution/calibration/schemas.ts +121 -0
- package/src/execution/calibration/sse-events.ts +125 -0
- package/src/execution/calibration/types.ts +190 -0
- package/src/execution/core/__tests__/api-schemas.test.ts +667 -0
- package/src/execution/core/__tests__/archived-logs.test.ts +72 -0
- package/src/execution/core/api-schemas.ts +312 -0
- package/src/execution/core/index.ts +11 -0
- package/src/execution/core/resource-validator.test.ts +63 -0
- package/src/execution/core/runner-types.ts +80 -0
- package/src/execution/core/server/environment.ts +31 -0
- package/src/execution/core/sse-executions.ts +119 -0
- package/src/execution/core/types.ts +29 -0
- package/src/execution/engine/__tests__/fixtures/index.ts +2 -0
- package/src/execution/engine/__tests__/fixtures/mock-scenarios.ts +60 -0
- package/src/execution/engine/__tests__/fixtures/test-agents.ts +85 -0
- package/src/execution/engine/__tests__/integration/agent-framework.integration.test.ts +1031 -0
- package/src/execution/engine/__tests__/timeout.test.ts +565 -0
- package/src/execution/engine/agent/__tests__/errors.test.ts +508 -0
- package/src/execution/engine/agent/actions/__tests__/processor.test.ts +531 -0
- package/src/execution/engine/agent/actions/executor.ts +205 -0
- package/src/execution/engine/agent/actions/navigate-knowledge-executor.ts +230 -0
- package/src/execution/engine/agent/actions/processor.ts +116 -0
- package/src/execution/engine/agent/actions/types.ts +70 -0
- package/src/execution/engine/agent/core/__tests__/agent.test.ts +614 -0
- package/src/execution/engine/agent/core/__tests__/error-passthrough.test.ts +134 -0
- package/src/execution/engine/agent/core/agent.ts +810 -0
- package/src/execution/engine/agent/core/types.ts +155 -0
- package/src/execution/engine/agent/errors.ts +251 -0
- package/src/execution/engine/agent/index.ts +78 -0
- package/src/execution/engine/agent/knowledge-map/__tests__/navigate-knowledge-executor.test.ts +580 -0
- package/src/execution/engine/agent/knowledge-map/__tests__/utils.test.ts +622 -0
- package/src/execution/engine/agent/knowledge-map/types.ts +106 -0
- package/src/execution/engine/agent/knowledge-map/utils.ts +101 -0
- package/src/execution/engine/agent/memory/__tests__/domains.test.ts +72 -0
- package/src/execution/engine/agent/memory/__tests__/manager.test.ts +754 -0
- package/src/execution/engine/agent/memory/__tests__/utils.test.ts +285 -0
- package/src/execution/engine/agent/memory/domains.ts +99 -0
- package/src/execution/engine/agent/memory/manager.ts +365 -0
- package/src/execution/engine/agent/memory/processor.ts +66 -0
- package/src/execution/engine/agent/memory/types.ts +90 -0
- package/src/execution/engine/agent/memory/utils.ts +134 -0
- package/src/execution/engine/agent/observability/logging.ts +467 -0
- package/src/execution/engine/agent/observability/types.ts +64 -0
- package/src/execution/engine/agent/reasoning/__tests__/request-builder.test.ts +209 -0
- package/src/execution/engine/agent/reasoning/adapters/agent-adapter-helpers.ts +349 -0
- package/src/execution/engine/agent/reasoning/processor.ts +92 -0
- package/src/execution/engine/agent/reasoning/prompt-sections/base-actions.ts +134 -0
- package/src/execution/engine/agent/reasoning/prompt-sections/completion.ts +49 -0
- package/src/execution/engine/agent/reasoning/prompt-sections/knowledge-map.ts +93 -0
- package/src/execution/engine/agent/reasoning/prompt-sections/memory.ts +65 -0
- package/src/execution/engine/agent/reasoning/prompt-sections/security.ts +32 -0
- package/src/execution/engine/agent/reasoning/prompt-sections/tools.ts +44 -0
- package/src/execution/engine/agent/reasoning/request-builder.ts +169 -0
- package/src/execution/engine/agent/reasoning/types.ts +18 -0
- package/src/execution/engine/base/__tests__/errors.test.ts +246 -0
- package/src/execution/engine/base/__tests__/serialization.test.ts +670 -0
- package/src/execution/engine/base/__tests__/utils.test.ts +45 -0
- package/src/execution/engine/base/errors.ts +118 -0
- package/src/execution/engine/base/index.ts +2 -0
- package/src/execution/engine/base/logging.ts +31 -0
- package/src/execution/engine/base/serialization.ts +324 -0
- package/src/execution/engine/base/types.ts +126 -0
- package/src/execution/engine/base/utils.ts +41 -0
- package/src/execution/engine/index.ts +440 -0
- package/src/execution/engine/interface/index.ts +1 -0
- package/src/execution/engine/interface/types.ts +62 -0
- package/src/execution/engine/llm/__tests__/errors.test.ts +318 -0
- package/src/execution/engine/llm/__tests__/input-sanitizer.test.ts +286 -0
- package/src/execution/engine/llm/__tests__/model-info.test.ts +50 -0
- package/src/execution/engine/llm/__tests__/model-validation.test.ts +321 -0
- package/src/execution/engine/llm/__tests__/response-schema-validator.test.ts +115 -0
- package/src/execution/engine/llm/adapters/__tests__/adapter-factory.test.ts +375 -0
- package/src/execution/engine/llm/adapters/__tests__/anthropic-adapter.test.ts +463 -0
- package/src/execution/engine/llm/adapters/__tests__/anthropic.integration.test.ts +177 -0
- package/src/execution/engine/llm/adapters/__tests__/circuit-breaker-error.test.ts +94 -0
- package/src/execution/engine/llm/adapters/__tests__/google-adapter.test.ts +722 -0
- package/src/execution/engine/llm/adapters/__tests__/google.integration.test.ts +376 -0
- package/src/execution/engine/llm/adapters/__tests__/mock-adapter.test.ts +432 -0
- package/src/execution/engine/llm/adapters/__tests__/openai-adapter.test.ts +551 -0
- package/src/execution/engine/llm/adapters/__tests__/openrouter-adapter.test.ts +563 -0
- package/src/execution/engine/llm/adapters/__tests__/openrouter.integration.test.ts +105 -0
- package/src/execution/engine/llm/adapters/__tests__/universal-adapter.test.ts +537 -0
- package/src/execution/engine/llm/adapters/circuit-breaker.ts +147 -0
- package/src/execution/engine/llm/adapters/index.ts +17 -0
- package/src/execution/engine/llm/adapters/mock-adapter.ts +116 -0
- package/src/execution/engine/llm/adapters/server/adapter-factory.ts +130 -0
- package/src/execution/engine/llm/adapters/server/anthropic.ts +137 -0
- package/src/execution/engine/llm/adapters/server/compose-signal.ts +18 -0
- package/src/execution/engine/llm/adapters/server/google.ts +283 -0
- package/src/execution/engine/llm/adapters/server/index.ts +12 -0
- package/src/execution/engine/llm/adapters/server/openai.ts +206 -0
- package/src/execution/engine/llm/adapters/server/openrouter.ts +235 -0
- package/src/execution/engine/llm/adapters/universal-adapter.ts +230 -0
- package/src/execution/engine/llm/errors.ts +186 -0
- package/src/execution/engine/llm/input-sanitizer.ts +129 -0
- package/src/execution/engine/llm/model-info.ts +332 -0
- package/src/execution/engine/llm/response-schema-validator.ts +113 -0
- package/src/execution/engine/llm/types.ts +86 -0
- package/src/execution/engine/test-utils/index.ts +6 -0
- package/src/execution/engine/test-utils/mocks.ts +56 -0
- package/src/execution/engine/tools/__tests__/tooling-error.test.ts +265 -0
- package/src/execution/engine/tools/__tests__/types.test.ts +47 -0
- package/src/execution/engine/tools/integration/base-integration-adapter.ts +50 -0
- package/src/execution/engine/tools/integration/index.ts +53 -0
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/anymailfinder-adapter.ts +73 -0
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/anymailfinder-tools.ts +209 -0
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/find-company-email/index.ts +82 -0
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/find-decision-maker-email/index.ts +122 -0
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/find-person-email/index.ts +89 -0
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/verify-email/index.ts +84 -0
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/index.ts +16 -0
- package/src/execution/engine/tools/integration/server/adapters/apify/__tests__/apify-run-actor.integration.test.ts +293 -0
- package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.ts +100 -0
- package/src/execution/engine/tools/integration/server/adapters/apify/apify-tools.ts +217 -0
- package/src/execution/engine/tools/integration/server/adapters/apify/fetch/get-dataset-items/index.ts +92 -0
- package/src/execution/engine/tools/integration/server/adapters/apify/fetch/run-actor/index.ts +218 -0
- package/src/execution/engine/tools/integration/server/adapters/apify/fetch/start-actor/index.ts +87 -0
- package/src/execution/engine/tools/integration/server/adapters/apify/index.ts +11 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/__tests__/attio-crud.integration.test.ts +362 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/attio-adapter.ts +162 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/attio-tools.ts +594 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/README.md +632 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/create-attribute/index.ts +214 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/create-note/index.ts +152 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/create-record/index.ts +141 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/delete-note/index.ts +86 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/delete-record/index.ts +105 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/get-record/index.test.ts +186 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/get-record/index.ts +118 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-attributes/index.ts +165 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-notes/index.ts +96 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-objects/index.ts +104 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-records/index.test.ts +338 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-records/index.ts +156 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/update-attribute/index.ts +220 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/update-record/index.ts +140 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/utils/types.ts +147 -0
- package/src/execution/engine/tools/integration/server/adapters/attio/index.ts +31 -0
- package/src/execution/engine/tools/integration/server/adapters/dropbox/__tests__/dropbox-adapter.test.ts +409 -0
- package/src/execution/engine/tools/integration/server/adapters/dropbox/dropbox-adapter.ts +281 -0
- package/src/execution/engine/tools/integration/server/adapters/dropbox/dropbox-tools.ts +106 -0
- package/src/execution/engine/tools/integration/server/adapters/dropbox/fetch/create-folder/__tests__/index.test.ts +451 -0
- package/src/execution/engine/tools/integration/server/adapters/dropbox/fetch/create-folder/index.ts +114 -0
- package/src/execution/engine/tools/integration/server/adapters/dropbox/fetch/upload-file/__tests__/index.test.ts +415 -0
- package/src/execution/engine/tools/integration/server/adapters/dropbox/fetch/upload-file/index.ts +111 -0
- package/src/execution/engine/tools/integration/server/adapters/dropbox/index.ts +25 -0
- package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-adapter.ts +210 -0
- package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-tools.ts +104 -0
- package/src/execution/engine/tools/integration/server/adapters/google-sheets/__tests__/google-sheets.integration.test.ts +261 -0
- package/src/execution/engine/tools/integration/server/adapters/google-sheets/google-sheets-adapter.ts +1189 -0
- package/src/execution/engine/tools/integration/server/adapters/google-sheets/google-sheets-tools.ts +641 -0
- package/src/execution/engine/tools/integration/server/adapters/google-sheets/index.ts +18 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/activate-campaign/index.ts +86 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/add-to-campaign/__tests__/index.test.ts +289 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/add-to-campaign/index.ts +154 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/bulk-add-leads/__tests__/index.test.ts +325 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/bulk-add-leads/index.ts +153 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/bulk-delete-leads/index.ts +84 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/create-campaign/index.ts +125 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/create-inbox-test/index.ts +107 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/delete-campaign/index.ts +85 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-account-health/index.ts +91 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-campaign/index.ts +92 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-campaign-analytics/__tests__/index.test.ts +195 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-campaign-analytics/index.ts +113 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-daily-campaign-analytics/index.ts +104 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-emails/index.ts +155 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-step-analytics/__tests__/index.test.ts +196 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-step-analytics/index.ts +102 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/list-campaigns/__tests__/index.test.ts +189 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/list-campaigns/index.ts +87 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/list-leads/index.ts +112 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/patch-lead/index.ts +76 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/pause-campaign/index.ts +86 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/remove-from-subsequence/index.ts +98 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/send-reply/index.ts +126 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-campaign/__tests__/index.test.ts +193 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-campaign/index.ts +99 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-interest-status/__tests__/index.test.ts +621 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-interest-status/index.ts +125 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/index.ts +29 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/instantly-adapter.ts +178 -0
- package/src/execution/engine/tools/integration/server/adapters/instantly/instantly-tools.ts +1473 -0
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/fetch/check-credits/index.ts +59 -0
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/fetch/verify-email/index.ts +102 -0
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/index.ts +17 -0
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/millionverifier-adapter.ts +80 -0
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/millionverifier-tools.ts +102 -0
- package/src/execution/engine/tools/integration/server/adapters/resend/fetch/get-email/index.ts +102 -0
- package/src/execution/engine/tools/integration/server/adapters/resend/fetch/send-email/index.ts +134 -0
- package/src/execution/engine/tools/integration/server/adapters/resend/fetch/utils/types.ts +75 -0
- package/src/execution/engine/tools/integration/server/adapters/resend/index.ts +27 -0
- package/src/execution/engine/tools/integration/server/adapters/resend/resend-adapter.ts +108 -0
- package/src/execution/engine/tools/integration/server/adapters/resend/resend-tools.ts +132 -0
- package/src/execution/engine/tools/integration/server/adapters/resend/types.ts +44 -0
- package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/create-envelope/index.ts +274 -0
- package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/download-document/index.ts +230 -0
- package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/get-envelope/index.ts +133 -0
- package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/utils/types.ts +246 -0
- package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/void-envelope/index.ts +90 -0
- package/src/execution/engine/tools/integration/server/adapters/signature-api/index.ts +38 -0
- package/src/execution/engine/tools/integration/server/adapters/signature-api/signature-api-adapter.ts +87 -0
- package/src/execution/engine/tools/integration/server/adapters/signature-api/signature-api-tools.ts +179 -0
- package/src/execution/engine/tools/integration/server/adapters/stripe/fetch/utils/types.ts +210 -0
- package/src/execution/engine/tools/integration/server/adapters/stripe/index.ts +44 -0
- package/src/execution/engine/tools/integration/server/adapters/stripe/stripe-adapter.ts +517 -0
- package/src/execution/engine/tools/integration/server/adapters/stripe/stripe-tools.ts +309 -0
- package/src/execution/engine/tools/integration/server/adapters/tomba/fetch/domain-search/index.ts +133 -0
- package/src/execution/engine/tools/integration/server/adapters/tomba/fetch/email-finder/index.ts +122 -0
- package/src/execution/engine/tools/integration/server/adapters/tomba/fetch/email-verifier/index.ts +111 -0
- package/src/execution/engine/tools/integration/server/adapters/tomba/index.ts +11 -0
- package/src/execution/engine/tools/integration/server/adapters/tomba/tomba-adapter.ts +78 -0
- package/src/execution/engine/tools/integration/server/adapters/tomba/tomba-tools.ts +222 -0
- package/src/execution/engine/tools/integration/server/index.ts +61 -0
- package/src/execution/engine/tools/integration/service.ts +161 -0
- package/src/execution/engine/tools/integration/tool.ts +253 -0
- package/src/execution/engine/tools/integration/types/anymailfinder.ts +74 -0
- package/src/execution/engine/tools/integration/types/apify.ts +92 -0
- package/src/execution/engine/tools/integration/types/attio.ts +354 -0
- package/src/execution/engine/tools/integration/types/dropbox.ts +64 -0
- package/src/execution/engine/tools/integration/types/gmail.ts +35 -0
- package/src/execution/engine/tools/integration/types/google-sheets.ts +303 -0
- package/src/execution/engine/tools/integration/types/index.ts +19 -0
- package/src/execution/engine/tools/integration/types/instantly.ts +557 -0
- package/src/execution/engine/tools/integration/types/millionverifier.ts +56 -0
- package/src/execution/engine/tools/integration/types/resend.ts +63 -0
- package/src/execution/engine/tools/integration/types/signature-api.ts +164 -0
- package/src/execution/engine/tools/integration/types/stripe.ts +162 -0
- package/src/execution/engine/tools/integration/types/tomba.ts +94 -0
- package/src/execution/engine/tools/lead-service-types.ts +884 -0
- package/src/execution/engine/tools/llm/index.ts +11 -0
- package/src/execution/engine/tools/llm/server/index.ts +8 -0
- package/src/execution/engine/tools/llm/server/llm-call-tool.ts +118 -0
- package/src/execution/engine/tools/platform/__tests__/approval.test.ts +242 -0
- package/src/execution/engine/tools/platform/__tests__/email.test.ts +482 -0
- package/src/execution/engine/tools/platform/__tests__/hitl-cancel.test.ts +97 -0
- package/src/execution/engine/tools/platform/__tests__/notification.test.ts +208 -0
- package/src/execution/engine/tools/platform/__tests__/pdf.test.ts +441 -0
- package/src/execution/engine/tools/platform/__tests__/scheduler.test.ts +189 -0
- package/src/execution/engine/tools/platform/__tests__/schedules.test.ts +336 -0
- package/src/execution/engine/tools/platform/acquisition/company-tools.ts +248 -0
- package/src/execution/engine/tools/platform/acquisition/contact-tools.ts +319 -0
- package/src/execution/engine/tools/platform/acquisition/index.ts +43 -0
- package/src/execution/engine/tools/platform/acquisition/list-tools.ts +148 -0
- package/src/execution/engine/tools/platform/acquisition/types.ts +260 -0
- package/src/execution/engine/tools/platform/approval/cancel-by-metadata.ts +65 -0
- package/src/execution/engine/tools/platform/approval/index.ts +4 -0
- package/src/execution/engine/tools/platform/approval/tool.ts +99 -0
- package/src/execution/engine/tools/platform/email/index.ts +122 -0
- package/src/execution/engine/tools/platform/email/types.ts +96 -0
- package/src/execution/engine/tools/platform/index.ts +181 -0
- package/src/execution/engine/tools/platform/notification.ts +81 -0
- package/src/execution/engine/tools/platform/pdf/index.ts +110 -0
- package/src/execution/engine/tools/platform/pdf/types.ts +77 -0
- package/src/execution/engine/tools/platform/resource-invocation/__tests__/edge-cases.test.ts +507 -0
- package/src/execution/engine/tools/platform/resource-invocation/__tests__/resource-invocation-service.test.ts +500 -0
- package/src/execution/engine/tools/platform/resource-invocation/__tests__/tool.test.ts +555 -0
- package/src/execution/engine/tools/platform/resource-invocation/dynamic-tool.ts +94 -0
- package/src/execution/engine/tools/platform/resource-invocation/index.ts +14 -0
- package/src/execution/engine/tools/platform/resource-invocation/resource-invocation-service.ts +147 -0
- package/src/execution/engine/tools/platform/resource-invocation/tool.ts +115 -0
- package/src/execution/engine/tools/platform/resource-invocation/types.ts +31 -0
- package/src/execution/engine/tools/platform/scheduler.ts +87 -0
- package/src/execution/engine/tools/platform/schedules/cancel-by-key-tool.ts +48 -0
- package/src/execution/engine/tools/platform/schedules/cancel-by-metadata-tool.ts +42 -0
- package/src/execution/engine/tools/platform/schedules/delete-by-key-tool.ts +43 -0
- package/src/execution/engine/tools/platform/schedules/index.ts +13 -0
- package/src/execution/engine/tools/platform/schedules/list-tool.ts +56 -0
- package/src/execution/engine/tools/platform/schedules/types.ts +88 -0
- package/src/execution/engine/tools/platform/storage/__tests__/storage.test.ts +998 -0
- package/src/execution/engine/tools/platform/storage/index.ts +370 -0
- package/src/execution/engine/tools/platform/storage/types.ts +128 -0
- package/src/execution/engine/tools/platform/types.ts +148 -0
- package/src/execution/engine/tools/registry.ts +590 -0
- package/src/execution/engine/tools/tool-maps.ts +694 -0
- package/src/execution/engine/tools/types.ts +233 -0
- package/src/execution/engine/workflow/__tests__/errors.test.ts +139 -0
- package/src/execution/engine/workflow/__tests__/utils.test.ts +645 -0
- package/src/execution/engine/workflow/__tests__/workflow.test.ts +818 -0
- package/src/execution/engine/workflow/errors.ts +63 -0
- package/src/execution/engine/workflow/helpers/index.ts +11 -0
- package/src/execution/engine/workflow/helpers/server/index.ts +8 -0
- package/src/execution/engine/workflow/helpers/server/llm-call.ts +93 -0
- package/src/execution/engine/workflow/index.ts +19 -0
- package/src/execution/engine/workflow/log-truncate.ts +26 -0
- package/src/execution/engine/workflow/logging.ts +191 -0
- package/src/execution/engine/workflow/types.ts +183 -0
- package/src/execution/engine/workflow/utils.ts +280 -0
- package/src/execution/engine/workflow/workflow.ts +168 -0
- package/src/execution/index.ts +20 -0
- package/src/execution/scheduler/__tests__/api-schemas.test.ts +733 -0
- package/src/execution/scheduler/__tests__/retry.test.ts +37 -0
- package/src/execution/scheduler/__tests__/utils.test.ts +1009 -0
- package/src/execution/scheduler/api-schemas.ts +296 -0
- package/src/execution/scheduler/index.ts +50 -0
- package/src/execution/scheduler/schemas.ts +264 -0
- package/src/execution/scheduler/types.ts +111 -0
- package/src/execution/scheduler/utils.ts +364 -0
- package/src/forms/index.ts +7 -0
- package/src/forms/schemas.test.ts +113 -0
- package/src/forms/schemas.ts +69 -0
- package/src/forms/types.ts +70 -0
- package/src/index.ts +54 -0
- package/src/integrations/credentials/__tests__/api-schemas.test.ts +496 -0
- package/src/integrations/credentials/__tests__/schemas.test.ts +82 -0
- package/src/integrations/credentials/__tests__/utils.test.ts +144 -0
- package/src/integrations/credentials/api-schemas.ts +143 -0
- package/src/integrations/credentials/index.ts +32 -0
- package/src/integrations/credentials/schemas.ts +164 -0
- package/src/integrations/credentials/utils.ts +59 -0
- package/src/integrations/oauth/__tests__/provider-registry.test.ts +59 -0
- package/src/integrations/oauth/api-schemas.ts +92 -0
- package/src/integrations/oauth/index.ts +19 -0
- package/src/integrations/oauth/provider-registry.ts +61 -0
- package/src/integrations/oauth/server/__tests__/refresh-concurrent.test.ts +183 -0
- package/src/integrations/oauth/server/__tests__/refresh.integration.test.ts +257 -0
- package/src/integrations/oauth/server/__tests__/refresh.test.ts +577 -0
- package/src/integrations/oauth/server/credentials.ts +39 -0
- package/src/integrations/oauth/server/refresh.ts +214 -0
- package/src/integrations/oauth/types.ts +34 -0
- package/src/integrations/webhook-endpoints/__tests__/api-schemas.test.ts +318 -0
- package/src/integrations/webhook-endpoints/api-schemas.ts +102 -0
- package/src/integrations/webhook-endpoints/index.ts +28 -0
- package/src/integrations/webhook-endpoints/types.ts +51 -0
- package/src/operations/activities/api-schemas.ts +79 -0
- package/src/operations/activities/index.ts +9 -0
- package/src/operations/activities/sse-events.ts +30 -0
- package/src/operations/activities/types.ts +63 -0
- package/src/operations/debug-logs/client.ts +60 -0
- package/src/operations/debug-logs/debug-logger.ts +83 -0
- package/src/operations/debug-logs/index.ts +8 -0
- package/src/operations/debug-logs/server.ts +19 -0
- package/src/operations/debug-logs/types.ts +33 -0
- package/src/operations/index.ts +50 -0
- package/src/operations/notifications/__tests__/api-schemas.test.ts +216 -0
- package/src/operations/notifications/api-schemas.ts +91 -0
- package/src/operations/notifications/index.ts +3 -0
- package/src/operations/notifications/sse-events.ts +21 -0
- package/src/operations/notifications/types.ts +47 -0
- package/src/operations/observability/__tests__/openrouter-cost-flow.test.ts +297 -0
- package/src/operations/observability/__tests__/schemas.test.ts +151 -0
- package/src/operations/observability/__tests__/types.test.ts +109 -0
- package/src/operations/observability/__tests__/utils.test.ts +54 -0
- package/src/operations/observability/ai-usage-collector.ts +64 -0
- package/src/operations/observability/index.ts +13 -0
- package/src/operations/observability/metrics-collector.ts +49 -0
- package/src/operations/observability/schemas.ts +39 -0
- package/src/operations/observability/types.ts +463 -0
- package/src/operations/observability/utils.ts +77 -0
- package/src/operations/sessions/__tests__/api-schemas.test.ts +361 -0
- package/src/operations/sessions/__tests__/manager.test.ts +821 -0
- package/src/operations/sessions/api-schemas.ts +166 -0
- package/src/operations/sessions/index.ts +26 -0
- package/src/operations/sessions/server/manager.ts +90 -0
- package/src/operations/sessions/server/session.ts +180 -0
- package/src/operations/sessions/types.ts +98 -0
- package/src/operations/triggers/index.ts +12 -0
- package/src/operations/triggers/webhook/definitions/__tests__/instantly-reply-received.test.ts +72 -0
- package/src/operations/triggers/webhook/definitions/instantly-account-error.ts +44 -0
- package/src/operations/triggers/webhook/definitions/instantly-auto-reply-received.ts +51 -0
- package/src/operations/triggers/webhook/definitions/instantly-campaign-completed.ts +45 -0
- package/src/operations/triggers/webhook/definitions/instantly-email-bounced.ts +49 -0
- package/src/operations/triggers/webhook/definitions/instantly-lead-unsubscribed.ts +45 -0
- package/src/operations/triggers/webhook/definitions/instantly-reply-received.ts +54 -0
- package/src/operations/triggers/webhook/index.ts +35 -0
- package/src/operations/triggers/webhook/types.ts +74 -0
- package/src/organization-model/README.md +79 -0
- package/src/organization-model/__tests__/graph.test.ts +250 -0
- package/src/organization-model/__tests__/resolve.test.ts +47 -0
- package/src/organization-model/defaults.ts +60 -0
- package/src/organization-model/domains/branding.ts +22 -0
- package/src/organization-model/domains/crm.ts +46 -0
- package/src/organization-model/domains/delivery.ts +48 -0
- package/src/organization-model/domains/features.ts +57 -0
- package/src/organization-model/domains/lead-gen.ts +33 -0
- package/src/organization-model/domains/navigation.ts +103 -0
- package/src/organization-model/domains/shared.ts +42 -0
- package/src/organization-model/graph/build.ts +432 -0
- package/src/organization-model/graph/index.ts +4 -0
- package/src/organization-model/graph/schema.ts +50 -0
- package/src/organization-model/graph/types.ts +52 -0
- package/src/organization-model/index.ts +11 -0
- package/src/organization-model/published.ts +18 -0
- package/src/organization-model/resolve.ts +42 -0
- package/src/organization-model/schema.ts +21 -0
- package/src/organization-model/types.ts +27 -0
- package/src/platform/api/index.ts +1 -0
- package/src/platform/api/types.ts +35 -0
- package/src/platform/constants/http.ts +37 -0
- package/src/platform/constants/index.ts +5 -0
- package/src/platform/constants/limits.ts +32 -0
- package/src/platform/constants/resilience.ts +51 -0
- package/src/platform/constants/timeouts.ts +20 -0
- package/src/platform/constants/versions.ts +3 -0
- package/src/platform/index.ts +27 -0
- package/src/platform/registry/__tests__/command-view.test.ts +410 -0
- package/src/platform/registry/__tests__/resource-registry-static.test.ts +347 -0
- package/src/platform/registry/__tests__/resource-registry.integration.test.ts +1004 -0
- package/src/platform/registry/__tests__/resource-registry.list-executable.test.ts +393 -0
- package/src/platform/registry/__tests__/resource-registry.test.ts +1942 -0
- package/src/platform/registry/__tests__/serialization.test.ts +1127 -0
- package/src/platform/registry/__tests__/validation.test.ts +1086 -0
- package/src/platform/registry/command-view.ts +180 -0
- package/src/platform/registry/domains.ts +165 -0
- package/src/platform/registry/index.ts +93 -0
- package/src/platform/registry/reserved.ts +24 -0
- package/src/platform/registry/resource-metadata.ts +59 -0
- package/src/platform/registry/resource-registry.command-queue-groups.test.ts +129 -0
- package/src/platform/registry/resource-registry.ts +788 -0
- package/src/platform/registry/serialization.ts +273 -0
- package/src/platform/registry/serialized-types.ts +231 -0
- package/src/platform/registry/stats-types.ts +66 -0
- package/src/platform/registry/types.ts +404 -0
- package/src/platform/registry/validation.ts +513 -0
- package/src/platform/resilience/__tests__/circuit-breaker.test.ts +291 -0
- package/src/platform/resilience/__tests__/http-error-mapper.test.ts +173 -0
- package/src/platform/resilience/__tests__/rate-limiter.test.ts +471 -0
- package/src/platform/resilience/__tests__/retry.test.ts +380 -0
- package/src/platform/resilience/__tests__/timeout.test.ts +219 -0
- package/src/platform/resilience/circuit-breaker.ts +164 -0
- package/src/platform/resilience/errors.ts +68 -0
- package/src/platform/resilience/http-error-mapper.ts +129 -0
- package/src/platform/resilience/index.ts +93 -0
- package/src/platform/resilience/rate-limiter-types.ts +46 -0
- package/src/platform/resilience/rate-limiter.ts +140 -0
- package/src/platform/resilience/retry.ts +89 -0
- package/src/platform/resilience/timeout.ts +63 -0
- package/src/platform/sse/events.ts +67 -0
- package/src/platform/sse/index.ts +7 -0
- package/src/platform/utils/__tests__/currency.test.ts +77 -0
- package/src/platform/utils/__tests__/validation.test.ts +1083 -0
- package/src/platform/utils/currency.ts +96 -0
- package/src/platform/utils/debounce.ts +52 -0
- package/src/platform/utils/error.ts +42 -0
- package/src/platform/utils/hmac.test.ts +97 -0
- package/src/platform/utils/index.ts +32 -0
- package/src/platform/utils/server/betterstack-logger.ts +210 -0
- package/src/platform/utils/server/hmac.ts +44 -0
- package/src/platform/utils/server/unsubscribe.ts +111 -0
- package/src/platform/utils/token-counter.ts +96 -0
- package/src/platform/utils/validation.ts +425 -0
- package/src/projects/api-schemas.ts +265 -0
- package/src/published.ts +1 -0
- package/src/server.ts +273 -0
- package/src/supabase/__tests__/helpers.test.ts +51 -0
- package/src/supabase/database.types.ts +2674 -0
- package/src/supabase/helpers.ts +20 -0
- package/src/supabase/index.ts +52 -0
- package/src/supabase/server/client.ts +58 -0
- package/src/test-utils/README.md +150 -0
- package/src/test-utils/browser-mocks.ts +54 -0
- package/src/test-utils/fixtures/api-keys.ts +52 -0
- package/src/test-utils/fixtures/index.ts +4 -0
- package/src/test-utils/fixtures/memberships.ts +80 -0
- package/src/test-utils/fixtures/organizations.ts +69 -0
- package/src/test-utils/fixtures/users.ts +79 -0
- package/src/test-utils/index.ts +11 -0
- package/src/test-utils/mocks/index.ts +2 -0
- package/src/test-utils/mocks/supabase.ts +142 -0
- package/src/test-utils/mocks/workos.ts +108 -0
- package/src/test-utils/rls/RLSTestContext.ts +586 -0
- package/src/test-utils/rls/index.ts +1 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-only LLM Adapters
|
|
3
|
+
*
|
|
4
|
+
* These adapters use server-only dependencies (betterstack-logger)
|
|
5
|
+
* and should only be imported from @repo/core/server
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { OpenAIAdapter, isOpenAIModel, type OpenAIAdapterConfig } from './openai'
|
|
9
|
+
export { OpenRouterAdapter, isOpenRouterModel, type OpenRouterAdapterConfig } from './openrouter'
|
|
10
|
+
export { GoogleAdapter, isGoogleModel, type GoogleAdapterConfig } from './google'
|
|
11
|
+
export { AnthropicAdapter, isAnthropicModel, type AnthropicAdapterConfig } from './anthropic'
|
|
12
|
+
export { createLLMAdapter } from './adapter-factory'
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic OpenAI Adapter
|
|
3
|
+
* Stateless translation layer between universal protocol and OpenAI API
|
|
4
|
+
*
|
|
5
|
+
* Responsibilities:
|
|
6
|
+
* 1. Translate LLMGenerateRequest to OpenAI format
|
|
7
|
+
* 2. Call OpenAI API with structured output
|
|
8
|
+
* 3. Parse OpenAI response
|
|
9
|
+
* 4. Translate back to LLMGenerateResponse
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type OpenAI from 'openai'
|
|
13
|
+
import type { LLMAdapter, LLMGenerateRequest, LLMGenerateResponse } from '../../types'
|
|
14
|
+
import type { LLMModel, ModelSpecificOptions } from '../../model-info'
|
|
15
|
+
import { getModelInfo } from '../../model-info'
|
|
16
|
+
import { TokenLimitExceededError, LLMResponseParseError } from '../../errors'
|
|
17
|
+
import { bslogExternalServiceError, isSystemError } from '../../../../../platform/utils/server/betterstack-logger'
|
|
18
|
+
import { composeSignal } from './compose-signal'
|
|
19
|
+
import { DEFAULT_LLM_TIMEOUT } from '../../../../../platform/constants/timeouts'
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Type guard for errors with HTTP status code (e.g., OpenAI API errors)
|
|
23
|
+
*/
|
|
24
|
+
interface ErrorWithStatus {
|
|
25
|
+
status: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function hasStatus(error: unknown): error is ErrorWithStatus {
|
|
29
|
+
return (
|
|
30
|
+
typeof error === 'object' &&
|
|
31
|
+
error !== null &&
|
|
32
|
+
'status' in error &&
|
|
33
|
+
typeof (error as ErrorWithStatus).status === 'number'
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* OpenAI adapter configuration
|
|
39
|
+
*/
|
|
40
|
+
export interface OpenAIAdapterConfig {
|
|
41
|
+
apiKey: string
|
|
42
|
+
model: LLMModel
|
|
43
|
+
modelOptions?: ModelSpecificOptions
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Check if the model and provider combination is for OpenAI
|
|
48
|
+
*
|
|
49
|
+
* @param model - Model identifier (e.g., 'gpt-5-mini', 'gpt-4')
|
|
50
|
+
* @param provider - Provider name
|
|
51
|
+
* @returns True if this is an OpenAI model configuration
|
|
52
|
+
*/
|
|
53
|
+
export function isOpenAIModel(model: LLMModel, provider: string): boolean {
|
|
54
|
+
return model.startsWith('gpt-') && provider === 'openai'
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Generic OpenAI Adapter - Implements universal protocol
|
|
59
|
+
* Uses structured output for guaranteed schema compliance
|
|
60
|
+
*/
|
|
61
|
+
export class OpenAIAdapter implements LLMAdapter {
|
|
62
|
+
private clientPromise: Promise<OpenAI>
|
|
63
|
+
private model: LLMModel
|
|
64
|
+
private modelOptions?: ModelSpecificOptions
|
|
65
|
+
|
|
66
|
+
constructor(config: OpenAIAdapterConfig) {
|
|
67
|
+
// Lazy load OpenAI SDK
|
|
68
|
+
this.clientPromise = import('openai').then(
|
|
69
|
+
({ default: OpenAIClass }) =>
|
|
70
|
+
new OpenAIClass({
|
|
71
|
+
apiKey: config.apiKey
|
|
72
|
+
})
|
|
73
|
+
)
|
|
74
|
+
this.model = config.model || 'gpt-5.4-mini'
|
|
75
|
+
this.modelOptions = config.modelOptions
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Generate structured output from messages using OpenAI
|
|
80
|
+
*
|
|
81
|
+
* @param request - Generic generation request
|
|
82
|
+
* @returns Structured output with usage metadata
|
|
83
|
+
*/
|
|
84
|
+
async generate<T>(request: LLMGenerateRequest): Promise<LLMGenerateResponse<T>> {
|
|
85
|
+
const client = await this.clientPromise
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
const createParams: OpenAI.Chat.ChatCompletionCreateParams = {
|
|
89
|
+
model: this.model,
|
|
90
|
+
messages: request.messages.map((msg) => ({
|
|
91
|
+
role: msg.role,
|
|
92
|
+
content: msg.content
|
|
93
|
+
})),
|
|
94
|
+
max_completion_tokens: request.maxOutputTokens,
|
|
95
|
+
response_format: {
|
|
96
|
+
type: 'json_schema',
|
|
97
|
+
json_schema: {
|
|
98
|
+
name: 'llm_response',
|
|
99
|
+
schema: request.responseSchema as Record<string, unknown>
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Only add temperature if specified and not default (1)
|
|
105
|
+
if (request.temperature !== undefined && request.temperature !== 1) {
|
|
106
|
+
createParams.temperature = request.temperature
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Only add topP if specified
|
|
110
|
+
if (request.topP !== undefined) {
|
|
111
|
+
createParams.top_p = request.topP
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Apply model-specific options if present (type narrowing for union)
|
|
115
|
+
if (this.modelOptions && 'reasoning_effort' in this.modelOptions) {
|
|
116
|
+
if (this.modelOptions.reasoning_effort) {
|
|
117
|
+
createParams.reasoning_effort = this.modelOptions.reasoning_effort
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (this.modelOptions && 'verbosity' in this.modelOptions) {
|
|
122
|
+
if (this.modelOptions.verbosity) {
|
|
123
|
+
createParams.verbosity = this.modelOptions.verbosity
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const response = await client.chat.completions.create(createParams, {
|
|
128
|
+
signal: composeSignal(DEFAULT_LLM_TIMEOUT, request.signal)
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
const choice = response.choices[0]
|
|
132
|
+
if (!choice) {
|
|
133
|
+
throw new Error('No response from OpenAI')
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// DETECT TOKEN LIMIT ERROR - OpenAI-specific
|
|
137
|
+
if (choice.finish_reason === 'length') {
|
|
138
|
+
const usage = response.usage
|
|
139
|
+
const reasoningTokens = usage?.completion_tokens_details?.reasoning_tokens || 0
|
|
140
|
+
const completionTokens = usage?.completion_tokens || 0
|
|
141
|
+
const modelInfo = getModelInfo(this.model)
|
|
142
|
+
|
|
143
|
+
throw new TokenLimitExceededError(
|
|
144
|
+
`LLM token limit exceeded during generation. ` +
|
|
145
|
+
`Model consumed all available tokens during reasoning phase with no tokens left for output.`,
|
|
146
|
+
{
|
|
147
|
+
model: this.model,
|
|
148
|
+
maxOutputTokens: request.maxOutputTokens,
|
|
149
|
+
usedTokens: completionTokens,
|
|
150
|
+
reasoningTokens,
|
|
151
|
+
outputTokens: completionTokens - reasoningTokens,
|
|
152
|
+
recommendation: modelInfo
|
|
153
|
+
? `Increase maxOutputTokens to at least ${modelInfo.minTokens} for this model.`
|
|
154
|
+
: 'Increase maxOutputTokens for this model.'
|
|
155
|
+
}
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Check for empty content (secondary validation)
|
|
160
|
+
const content = choice.message.content || '{}'
|
|
161
|
+
|
|
162
|
+
// Parse JSON with error wrapping for retry support
|
|
163
|
+
let output: unknown
|
|
164
|
+
try {
|
|
165
|
+
output = JSON.parse(content)
|
|
166
|
+
} catch (error) {
|
|
167
|
+
if (error instanceof SyntaxError) {
|
|
168
|
+
throw new LLMResponseParseError(`Failed to parse LLM response as JSON: ${error.message}`, {
|
|
169
|
+
rawContent: content.substring(0, 500),
|
|
170
|
+
parseError: error.message
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
throw error
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Extract usage data from OpenAI response (for wrapper to track)
|
|
177
|
+
const usage = response.usage
|
|
178
|
+
? {
|
|
179
|
+
inputTokens: response.usage.prompt_tokens,
|
|
180
|
+
outputTokens: response.usage.completion_tokens,
|
|
181
|
+
totalTokens: response.usage.total_tokens
|
|
182
|
+
}
|
|
183
|
+
: undefined
|
|
184
|
+
|
|
185
|
+
// Return response with usage (wrapper will extract and strip it)
|
|
186
|
+
return {
|
|
187
|
+
output: output as T,
|
|
188
|
+
usage
|
|
189
|
+
}
|
|
190
|
+
} catch (error) {
|
|
191
|
+
// Add Better Stack logging for system errors (5xx only)
|
|
192
|
+
if (isSystemError(error)) {
|
|
193
|
+
bslogExternalServiceError('openai', 'chat.completions.create', error as Error, {
|
|
194
|
+
model: this.model,
|
|
195
|
+
statusCode: hasStatus(error) ? error.status : undefined,
|
|
196
|
+
reasoningEffort:
|
|
197
|
+
this.modelOptions && 'reasoning_effort' in this.modelOptions
|
|
198
|
+
? this.modelOptions.reasoning_effort
|
|
199
|
+
: undefined
|
|
200
|
+
})
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
throw error
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenRouter Adapter
|
|
3
|
+
* Stateless translation layer between universal protocol and OpenRouter API
|
|
4
|
+
*
|
|
5
|
+
* OpenRouter provides access to 400+ LLM models (Claude, Gemini, Llama, etc.)
|
|
6
|
+
* via an OpenAI-compatible API with a single API key.
|
|
7
|
+
*
|
|
8
|
+
* Responsibilities:
|
|
9
|
+
* 1. Translate LLMGenerateRequest to OpenRouter format
|
|
10
|
+
* 2. Call OpenRouter API with structured output
|
|
11
|
+
* 3. Parse OpenRouter response
|
|
12
|
+
* 4. Translate back to LLMGenerateResponse
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type OpenAI from 'openai'
|
|
16
|
+
import type { LLMAdapter, LLMGenerateRequest, LLMGenerateResponse } from '../../types'
|
|
17
|
+
import type { LLMModel, OpenRouterOptions } from '../../model-info'
|
|
18
|
+
import { getModelInfo } from '../../model-info'
|
|
19
|
+
import { TokenLimitExceededError, LLMResponseParseError } from '../../errors'
|
|
20
|
+
import { bslogExternalServiceError, isSystemError } from '../../../../../platform/utils/server/betterstack-logger'
|
|
21
|
+
import { composeSignal } from './compose-signal'
|
|
22
|
+
import { DEFAULT_LLM_TIMEOUT } from '../../../../../platform/constants/timeouts'
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Type guard for errors with HTTP status code
|
|
26
|
+
*/
|
|
27
|
+
interface ErrorWithStatus {
|
|
28
|
+
status: number
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function hasStatus(error: unknown): error is ErrorWithStatus {
|
|
32
|
+
return (
|
|
33
|
+
typeof error === 'object' &&
|
|
34
|
+
error !== null &&
|
|
35
|
+
'status' in error &&
|
|
36
|
+
typeof (error as ErrorWithStatus).status === 'number'
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* OpenRouter-specific extra_body options for usage accounting and routing
|
|
42
|
+
*/
|
|
43
|
+
interface OpenRouterExtraBody {
|
|
44
|
+
usage?: { include: boolean }
|
|
45
|
+
transforms?: string[]
|
|
46
|
+
route?: 'fallback'
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Extended params type for OpenRouter with extra_body support
|
|
51
|
+
*/
|
|
52
|
+
type OpenRouterCreateParams = OpenAI.Chat.ChatCompletionCreateParamsNonStreaming & {
|
|
53
|
+
extra_body?: OpenRouterExtraBody
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* OpenRouter adapter configuration
|
|
58
|
+
*/
|
|
59
|
+
export interface OpenRouterAdapterConfig {
|
|
60
|
+
apiKey: string
|
|
61
|
+
model: LLMModel
|
|
62
|
+
modelOptions?: OpenRouterOptions
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Check if the model is an OpenRouter model
|
|
67
|
+
*
|
|
68
|
+
* @param model - Model identifier (e.g., 'openrouter/anthropic/claude-3.5-sonnet')
|
|
69
|
+
* @returns True if this is an OpenRouter model
|
|
70
|
+
*/
|
|
71
|
+
export function isOpenRouterModel(model: LLMModel): boolean {
|
|
72
|
+
return typeof model === 'string' && model.startsWith('openrouter/')
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* OpenRouter Adapter - Implements universal protocol
|
|
77
|
+
* Uses OpenAI SDK with custom baseURL for OpenRouter API
|
|
78
|
+
*/
|
|
79
|
+
export class OpenRouterAdapter implements LLMAdapter {
|
|
80
|
+
private clientPromise: Promise<OpenAI>
|
|
81
|
+
private model: LLMModel
|
|
82
|
+
private openRouterModel: string
|
|
83
|
+
private modelOptions?: OpenRouterOptions
|
|
84
|
+
|
|
85
|
+
constructor(config: OpenRouterAdapterConfig) {
|
|
86
|
+
// Strip 'openrouter/' prefix for API calls
|
|
87
|
+
// e.g., 'openrouter/anthropic/claude-3.5-sonnet' -> 'anthropic/claude-3.5-sonnet'
|
|
88
|
+
this.openRouterModel = config.model.replace('openrouter/', '')
|
|
89
|
+
this.model = config.model
|
|
90
|
+
this.modelOptions = config.modelOptions
|
|
91
|
+
|
|
92
|
+
// Lazy load OpenAI SDK with OpenRouter configuration
|
|
93
|
+
this.clientPromise = import('openai').then(
|
|
94
|
+
({ default: OpenAIClass }) =>
|
|
95
|
+
new OpenAIClass({
|
|
96
|
+
baseURL: 'https://openrouter.ai/api/v1',
|
|
97
|
+
apiKey: config.apiKey,
|
|
98
|
+
defaultHeaders: {
|
|
99
|
+
'HTTP-Referer': 'https://elevasis.com',
|
|
100
|
+
'X-Title': 'Elevasis'
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Generate structured output from messages using OpenRouter
|
|
108
|
+
*
|
|
109
|
+
* @param request - Generic generation request
|
|
110
|
+
* @returns Structured output with usage metadata
|
|
111
|
+
*/
|
|
112
|
+
async generate<T>(request: LLMGenerateRequest): Promise<LLMGenerateResponse<T>> {
|
|
113
|
+
const client = await this.clientPromise
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
// Build OpenRouter-specific extra_body with usage accounting
|
|
117
|
+
const extraBody: OpenRouterExtraBody = {
|
|
118
|
+
usage: { include: true }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (this.modelOptions?.transforms) {
|
|
122
|
+
extraBody.transforms = this.modelOptions.transforms
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (this.modelOptions?.route) {
|
|
126
|
+
extraBody.route = this.modelOptions.route
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const createParams: OpenRouterCreateParams = {
|
|
130
|
+
model: this.openRouterModel,
|
|
131
|
+
messages: request.messages.map((msg) => ({
|
|
132
|
+
role: msg.role,
|
|
133
|
+
content: msg.content
|
|
134
|
+
})),
|
|
135
|
+
max_tokens: request.maxOutputTokens,
|
|
136
|
+
response_format: {
|
|
137
|
+
type: 'json_schema',
|
|
138
|
+
json_schema: {
|
|
139
|
+
name: 'llm_response',
|
|
140
|
+
strict: true,
|
|
141
|
+
schema: request.responseSchema as Record<string, unknown>
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
extra_body: extraBody
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Add temperature if specified
|
|
148
|
+
if (request.temperature !== undefined) {
|
|
149
|
+
createParams.temperature = request.temperature
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Add topP if specified
|
|
153
|
+
if (request.topP !== undefined) {
|
|
154
|
+
createParams.top_p = request.topP
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const response = await client.chat.completions.create(createParams, {
|
|
158
|
+
signal: composeSignal(DEFAULT_LLM_TIMEOUT, request.signal)
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
const choice = response.choices[0]
|
|
162
|
+
if (!choice) {
|
|
163
|
+
throw new Error('No response from OpenRouter')
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Detect token limit error
|
|
167
|
+
if (choice.finish_reason === 'length') {
|
|
168
|
+
const usage = response.usage
|
|
169
|
+
const completionTokens = usage?.completion_tokens || 0
|
|
170
|
+
const modelInfo = getModelInfo(this.model)
|
|
171
|
+
|
|
172
|
+
throw new TokenLimitExceededError(
|
|
173
|
+
`LLM token limit exceeded during generation. ` +
|
|
174
|
+
`Model consumed all available tokens with no tokens left for output.`,
|
|
175
|
+
{
|
|
176
|
+
model: this.model,
|
|
177
|
+
maxOutputTokens: request.maxOutputTokens,
|
|
178
|
+
usedTokens: completionTokens,
|
|
179
|
+
reasoningTokens: 0, // OpenRouter doesn't expose reasoning tokens
|
|
180
|
+
outputTokens: completionTokens,
|
|
181
|
+
recommendation: modelInfo
|
|
182
|
+
? `Increase maxOutputTokens to at least ${modelInfo.minTokens} for this model.`
|
|
183
|
+
: 'Increase maxOutputTokens for this model.'
|
|
184
|
+
}
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Parse response content with error wrapping for retry support
|
|
189
|
+
const content = choice.message.content || '{}'
|
|
190
|
+
let output: unknown
|
|
191
|
+
try {
|
|
192
|
+
output = JSON.parse(content)
|
|
193
|
+
} catch (error) {
|
|
194
|
+
if (error instanceof SyntaxError) {
|
|
195
|
+
throw new LLMResponseParseError(`Failed to parse LLM response as JSON: ${error.message}`, {
|
|
196
|
+
rawContent: content.substring(0, 500),
|
|
197
|
+
parseError: error.message
|
|
198
|
+
})
|
|
199
|
+
}
|
|
200
|
+
throw error
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Extract usage data
|
|
204
|
+
const usage = response.usage
|
|
205
|
+
? {
|
|
206
|
+
inputTokens: response.usage.prompt_tokens,
|
|
207
|
+
outputTokens: response.usage.completion_tokens,
|
|
208
|
+
totalTokens: response.usage.total_tokens
|
|
209
|
+
}
|
|
210
|
+
: undefined
|
|
211
|
+
|
|
212
|
+
// Extract actual cost from OpenRouter (when usage accounting is enabled)
|
|
213
|
+
// OpenRouter returns cost in the usage object as an extended field
|
|
214
|
+
const usageWithCost = response.usage as { cost?: number } | undefined
|
|
215
|
+
const cost = usageWithCost?.cost
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
output: output as T,
|
|
219
|
+
usage,
|
|
220
|
+
cost
|
|
221
|
+
}
|
|
222
|
+
} catch (error) {
|
|
223
|
+
// Add Better Stack logging for system errors (5xx only)
|
|
224
|
+
if (isSystemError(error)) {
|
|
225
|
+
bslogExternalServiceError('openrouter', 'chat.completions.create', error as Error, {
|
|
226
|
+
model: this.model,
|
|
227
|
+
openRouterModel: this.openRouterModel,
|
|
228
|
+
statusCode: hasStatus(error) ? error.status : undefined
|
|
229
|
+
})
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
throw error
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Universal LLM Adapter
|
|
3
|
+
* Adds reliability (retry, circuit breaker) and observability (automatic token tracking) to ALL LLM calls
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { LLMAdapter, LLMGenerateRequest, LLMGenerateResponse } from '../types'
|
|
7
|
+
import type { LLMModel } from '../model-info'
|
|
8
|
+
import type { AIUsageCollector } from '../../../../operations/observability/ai-usage-collector'
|
|
9
|
+
import type { AICallContext, BaseAICall } from '../../../../operations/observability/types'
|
|
10
|
+
import { executeWithCircuitBreaker } from './circuit-breaker'
|
|
11
|
+
import { LLMResponseParseError, LLMInputBlockedError } from '../errors'
|
|
12
|
+
import { sanitizeUserInput } from '../input-sanitizer'
|
|
13
|
+
import { validateResponseSchema } from '../response-schema-validator'
|
|
14
|
+
import { LLM_MAX_ATTEMPTS, LLM_RETRY_DELAYS } from '../../../../platform/constants/resilience'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Type guard to check if an object has HTTP status property
|
|
18
|
+
*/
|
|
19
|
+
interface ErrorWithStatus {
|
|
20
|
+
status: number
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Type guard to check if an object has message property
|
|
25
|
+
*/
|
|
26
|
+
interface ErrorWithMessage {
|
|
27
|
+
message: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Type guard for error with status
|
|
32
|
+
*/
|
|
33
|
+
function hasStatus(error: unknown): error is ErrorWithStatus {
|
|
34
|
+
return (
|
|
35
|
+
typeof error === 'object' &&
|
|
36
|
+
error !== null &&
|
|
37
|
+
'status' in error &&
|
|
38
|
+
typeof (error as ErrorWithStatus).status === 'number'
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Type guard for error with message
|
|
44
|
+
*/
|
|
45
|
+
function hasMessage(error: unknown): error is ErrorWithMessage {
|
|
46
|
+
return (
|
|
47
|
+
typeof error === 'object' &&
|
|
48
|
+
error !== null &&
|
|
49
|
+
'message' in error &&
|
|
50
|
+
typeof (error as ErrorWithMessage).message === 'string'
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Universal wrapper that adds reliability + observability to any LLM adapter
|
|
56
|
+
*
|
|
57
|
+
* Features:
|
|
58
|
+
* - Exponential backoff retry (3 attempts: 1s, 2s, 4s)
|
|
59
|
+
* - Circuit breaker for cascading failure prevention (shared per model)
|
|
60
|
+
* - Automatic token usage tracking with typed contexts
|
|
61
|
+
* - Automatic latency tracking
|
|
62
|
+
*
|
|
63
|
+
* Implements decorator pattern - delegates to base adapter
|
|
64
|
+
*
|
|
65
|
+
* Note: Base adapters may include optional 'usage' field in responses.
|
|
66
|
+
* This wrapper extracts usage for observability and strips it before returning to callers.
|
|
67
|
+
*/
|
|
68
|
+
export class UniversalLLMAdapter implements LLMAdapter {
|
|
69
|
+
constructor(
|
|
70
|
+
private baseAdapter: LLMAdapter,
|
|
71
|
+
private modelName: LLMModel,
|
|
72
|
+
private aiUsageCollector?: AIUsageCollector,
|
|
73
|
+
private callType?: BaseAICall['callType'],
|
|
74
|
+
private context?: AICallContext
|
|
75
|
+
) {}
|
|
76
|
+
|
|
77
|
+
async generate<T>(request: LLMGenerateRequest): Promise<LLMGenerateResponse<T>> {
|
|
78
|
+
// Sanitize user-role messages BEFORE retry/circuit breaker
|
|
79
|
+
// Blocked input is not transient (no retry) and not a service failure (no circuit breaker impact)
|
|
80
|
+
const sanitizedRequest = this.sanitizeRequest(request)
|
|
81
|
+
return this.withRetryAndTracking(async () => {
|
|
82
|
+
const response = await this.baseAdapter.generate<T>(sanitizedRequest)
|
|
83
|
+
|
|
84
|
+
// Validate output against responseSchema (throws retryable LLMResponseParseError)
|
|
85
|
+
if (sanitizedRequest.responseSchema) {
|
|
86
|
+
validateResponseSchema(response.output, sanitizedRequest.responseSchema)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return response
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Sanitize user-role messages for prompt injection attacks
|
|
95
|
+
* System and assistant messages are framework-generated and skipped
|
|
96
|
+
*
|
|
97
|
+
* @throws LLMInputBlockedError if 3+ distinct attack patterns detected
|
|
98
|
+
*/
|
|
99
|
+
private sanitizeRequest(request: LLMGenerateRequest): LLMGenerateRequest {
|
|
100
|
+
return {
|
|
101
|
+
...request,
|
|
102
|
+
messages: request.messages.map((msg) => {
|
|
103
|
+
if (msg.role !== 'user') return msg
|
|
104
|
+
const result = sanitizeUserInput(msg.content)
|
|
105
|
+
if (result.blocked) {
|
|
106
|
+
throw new LLMInputBlockedError(result.warnings)
|
|
107
|
+
}
|
|
108
|
+
return { ...msg, content: result.sanitized }
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Wraps LLM call with retry logic, circuit breaker, and observability tracking
|
|
115
|
+
*
|
|
116
|
+
* The circuit breaker wraps the entire retry loop - this ensures that:
|
|
117
|
+
* - Failures during retries are counted toward the circuit breaker threshold
|
|
118
|
+
* - The circuit opens after sustained failures across retry attempts
|
|
119
|
+
* - Success after retries properly records success to the circuit breaker
|
|
120
|
+
*/
|
|
121
|
+
private async withRetryAndTracking<T>(fn: () => Promise<LLMGenerateResponse<T>>): Promise<LLMGenerateResponse<T>> {
|
|
122
|
+
// Circuit breaker wraps the entire retry loop
|
|
123
|
+
return executeWithCircuitBreaker(this.modelName, async () => {
|
|
124
|
+
// Retry loop (max 3 attempts with exponential backoff)
|
|
125
|
+
let lastError: unknown
|
|
126
|
+
|
|
127
|
+
for (let attempt = 1; attempt <= LLM_MAX_ATTEMPTS; attempt++) {
|
|
128
|
+
try {
|
|
129
|
+
// Measure per-attempt latency (not cumulative across retries)
|
|
130
|
+
const startTime = Date.now()
|
|
131
|
+
|
|
132
|
+
// Execute LLM call
|
|
133
|
+
const response = await fn()
|
|
134
|
+
|
|
135
|
+
// Calculate latency for this attempt only
|
|
136
|
+
const latencyMs = Date.now() - startTime
|
|
137
|
+
|
|
138
|
+
// Extract usage from response (if available)
|
|
139
|
+
if (response.usage && this.aiUsageCollector) {
|
|
140
|
+
this.aiUsageCollector.record(
|
|
141
|
+
{
|
|
142
|
+
model: this.modelName,
|
|
143
|
+
inputTokens: response.usage.inputTokens,
|
|
144
|
+
outputTokens: response.usage.outputTokens,
|
|
145
|
+
latencyMs,
|
|
146
|
+
cost: response.cost // Pass actual cost from provider (e.g., OpenRouter)
|
|
147
|
+
},
|
|
148
|
+
this.callType,
|
|
149
|
+
this.context
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Return response (usage hidden from callers by TypeScript interface)
|
|
154
|
+
return response
|
|
155
|
+
} catch (error) {
|
|
156
|
+
lastError = error
|
|
157
|
+
|
|
158
|
+
// Determine if error is retryable
|
|
159
|
+
const isRetryable = this.isRetryableError(error)
|
|
160
|
+
|
|
161
|
+
if (!isRetryable) {
|
|
162
|
+
// Non-retryable error (401, 400, etc.) - fail immediately
|
|
163
|
+
// Let the circuit breaker record this as a failure
|
|
164
|
+
throw error
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Retryable error (429, 500, 503, network) - retry with exponential backoff
|
|
168
|
+
if (attempt < LLM_MAX_ATTEMPTS) {
|
|
169
|
+
const delay = LLM_RETRY_DELAYS[attempt - 1]
|
|
170
|
+
await this.sleep(delay)
|
|
171
|
+
}
|
|
172
|
+
// If this was the last attempt, the loop ends and we throw below
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// All retries failed - throw to let circuit breaker record failure
|
|
177
|
+
throw lastError
|
|
178
|
+
})
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Determine if error should be retried
|
|
183
|
+
*/
|
|
184
|
+
private isRetryableError(error: unknown): boolean {
|
|
185
|
+
if (!error || typeof error !== 'object') return false
|
|
186
|
+
|
|
187
|
+
// LLMResponseParseError is always retryable (transient JSON parse failures)
|
|
188
|
+
if (error instanceof LLMResponseParseError) {
|
|
189
|
+
return true
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Check for HTTP status codes using type guard
|
|
193
|
+
if (hasStatus(error)) {
|
|
194
|
+
const status = error.status
|
|
195
|
+
|
|
196
|
+
// Retryable: Rate limits, server errors, service unavailable
|
|
197
|
+
if (status === 429 || status === 500 || status === 503) {
|
|
198
|
+
return true
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Non-retryable: Auth errors, bad requests
|
|
202
|
+
if (status === 401 || status === 400 || status === 403) {
|
|
203
|
+
return false
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Check for network errors using type guard
|
|
208
|
+
if (hasMessage(error)) {
|
|
209
|
+
const message = error.message
|
|
210
|
+
if (
|
|
211
|
+
message.includes('ECONNRESET') ||
|
|
212
|
+
message.includes('ETIMEDOUT') ||
|
|
213
|
+
message.includes('ENOTFOUND') ||
|
|
214
|
+
message.includes('network')
|
|
215
|
+
) {
|
|
216
|
+
return true
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Default: don't retry unknown errors
|
|
221
|
+
return false
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Sleep utility for retry delays
|
|
226
|
+
*/
|
|
227
|
+
private sleep(ms: number): Promise<void> {
|
|
228
|
+
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
229
|
+
}
|
|
230
|
+
}
|