@elevasis/core 0.7.1 → 0.8.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/package.json +1 -1
- package/src/README.md +41 -41
- package/src/__tests__/publish.test.ts +18 -18
- package/src/__tests__/{template-foundations-compatibility.test.ts → template-core-compatibility.test.ts} +99 -99
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +1135 -1131
- package/src/_gen/__tests__/scaffold-contracts.test.ts +53 -53
- package/src/_gen/scaffold-contracts.ts +45 -45
- package/src/auth/multi-tenancy/credentials/README.md +38 -38
- package/src/auth/multi-tenancy/credentials/index.ts +6 -6
- package/src/auth/multi-tenancy/credentials/server/encryption.ts +39 -39
- package/src/auth/multi-tenancy/credentials/server/service.ts +60 -60
- package/src/auth/multi-tenancy/index.ts +17 -17
- package/src/auth/multi-tenancy/invitations/api-schemas.ts +107 -107
- package/src/auth/multi-tenancy/invitations/index.ts +37 -37
- package/src/auth/multi-tenancy/invitations/invitation.ts +86 -86
- package/src/auth/multi-tenancy/invitations/server/index.ts +25 -25
- package/src/auth/multi-tenancy/invitations/server/transforms.ts +24 -24
- package/src/auth/multi-tenancy/invitations/server/workos.ts +24 -24
- package/src/auth/multi-tenancy/invitations/supabase.ts +50 -50
- package/src/auth/multi-tenancy/memberships/api-schemas.ts +126 -126
- package/src/auth/multi-tenancy/memberships/index.ts +21 -21
- package/src/auth/multi-tenancy/memberships/membership.ts +138 -138
- package/src/auth/multi-tenancy/memberships/server/index.ts +15 -15
- package/src/auth/multi-tenancy/memberships/server/transforms.ts +32 -32
- package/src/auth/multi-tenancy/memberships/server/workos.ts +21 -21
- package/src/auth/multi-tenancy/memberships/supabase.ts +46 -46
- package/src/auth/multi-tenancy/organizations/api-schemas.ts +128 -128
- package/src/auth/multi-tenancy/organizations/index.ts +23 -23
- package/src/auth/multi-tenancy/organizations/organization.ts +24 -24
- package/src/auth/multi-tenancy/organizations/server/index.ts +10 -10
- package/src/auth/multi-tenancy/organizations/server/transforms.ts +35 -35
- package/src/auth/multi-tenancy/organizations/server/workos.ts +20 -20
- package/src/auth/multi-tenancy/types.ts +83 -83
- package/src/auth/multi-tenancy/users/api-schemas.ts +194 -194
- package/src/auth/multi-tenancy/users/index.ts +27 -27
- package/src/auth/multi-tenancy/users/server/index.ts +19 -19
- package/src/auth/multi-tenancy/users/server/transforms.ts +21 -21
- package/src/auth/multi-tenancy/users/server/workos.ts +16 -16
- package/src/auth/multi-tenancy/users/user.ts +65 -65
- package/src/business/README.md +52 -52
- package/src/business/__tests__/entities-published.test.ts +33 -33
- package/src/business/acquisition/api-schemas.ts +759 -759
- package/src/business/acquisition/index.ts +109 -109
- package/src/business/acquisition/types.ts +402 -402
- package/src/business/base-entities.test.ts +481 -481
- package/src/business/base-entities.ts +241 -241
- package/src/business/entities-published.ts +24 -24
- package/src/business/index.ts +15 -15
- package/src/business/pdf/browser/pdfmake-browser.ts +229 -229
- package/src/business/pdf/index.ts +10 -10
- package/src/business/pdf/server/index.ts +21 -21
- package/src/business/pdf/server/themes/default.ts +8 -8
- package/src/business/pdf/server/themes/index.ts +9 -9
- package/src/business/pdf/server/themes/types.ts +8 -8
- package/src/business/pdf/types.ts +272 -272
- package/src/business/projects/index.ts +2 -2
- package/src/business/projects/sse-events.ts +21 -21
- package/src/business/projects/types.ts +89 -89
- package/src/business/sales/api-schemas.ts +75 -75
- package/src/business/seo/__tests__/linking.test.ts +549 -549
- package/src/business/seo/__tests__/types.test.ts +404 -404
- package/src/business/seo/index.ts +2 -2
- package/src/business/seo/linking.ts +281 -281
- package/src/business/seo/types.ts +199 -199
- package/src/commands/queue/index.ts +3 -3
- package/src/commands/queue/schemas.test.ts +593 -593
- package/src/commands/queue/schemas.ts +125 -125
- package/src/commands/queue/sse-events.ts +61 -61
- package/src/commands/queue/types/action.ts +52 -52
- package/src/commands/queue/types/checkpoint.ts +44 -44
- package/src/commands/queue/types/index.ts +7 -7
- package/src/commands/queue/types/task.ts +116 -116
- package/src/commands/queue/types.ts +14 -14
- package/src/content/distribution-metadata.ts +61 -61
- package/src/content/index.ts +10 -10
- package/src/deployments/index.ts +22 -22
- package/src/execution/core/__tests__/archived-logs.test.ts +72 -72
- package/src/execution/core/index.ts +11 -11
- package/src/execution/core/runner-types.ts +80 -80
- package/src/execution/core/server/environment.ts +31 -31
- package/src/execution/core/sse-executions.ts +119 -119
- package/src/execution/core/types.ts +29 -29
- package/src/execution/engine/__tests__/fixtures/test-agents.ts +4 -4
- package/src/execution/engine/__tests__/timeout.test.ts +565 -565
- package/src/execution/engine/agent/__tests__/errors.test.ts +508 -508
- package/src/execution/engine/agent/actions/__tests__/processor.test.ts +531 -531
- package/src/execution/engine/agent/actions/executor.ts +205 -205
- package/src/execution/engine/agent/actions/navigate-knowledge-executor.ts +230 -230
- package/src/execution/engine/agent/actions/processor.ts +116 -116
- package/src/execution/engine/agent/actions/types.ts +70 -70
- package/src/execution/engine/agent/core/agent.ts +810 -810
- package/src/execution/engine/agent/core/types.ts +155 -155
- package/src/execution/engine/agent/errors.ts +251 -251
- package/src/execution/engine/agent/index.ts +78 -78
- package/src/execution/engine/agent/knowledge-map/types.ts +106 -106
- package/src/execution/engine/agent/knowledge-map/utils.ts +101 -101
- package/src/execution/engine/agent/memory/__tests__/manager.test.ts +754 -754
- package/src/execution/engine/agent/memory/domains.ts +99 -99
- package/src/execution/engine/agent/memory/manager.ts +365 -365
- package/src/execution/engine/agent/memory/processor.ts +66 -66
- package/src/execution/engine/agent/memory/types.ts +90 -90
- package/src/execution/engine/agent/memory/utils.ts +134 -134
- package/src/execution/engine/agent/observability/logging.ts +467 -467
- package/src/execution/engine/agent/observability/types.ts +64 -64
- package/src/execution/engine/agent/reasoning/adapters/agent-adapter-helpers.ts +349 -349
- package/src/execution/engine/agent/reasoning/processor.ts +92 -92
- package/src/execution/engine/agent/reasoning/prompt-sections/base-actions.ts +134 -134
- package/src/execution/engine/agent/reasoning/prompt-sections/completion.ts +49 -49
- package/src/execution/engine/agent/reasoning/prompt-sections/knowledge-map.ts +93 -93
- package/src/execution/engine/agent/reasoning/prompt-sections/memory.ts +65 -65
- package/src/execution/engine/agent/reasoning/prompt-sections/tools.ts +44 -44
- package/src/execution/engine/agent/reasoning/request-builder.ts +169 -169
- package/src/execution/engine/agent/reasoning/types.ts +18 -18
- package/src/execution/engine/base/errors.ts +118 -118
- package/src/execution/engine/base/index.ts +2 -2
- package/src/execution/engine/base/logging.ts +31 -31
- package/src/execution/engine/base/serialization.ts +324 -324
- package/src/execution/engine/base/types.ts +126 -126
- package/src/execution/engine/base/utils.ts +41 -41
- package/src/execution/engine/index.ts +434 -434
- package/src/execution/engine/interface/index.ts +1 -1
- package/src/execution/engine/interface/types.ts +62 -62
- package/src/execution/engine/llm/__tests__/model-info.test.ts +50 -50
- package/src/execution/engine/llm/__tests__/model-validation.test.ts +321 -321
- package/src/execution/engine/llm/__tests__/response-schema-validator.test.ts +115 -115
- package/src/execution/engine/llm/adapters/__tests__/adapter-factory.test.ts +375 -375
- package/src/execution/engine/llm/adapters/__tests__/anthropic-adapter.test.ts +463 -463
- package/src/execution/engine/llm/adapters/__tests__/anthropic.integration.test.ts +177 -177
- package/src/execution/engine/llm/adapters/__tests__/google-adapter.test.ts +722 -722
- package/src/execution/engine/llm/adapters/__tests__/google.integration.test.ts +376 -376
- package/src/execution/engine/llm/adapters/__tests__/openai-adapter.test.ts +551 -551
- package/src/execution/engine/llm/adapters/__tests__/openrouter-adapter.test.ts +563 -563
- package/src/execution/engine/llm/adapters/__tests__/openrouter.integration.test.ts +105 -105
- package/src/execution/engine/llm/adapters/__tests__/universal-adapter.test.ts +537 -537
- package/src/execution/engine/llm/adapters/circuit-breaker.ts +147 -147
- package/src/execution/engine/llm/adapters/index.ts +17 -17
- package/src/execution/engine/llm/adapters/mock-adapter.ts +116 -116
- package/src/execution/engine/llm/adapters/server/adapter-factory.ts +130 -130
- package/src/execution/engine/llm/adapters/server/anthropic.ts +137 -137
- package/src/execution/engine/llm/adapters/server/google.ts +283 -283
- package/src/execution/engine/llm/adapters/server/index.ts +12 -12
- package/src/execution/engine/llm/adapters/server/openai.ts +206 -206
- package/src/execution/engine/llm/adapters/server/openrouter.ts +235 -235
- package/src/execution/engine/llm/adapters/universal-adapter.ts +230 -230
- package/src/execution/engine/llm/errors.ts +186 -186
- package/src/execution/engine/llm/model-info.ts +332 -332
- package/src/execution/engine/llm/response-schema-validator.ts +113 -113
- package/src/execution/engine/llm/types.ts +86 -86
- package/src/execution/engine/test-utils/index.ts +6 -6
- package/src/execution/engine/test-utils/mocks.ts +56 -56
- package/src/execution/engine/tools/integration/base-integration-adapter.ts +50 -50
- package/src/execution/engine/tools/integration/index.ts +53 -53
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/anymailfinder-adapter.ts +73 -73
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/anymailfinder-tools.ts +209 -209
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/find-company-email/index.ts +82 -82
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/find-decision-maker-email/index.ts +122 -122
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/find-person-email/index.ts +89 -89
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/verify-email/index.ts +84 -84
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/index.ts +16 -16
- package/src/execution/engine/tools/integration/server/adapters/apify/__tests__/apify-run-actor.integration.test.ts +293 -293
- package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.ts +100 -100
- package/src/execution/engine/tools/integration/server/adapters/apify/apify-tools.ts +217 -217
- package/src/execution/engine/tools/integration/server/adapters/apify/fetch/get-dataset-items/index.ts +92 -92
- package/src/execution/engine/tools/integration/server/adapters/apify/fetch/run-actor/index.ts +218 -218
- package/src/execution/engine/tools/integration/server/adapters/apify/fetch/start-actor/index.ts +87 -87
- package/src/execution/engine/tools/integration/server/adapters/apify/index.ts +11 -11
- package/src/execution/engine/tools/integration/server/adapters/attio/__tests__/attio-crud.integration.test.ts +361 -361
- package/src/execution/engine/tools/integration/server/adapters/attio/attio-adapter.ts +162 -162
- package/src/execution/engine/tools/integration/server/adapters/attio/attio-tools.ts +594 -594
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/create-attribute/index.ts +214 -214
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/create-note/index.ts +152 -152
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/create-record/index.ts +141 -141
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/delete-note/index.ts +86 -86
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/delete-record/index.ts +105 -105
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/get-record/index.ts +118 -118
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-attributes/index.ts +165 -165
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-notes/index.ts +96 -96
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-objects/index.ts +104 -104
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-records/index.ts +156 -156
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/update-attribute/index.ts +220 -220
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/update-record/index.ts +140 -140
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/utils/types.ts +146 -146
- package/src/execution/engine/tools/integration/server/adapters/attio/index.ts +31 -31
- package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-adapter.ts +210 -210
- package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-tools.ts +104 -104
- package/src/execution/engine/tools/integration/server/adapters/google-sheets/__tests__/google-sheets.integration.test.ts +261 -261
- package/src/execution/engine/tools/integration/server/adapters/google-sheets/google-sheets-adapter.ts +1189 -1189
- package/src/execution/engine/tools/integration/server/adapters/google-sheets/google-sheets-tools.ts +641 -641
- package/src/execution/engine/tools/integration/server/adapters/google-sheets/index.ts +18 -18
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/activate-campaign/index.ts +86 -86
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/add-to-campaign/__tests__/index.test.ts +289 -289
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/add-to-campaign/index.ts +154 -154
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/bulk-add-leads/__tests__/index.test.ts +325 -325
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/bulk-add-leads/index.ts +153 -153
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/bulk-delete-leads/index.ts +84 -84
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/create-campaign/index.ts +125 -125
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/create-inbox-test/index.ts +107 -107
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/delete-campaign/index.ts +85 -85
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-account-health/index.ts +91 -91
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-campaign/index.ts +92 -92
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-campaign-analytics/__tests__/index.test.ts +195 -195
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-campaign-analytics/index.ts +113 -113
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-daily-campaign-analytics/index.ts +104 -104
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-emails/index.ts +155 -155
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-step-analytics/__tests__/index.test.ts +196 -196
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-step-analytics/index.ts +102 -102
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/list-campaigns/__tests__/index.test.ts +189 -189
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/list-campaigns/index.ts +87 -87
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/list-leads/index.ts +112 -112
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/patch-lead/index.ts +76 -76
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/pause-campaign/index.ts +86 -86
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/remove-from-subsequence/index.ts +98 -98
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/send-reply/index.ts +126 -126
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-campaign/__tests__/index.test.ts +193 -193
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-campaign/index.ts +99 -99
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-interest-status/__tests__/index.test.ts +621 -621
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-interest-status/index.ts +125 -125
- package/src/execution/engine/tools/integration/server/adapters/instantly/index.ts +29 -29
- package/src/execution/engine/tools/integration/server/adapters/instantly/instantly-adapter.ts +178 -178
- package/src/execution/engine/tools/integration/server/adapters/instantly/instantly-tools.ts +1473 -1473
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/fetch/check-credits/index.ts +59 -59
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/fetch/verify-email/index.ts +102 -102
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/index.ts +17 -17
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/millionverifier-adapter.ts +80 -80
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/millionverifier-tools.ts +102 -102
- package/src/execution/engine/tools/integration/server/adapters/resend/fetch/get-email/index.ts +102 -102
- package/src/execution/engine/tools/integration/server/adapters/resend/fetch/send-email/index.ts +134 -134
- package/src/execution/engine/tools/integration/server/adapters/resend/fetch/utils/types.ts +75 -75
- package/src/execution/engine/tools/integration/server/adapters/resend/index.ts +27 -27
- package/src/execution/engine/tools/integration/server/adapters/resend/resend-adapter.ts +108 -108
- package/src/execution/engine/tools/integration/server/adapters/resend/resend-tools.ts +132 -132
- package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/create-envelope/index.ts +274 -274
- package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/download-document/index.ts +230 -230
- package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/get-envelope/index.ts +133 -133
- package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/void-envelope/index.ts +90 -90
- package/src/execution/engine/tools/integration/server/adapters/stripe/fetch/utils/types.ts +210 -210
- package/src/execution/engine/tools/integration/server/adapters/stripe/stripe-adapter.ts +517 -517
- package/src/execution/engine/tools/integration/server/adapters/stripe/stripe-tools.ts +309 -309
- package/src/execution/engine/tools/integration/server/adapters/tomba/fetch/domain-search/index.ts +133 -133
- package/src/execution/engine/tools/integration/server/adapters/tomba/fetch/email-finder/index.ts +122 -122
- package/src/execution/engine/tools/integration/server/adapters/tomba/fetch/email-verifier/index.ts +111 -111
- package/src/execution/engine/tools/integration/server/adapters/tomba/index.ts +11 -11
- package/src/execution/engine/tools/integration/server/adapters/tomba/tomba-adapter.ts +78 -78
- package/src/execution/engine/tools/integration/server/adapters/tomba/tomba-tools.ts +222 -222
- package/src/execution/engine/tools/integration/server/index.ts +61 -61
- package/src/execution/engine/tools/integration/service.ts +161 -161
- package/src/execution/engine/tools/integration/tool.ts +253 -253
- package/src/execution/engine/tools/integration/types/anymailfinder.ts +74 -74
- package/src/execution/engine/tools/integration/types/apify.ts +92 -92
- package/src/execution/engine/tools/integration/types/index.ts +19 -19
- package/src/execution/engine/tools/integration/types/instantly.ts +557 -557
- package/src/execution/engine/tools/integration/types/millionverifier.ts +56 -56
- package/src/execution/engine/tools/integration/types/stripe.ts +162 -162
- package/src/execution/engine/tools/integration/types/tomba.ts +94 -94
- package/src/execution/engine/tools/lead-service-types.ts +884 -884
- package/src/execution/engine/tools/llm/index.ts +11 -11
- package/src/execution/engine/tools/llm/server/index.ts +8 -8
- package/src/execution/engine/tools/llm/server/llm-call-tool.ts +118 -118
- package/src/execution/engine/tools/platform/__tests__/pdf.test.ts +441 -441
- package/src/execution/engine/tools/platform/acquisition/company-tools.ts +248 -248
- package/src/execution/engine/tools/platform/acquisition/contact-tools.ts +319 -319
- package/src/execution/engine/tools/platform/acquisition/index.ts +43 -43
- package/src/execution/engine/tools/platform/acquisition/list-tools.ts +148 -148
- package/src/execution/engine/tools/platform/acquisition/types.ts +260 -260
- package/src/execution/engine/tools/platform/email/index.ts +122 -122
- package/src/execution/engine/tools/platform/email/types.ts +96 -96
- package/src/execution/engine/tools/platform/index.ts +157 -157
- package/src/execution/engine/tools/platform/notification.ts +81 -81
- package/src/execution/engine/tools/platform/pdf/index.ts +110 -110
- package/src/execution/engine/tools/platform/pdf/types.ts +77 -77
- package/src/execution/engine/tools/platform/scheduler.ts +87 -87
- package/src/execution/engine/tools/platform/storage/index.ts +370 -370
- package/src/execution/engine/tools/platform/types.ts +148 -148
- package/src/execution/engine/tools/registry.ts +700 -700
- package/src/execution/engine/tools/tool-maps.ts +786 -786
- package/src/execution/engine/tools/types.ts +233 -233
- package/src/execution/engine/workflow/__tests__/errors.test.ts +139 -139
- package/src/execution/engine/workflow/errors.ts +63 -63
- package/src/execution/engine/workflow/helpers/index.ts +11 -11
- package/src/execution/engine/workflow/helpers/server/index.ts +8 -8
- package/src/execution/engine/workflow/helpers/server/llm-call.ts +93 -93
- package/src/execution/engine/workflow/index.ts +19 -19
- package/src/execution/engine/workflow/log-truncate.ts +26 -26
- package/src/execution/engine/workflow/logging.ts +191 -191
- package/src/execution/engine/workflow/types.ts +182 -182
- package/src/execution/engine/workflow/utils.ts +280 -280
- package/src/execution/engine/workflow/workflow.ts +168 -168
- package/src/execution/index.ts +3 -3
- package/src/execution/scheduler/__tests__/api-schemas.test.ts +733 -733
- package/src/execution/scheduler/__tests__/utils.test.ts +1009 -1009
- package/src/execution/scheduler/api-schemas.ts +296 -296
- package/src/execution/scheduler/index.ts +50 -50
- package/src/execution/scheduler/schemas.ts +264 -264
- package/src/execution/scheduler/types.ts +111 -111
- package/src/execution/scheduler/utils.ts +364 -364
- package/src/forms/index.ts +7 -7
- package/src/forms/schemas.ts +69 -69
- package/src/forms/types.ts +70 -70
- package/src/index.ts +71 -60
- package/src/integrations/credentials/__tests__/schemas.test.ts +82 -82
- package/src/integrations/credentials/__tests__/utils.test.ts +144 -144
- package/src/integrations/credentials/api-schemas.ts +143 -143
- package/src/integrations/credentials/index.ts +32 -32
- package/src/integrations/credentials/schemas.ts +164 -164
- package/src/integrations/credentials/utils.ts +59 -59
- package/src/integrations/oauth/__tests__/provider-registry.test.ts +59 -59
- package/src/integrations/oauth/api-schemas.ts +92 -92
- package/src/integrations/oauth/index.ts +19 -19
- package/src/integrations/oauth/provider-registry.ts +61 -61
- package/src/integrations/oauth/server/__tests__/refresh-concurrent.test.ts +183 -183
- package/src/integrations/oauth/server/__tests__/refresh.test.ts +577 -577
- package/src/integrations/oauth/server/credentials.ts +39 -39
- package/src/integrations/oauth/server/refresh.ts +214 -214
- package/src/integrations/oauth/types.ts +34 -34
- package/src/integrations/webhook-endpoints/__tests__/api-schemas.test.ts +318 -318
- package/src/integrations/webhook-endpoints/api-schemas.ts +102 -102
- package/src/integrations/webhook-endpoints/index.ts +28 -28
- package/src/integrations/webhook-endpoints/types.ts +51 -51
- package/src/operations/activities/api-schemas.ts +79 -79
- package/src/operations/activities/index.ts +9 -9
- package/src/operations/activities/sse-events.ts +30 -30
- package/src/operations/activities/types.ts +63 -63
- package/src/operations/debug-logs/client.ts +60 -60
- package/src/operations/debug-logs/debug-logger.ts +83 -83
- package/src/operations/debug-logs/index.ts +8 -8
- package/src/operations/debug-logs/server.ts +19 -19
- package/src/operations/debug-logs/types.ts +33 -33
- package/src/operations/index.ts +50 -50
- package/src/operations/notifications/api-schemas.ts +91 -91
- package/src/operations/notifications/index.ts +3 -3
- package/src/operations/notifications/sse-events.ts +21 -21
- package/src/operations/notifications/types.ts +47 -47
- package/src/operations/observability/__tests__/openrouter-cost-flow.test.ts +297 -297
- package/src/operations/observability/__tests__/utils.test.ts +54 -54
- package/src/operations/observability/ai-usage-collector.ts +64 -64
- package/src/operations/observability/index.ts +13 -13
- package/src/operations/observability/metrics-collector.ts +49 -49
- package/src/operations/observability/schemas.ts +39 -39
- package/src/operations/observability/types.ts +463 -463
- package/src/operations/observability/utils.ts +77 -77
- package/src/operations/sessions/__tests__/manager.test.ts +821 -821
- package/src/operations/sessions/index.ts +26 -26
- package/src/operations/sessions/server/manager.ts +90 -90
- package/src/operations/sessions/server/session.ts +180 -180
- package/src/operations/sessions/types.ts +98 -98
- package/src/operations/triggers/index.ts +12 -12
- package/src/operations/triggers/webhook/definitions/instantly-account-error.ts +44 -44
- package/src/operations/triggers/webhook/definitions/instantly-auto-reply-received.ts +51 -51
- package/src/operations/triggers/webhook/definitions/instantly-campaign-completed.ts +45 -45
- package/src/operations/triggers/webhook/definitions/instantly-email-bounced.ts +49 -49
- package/src/operations/triggers/webhook/definitions/instantly-lead-unsubscribed.ts +45 -45
- package/src/operations/triggers/webhook/definitions/instantly-reply-received.ts +54 -54
- package/src/operations/triggers/webhook/index.ts +35 -35
- package/src/operations/triggers/webhook/types.ts +74 -74
- package/src/organization-model/README.md +97 -97
- package/src/organization-model/__tests__/defaults.test.ts +175 -175
- package/src/organization-model/__tests__/domains/customers.test.ts +295 -295
- package/src/organization-model/__tests__/domains/goals.test.ts +479 -479
- package/src/organization-model/__tests__/domains/identity.test.ts +279 -279
- package/src/organization-model/__tests__/domains/navigation.test.ts +212 -212
- package/src/organization-model/__tests__/domains/offerings.test.ts +419 -419
- package/src/organization-model/__tests__/domains/operations.test.ts +203 -203
- package/src/organization-model/__tests__/domains/resource-mappings.test.ts +362 -362
- package/src/organization-model/__tests__/domains/roles.test.ts +347 -347
- package/src/organization-model/__tests__/domains/statuses.test.ts +243 -243
- package/src/organization-model/__tests__/foundation.test.ts +105 -105
- package/src/organization-model/__tests__/graph.test.ts +894 -894
- package/src/organization-model/__tests__/resolve.test.ts +690 -690
- package/src/organization-model/__tests__/schema.test.ts +407 -407
- package/src/organization-model/contracts.ts +14 -14
- package/src/organization-model/defaults.ts +148 -148
- package/src/organization-model/domains/branding.ts +22 -22
- package/src/organization-model/domains/customers.ts +75 -75
- package/src/organization-model/domains/features.ts +22 -22
- package/src/organization-model/domains/goals.ts +80 -80
- package/src/organization-model/domains/identity.ts +94 -94
- package/src/organization-model/domains/navigation.ts +391 -391
- package/src/organization-model/domains/offerings.ts +66 -66
- package/src/organization-model/domains/operations.ts +85 -85
- package/src/organization-model/domains/projects.ts +48 -48
- package/src/organization-model/domains/prospecting.ts +33 -33
- package/src/organization-model/domains/roles.ts +55 -55
- package/src/organization-model/domains/sales.ts +94 -94
- package/src/organization-model/domains/shared.ts +62 -62
- package/src/organization-model/domains/statuses.ts +130 -130
- package/src/organization-model/foundation.ts +97 -97
- package/src/organization-model/graph/build.ts +399 -399
- package/src/organization-model/graph/index.ts +4 -4
- package/src/organization-model/graph/schema.ts +48 -48
- package/src/organization-model/graph/types.ts +40 -40
- package/src/organization-model/index.ts +13 -13
- package/src/organization-model/organization-graph.mdx +272 -272
- package/src/organization-model/organization-model.mdx +320 -320
- package/src/organization-model/published.ts +85 -85
- package/src/organization-model/resolve.ts +66 -66
- package/src/organization-model/schema.ts +287 -287
- package/src/organization-model/types.ts +46 -46
- package/src/platform/api/index.ts +1 -1
- package/src/platform/api/types.ts +35 -35
- package/src/platform/constants/http.ts +37 -37
- package/src/platform/constants/index.ts +5 -5
- package/src/platform/constants/limits.ts +32 -32
- package/src/platform/constants/resilience.ts +51 -51
- package/src/platform/constants/timeouts.ts +20 -20
- package/src/platform/constants/versions.ts +3 -3
- package/src/platform/registry/__tests__/resource-registry-static.test.ts +347 -347
- package/src/platform/registry/__tests__/resource-registry.integration.test.ts +1028 -1028
- package/src/platform/registry/__tests__/resource-registry.list-executable.test.ts +393 -393
- package/src/platform/registry/__tests__/resource-registry.test.ts +2005 -2005
- package/src/platform/registry/__tests__/serialization.test.ts +1127 -1127
- package/src/platform/registry/command-view.ts +180 -180
- package/src/platform/registry/domains.ts +165 -165
- package/src/platform/registry/index.ts +93 -93
- package/src/platform/registry/reserved.ts +24 -24
- package/src/platform/registry/resource-metadata.ts +59 -59
- package/src/platform/registry/resource-registry.command-queue-groups.test.ts +129 -129
- package/src/platform/registry/resource-registry.ts +876 -876
- package/src/platform/registry/serialization.ts +273 -273
- package/src/platform/registry/serialized-types.ts +231 -231
- package/src/platform/registry/stats-types.ts +66 -66
- package/src/platform/registry/types.ts +404 -404
- package/src/platform/registry/validation.ts +513 -513
- package/src/platform/resilience/__tests__/rate-limiter.test.ts +471 -471
- package/src/platform/resilience/circuit-breaker.ts +164 -164
- package/src/platform/resilience/errors.ts +68 -68
- package/src/platform/resilience/http-error-mapper.ts +129 -129
- package/src/platform/resilience/index.ts +93 -93
- package/src/platform/resilience/rate-limiter-types.ts +46 -46
- package/src/platform/resilience/rate-limiter.ts +140 -140
- package/src/platform/resilience/retry.ts +89 -89
- package/src/platform/resilience/timeout.ts +63 -63
- package/src/platform/sse/events.ts +37 -37
- package/src/platform/sse/index.ts +7 -7
- package/src/platform/utils/__tests__/validation.test.ts +1083 -1083
- package/src/platform/utils/currency.ts +96 -96
- package/src/platform/utils/debounce.ts +52 -52
- package/src/platform/utils/error.ts +41 -41
- package/src/platform/utils/hmac.test.ts +97 -97
- package/src/platform/utils/index.ts +32 -32
- package/src/platform/utils/server/betterstack-logger.ts +210 -210
- package/src/platform/utils/server/hmac.ts +44 -44
- package/src/platform/utils/server/unsubscribe.ts +111 -111
- package/src/platform/utils/token-counter.ts +96 -96
- package/src/platform/utils/validation.ts +425 -425
- package/src/projects/api-schemas.ts +268 -268
- package/src/published.ts +1 -1
- package/src/reference/_generated/contracts.md +607 -607
- package/src/reference/glossary.md +105 -105
- package/src/requests/__tests__/api-schemas.test.ts +277 -277
- package/src/requests/api-schemas.ts +83 -83
- package/src/requests/index.ts +1 -1
- package/src/scaffold-registry/__tests__/index.test.ts +17 -0
- package/src/scaffold-registry/__tests__/schema.test.ts +329 -230
- package/src/scaffold-registry/index.ts +205 -189
- package/src/scaffold-registry/schema.ts +196 -128
- package/src/server.ts +272 -272
- package/src/supabase/database.types.ts +2719 -2719
- package/src/supabase/helpers.ts +20 -20
- package/src/supabase/index.ts +52 -52
- package/src/supabase/server/client.ts +58 -58
- package/src/test-utils/README.md +38 -38
- package/src/test-utils/browser-mocks.ts +54 -54
- package/src/test-utils/fixtures/api-keys.ts +52 -52
- package/src/test-utils/fixtures/index.ts +4 -4
- package/src/test-utils/fixtures/memberships.ts +80 -80
- package/src/test-utils/fixtures/organizations.ts +69 -69
- package/src/test-utils/fixtures/users.ts +79 -79
- package/src/test-utils/index.ts +11 -11
- package/src/test-utils/mocks/index.ts +2 -2
- package/src/test-utils/mocks/supabase.ts +142 -142
- package/src/test-utils/mocks/workos.ts +108 -108
- package/src/test-utils/rls/RLSTestContext.ts +556 -556
- package/src/test-utils/rls/index.ts +1 -1
|
@@ -1,551 +1,551 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OpenAI Adapter Tests
|
|
3
|
-
* Verifies model options handling and configuration
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
7
|
-
import { OpenAIAdapter } from '../server/openai'
|
|
8
|
-
import { LLMResponseParseError } from '../../errors'
|
|
9
|
-
import type { LLMGenerateRequest } from '../../types'
|
|
10
|
-
|
|
11
|
-
// Mock OpenAI SDK
|
|
12
|
-
vi.mock('openai', () => {
|
|
13
|
-
return {
|
|
14
|
-
default: vi.fn().mockImplementation(() => ({
|
|
15
|
-
chat: {
|
|
16
|
-
completions: {
|
|
17
|
-
create: vi.fn()
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}))
|
|
21
|
-
}
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
describe('OpenAIAdapter', () => {
|
|
25
|
-
const mockOpenAICreate = vi.fn()
|
|
26
|
-
|
|
27
|
-
beforeEach(async () => {
|
|
28
|
-
vi.clearAllMocks()
|
|
29
|
-
|
|
30
|
-
// Get the mock OpenAI constructor
|
|
31
|
-
const OpenAI = (await import('openai')).default as unknown as { mockImplementation: (fn: () => unknown) => void }
|
|
32
|
-
OpenAI.mockImplementation(() => ({
|
|
33
|
-
chat: {
|
|
34
|
-
completions: {
|
|
35
|
-
create: mockOpenAICreate
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}))
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
const baseRequest: LLMGenerateRequest = {
|
|
42
|
-
messages: [
|
|
43
|
-
{ role: 'system', content: 'You are a helpful assistant' },
|
|
44
|
-
{ role: 'user', content: 'Hello' }
|
|
45
|
-
],
|
|
46
|
-
responseSchema: {
|
|
47
|
-
type: 'object',
|
|
48
|
-
properties: {
|
|
49
|
-
response: { type: 'string' }
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
maxOutputTokens: 1000
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const mockOpenAIResponse = {
|
|
56
|
-
choices: [
|
|
57
|
-
{
|
|
58
|
-
message: {
|
|
59
|
-
content: JSON.stringify({ response: 'Hello! How can I help you?' })
|
|
60
|
-
},
|
|
61
|
-
finish_reason: 'stop'
|
|
62
|
-
}
|
|
63
|
-
],
|
|
64
|
-
usage: {
|
|
65
|
-
prompt_tokens: 10,
|
|
66
|
-
completion_tokens: 20,
|
|
67
|
-
total_tokens: 30
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
describe('Model Options - Basic Configuration', () => {
|
|
72
|
-
it('creates adapter without model options', async () => {
|
|
73
|
-
const adapter = new OpenAIAdapter({
|
|
74
|
-
apiKey: 'test-key',
|
|
75
|
-
model: 'gpt-5-mini'
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
79
|
-
|
|
80
|
-
await adapter.generate(baseRequest)
|
|
81
|
-
|
|
82
|
-
// Verify no model options were passed to OpenAI
|
|
83
|
-
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
84
|
-
expect(createParams).not.toHaveProperty('reasoning_effort')
|
|
85
|
-
expect(createParams).not.toHaveProperty('verbosity')
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
it('creates adapter with reasoning_effort option', async () => {
|
|
89
|
-
const adapter = new OpenAIAdapter({
|
|
90
|
-
apiKey: 'test-key',
|
|
91
|
-
model: 'gpt-5',
|
|
92
|
-
modelOptions: {
|
|
93
|
-
reasoning_effort: 'low'
|
|
94
|
-
}
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
98
|
-
|
|
99
|
-
await adapter.generate(baseRequest)
|
|
100
|
-
|
|
101
|
-
// Verify reasoning_effort was passed to OpenAI
|
|
102
|
-
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
103
|
-
expect(createParams).toHaveProperty('reasoning_effort', 'low')
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
it('creates adapter with verbosity option', async () => {
|
|
107
|
-
const adapter = new OpenAIAdapter({
|
|
108
|
-
apiKey: 'test-key',
|
|
109
|
-
model: 'gpt-5-mini',
|
|
110
|
-
modelOptions: {
|
|
111
|
-
verbosity: 'low'
|
|
112
|
-
}
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
116
|
-
|
|
117
|
-
await adapter.generate(baseRequest)
|
|
118
|
-
|
|
119
|
-
// Verify verbosity was passed to OpenAI
|
|
120
|
-
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
121
|
-
expect(createParams).toHaveProperty('verbosity', 'low')
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
it('creates adapter with both reasoning_effort and verbosity options', async () => {
|
|
125
|
-
const adapter = new OpenAIAdapter({
|
|
126
|
-
apiKey: 'test-key',
|
|
127
|
-
model: 'gpt-5',
|
|
128
|
-
modelOptions: {
|
|
129
|
-
reasoning_effort: 'medium',
|
|
130
|
-
verbosity: 'high'
|
|
131
|
-
}
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
135
|
-
|
|
136
|
-
await adapter.generate(baseRequest)
|
|
137
|
-
|
|
138
|
-
// Verify both options were passed to OpenAI
|
|
139
|
-
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
140
|
-
expect(createParams).toHaveProperty('reasoning_effort', 'medium')
|
|
141
|
-
expect(createParams).toHaveProperty('verbosity', 'high')
|
|
142
|
-
})
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
describe('Model Options - Option Values', () => {
|
|
146
|
-
it('supports minimal reasoning effort', async () => {
|
|
147
|
-
const adapter = new OpenAIAdapter({
|
|
148
|
-
apiKey: 'test-key',
|
|
149
|
-
model: 'gpt-5',
|
|
150
|
-
modelOptions: {
|
|
151
|
-
reasoning_effort: 'minimal'
|
|
152
|
-
}
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
156
|
-
|
|
157
|
-
await adapter.generate(baseRequest)
|
|
158
|
-
|
|
159
|
-
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
160
|
-
expect(createParams.reasoning_effort).toBe('minimal')
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
it('supports low reasoning effort', async () => {
|
|
164
|
-
const adapter = new OpenAIAdapter({
|
|
165
|
-
apiKey: 'test-key',
|
|
166
|
-
model: 'gpt-5',
|
|
167
|
-
modelOptions: {
|
|
168
|
-
reasoning_effort: 'low'
|
|
169
|
-
}
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
173
|
-
|
|
174
|
-
await adapter.generate(baseRequest)
|
|
175
|
-
|
|
176
|
-
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
177
|
-
expect(createParams.reasoning_effort).toBe('low')
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
it('supports medium reasoning effort', async () => {
|
|
181
|
-
const adapter = new OpenAIAdapter({
|
|
182
|
-
apiKey: 'test-key',
|
|
183
|
-
model: 'gpt-5',
|
|
184
|
-
modelOptions: {
|
|
185
|
-
reasoning_effort: 'medium'
|
|
186
|
-
}
|
|
187
|
-
})
|
|
188
|
-
|
|
189
|
-
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
190
|
-
|
|
191
|
-
await adapter.generate(baseRequest)
|
|
192
|
-
|
|
193
|
-
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
194
|
-
expect(createParams.reasoning_effort).toBe('medium')
|
|
195
|
-
})
|
|
196
|
-
|
|
197
|
-
it('supports high reasoning effort', async () => {
|
|
198
|
-
const adapter = new OpenAIAdapter({
|
|
199
|
-
apiKey: 'test-key',
|
|
200
|
-
model: 'gpt-5',
|
|
201
|
-
modelOptions: {
|
|
202
|
-
reasoning_effort: 'high'
|
|
203
|
-
}
|
|
204
|
-
})
|
|
205
|
-
|
|
206
|
-
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
207
|
-
|
|
208
|
-
await adapter.generate(baseRequest)
|
|
209
|
-
|
|
210
|
-
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
211
|
-
expect(createParams.reasoning_effort).toBe('high')
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
it('supports low verbosity', async () => {
|
|
215
|
-
const adapter = new OpenAIAdapter({
|
|
216
|
-
apiKey: 'test-key',
|
|
217
|
-
model: 'gpt-5-mini',
|
|
218
|
-
modelOptions: {
|
|
219
|
-
verbosity: 'low'
|
|
220
|
-
}
|
|
221
|
-
})
|
|
222
|
-
|
|
223
|
-
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
224
|
-
|
|
225
|
-
await adapter.generate(baseRequest)
|
|
226
|
-
|
|
227
|
-
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
228
|
-
expect(createParams.verbosity).toBe('low')
|
|
229
|
-
})
|
|
230
|
-
|
|
231
|
-
it('supports medium verbosity', async () => {
|
|
232
|
-
const adapter = new OpenAIAdapter({
|
|
233
|
-
apiKey: 'test-key',
|
|
234
|
-
model: 'gpt-5-mini',
|
|
235
|
-
modelOptions: {
|
|
236
|
-
verbosity: 'medium'
|
|
237
|
-
}
|
|
238
|
-
})
|
|
239
|
-
|
|
240
|
-
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
241
|
-
|
|
242
|
-
await adapter.generate(baseRequest)
|
|
243
|
-
|
|
244
|
-
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
245
|
-
expect(createParams.verbosity).toBe('medium')
|
|
246
|
-
})
|
|
247
|
-
|
|
248
|
-
it('supports high verbosity', async () => {
|
|
249
|
-
const adapter = new OpenAIAdapter({
|
|
250
|
-
apiKey: 'test-key',
|
|
251
|
-
model: 'gpt-5-mini',
|
|
252
|
-
modelOptions: {
|
|
253
|
-
verbosity: 'high'
|
|
254
|
-
}
|
|
255
|
-
})
|
|
256
|
-
|
|
257
|
-
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
258
|
-
|
|
259
|
-
await adapter.generate(baseRequest)
|
|
260
|
-
|
|
261
|
-
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
262
|
-
expect(createParams.verbosity).toBe('high')
|
|
263
|
-
})
|
|
264
|
-
})
|
|
265
|
-
|
|
266
|
-
describe('Model Options - Backward Compatibility', () => {
|
|
267
|
-
it('works with existing code that does not provide model options', async () => {
|
|
268
|
-
const adapter = new OpenAIAdapter({
|
|
269
|
-
apiKey: 'test-key',
|
|
270
|
-
model: 'gpt-5-mini'
|
|
271
|
-
// No modelOptions field
|
|
272
|
-
})
|
|
273
|
-
|
|
274
|
-
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
275
|
-
|
|
276
|
-
const response = await adapter.generate(baseRequest)
|
|
277
|
-
|
|
278
|
-
// Should complete successfully
|
|
279
|
-
expect(response.output).toEqual({ response: 'Hello! How can I help you?' })
|
|
280
|
-
expect(response.usage).toEqual({
|
|
281
|
-
inputTokens: 10,
|
|
282
|
-
outputTokens: 20,
|
|
283
|
-
totalTokens: 30
|
|
284
|
-
})
|
|
285
|
-
|
|
286
|
-
// No model options should be in the API call
|
|
287
|
-
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
288
|
-
expect(createParams).not.toHaveProperty('reasoning')
|
|
289
|
-
expect(createParams).not.toHaveProperty('text')
|
|
290
|
-
})
|
|
291
|
-
|
|
292
|
-
it('handles empty model options object', async () => {
|
|
293
|
-
const adapter = new OpenAIAdapter({
|
|
294
|
-
apiKey: 'test-key',
|
|
295
|
-
model: 'gpt-5-mini',
|
|
296
|
-
modelOptions: {}
|
|
297
|
-
})
|
|
298
|
-
|
|
299
|
-
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
300
|
-
|
|
301
|
-
await adapter.generate(baseRequest)
|
|
302
|
-
|
|
303
|
-
// No model options should be in the API call
|
|
304
|
-
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
305
|
-
expect(createParams).not.toHaveProperty('reasoning_effort')
|
|
306
|
-
expect(createParams).not.toHaveProperty('verbosity')
|
|
307
|
-
})
|
|
308
|
-
})
|
|
309
|
-
|
|
310
|
-
describe('Model Options - Integration with Other Parameters', () => {
|
|
311
|
-
it('combines model options with temperature and maxOutputTokens', async () => {
|
|
312
|
-
const adapter = new OpenAIAdapter({
|
|
313
|
-
apiKey: 'test-key',
|
|
314
|
-
model: 'gpt-5',
|
|
315
|
-
modelOptions: {
|
|
316
|
-
reasoning_effort: 'low',
|
|
317
|
-
verbosity: 'low'
|
|
318
|
-
}
|
|
319
|
-
})
|
|
320
|
-
|
|
321
|
-
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
322
|
-
|
|
323
|
-
await adapter.generate({
|
|
324
|
-
...baseRequest,
|
|
325
|
-
temperature: 0.7,
|
|
326
|
-
topP: 0.9,
|
|
327
|
-
maxOutputTokens: 2000
|
|
328
|
-
})
|
|
329
|
-
|
|
330
|
-
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
331
|
-
|
|
332
|
-
// Standard parameters
|
|
333
|
-
expect(createParams.temperature).toBe(0.7)
|
|
334
|
-
expect(createParams.top_p).toBe(0.9)
|
|
335
|
-
expect(createParams.max_completion_tokens).toBe(2000)
|
|
336
|
-
|
|
337
|
-
// Model options
|
|
338
|
-
expect(createParams.reasoning_effort).toBe('low')
|
|
339
|
-
expect(createParams.verbosity).toBe('low')
|
|
340
|
-
})
|
|
341
|
-
|
|
342
|
-
it('preserves standard OpenAI parameters when model options present', async () => {
|
|
343
|
-
const adapter = new OpenAIAdapter({
|
|
344
|
-
apiKey: 'test-key',
|
|
345
|
-
model: 'gpt-5',
|
|
346
|
-
modelOptions: {
|
|
347
|
-
reasoning_effort: 'high'
|
|
348
|
-
}
|
|
349
|
-
})
|
|
350
|
-
|
|
351
|
-
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
352
|
-
|
|
353
|
-
await adapter.generate(baseRequest)
|
|
354
|
-
|
|
355
|
-
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
356
|
-
|
|
357
|
-
// Standard parameters should still be present
|
|
358
|
-
expect(createParams.model).toBe('gpt-5')
|
|
359
|
-
expect(createParams.messages).toHaveLength(2)
|
|
360
|
-
expect(createParams.max_completion_tokens).toBe(1000)
|
|
361
|
-
expect(createParams.response_format).toEqual({
|
|
362
|
-
type: 'json_schema',
|
|
363
|
-
json_schema: {
|
|
364
|
-
name: 'llm_response',
|
|
365
|
-
schema: baseRequest.responseSchema
|
|
366
|
-
}
|
|
367
|
-
})
|
|
368
|
-
})
|
|
369
|
-
})
|
|
370
|
-
|
|
371
|
-
describe('Model Options - Multiple Calls', () => {
|
|
372
|
-
it('applies same model options to multiple generate calls', async () => {
|
|
373
|
-
const adapter = new OpenAIAdapter({
|
|
374
|
-
apiKey: 'test-key',
|
|
375
|
-
model: 'gpt-5',
|
|
376
|
-
modelOptions: {
|
|
377
|
-
reasoning_effort: 'low',
|
|
378
|
-
verbosity: 'low'
|
|
379
|
-
}
|
|
380
|
-
})
|
|
381
|
-
|
|
382
|
-
mockOpenAICreate.mockResolvedValue(mockOpenAIResponse)
|
|
383
|
-
|
|
384
|
-
// First call
|
|
385
|
-
await adapter.generate(baseRequest)
|
|
386
|
-
const firstCallParams = mockOpenAICreate.mock.calls[0][0]
|
|
387
|
-
expect(firstCallParams.reasoning_effort).toBe('low')
|
|
388
|
-
expect(firstCallParams.verbosity).toBe('low')
|
|
389
|
-
|
|
390
|
-
// Second call
|
|
391
|
-
await adapter.generate(baseRequest)
|
|
392
|
-
const secondCallParams = mockOpenAICreate.mock.calls[1][0]
|
|
393
|
-
expect(secondCallParams.reasoning_effort).toBe('low')
|
|
394
|
-
expect(secondCallParams.verbosity).toBe('low')
|
|
395
|
-
})
|
|
396
|
-
})
|
|
397
|
-
|
|
398
|
-
describe('JSON Parse Error Handling', () => {
|
|
399
|
-
it('throws LLMResponseParseError when response is invalid JSON', async () => {
|
|
400
|
-
const adapter = new OpenAIAdapter({
|
|
401
|
-
apiKey: 'test-key',
|
|
402
|
-
model: 'gpt-5-mini'
|
|
403
|
-
})
|
|
404
|
-
|
|
405
|
-
const invalidJsonResponse = {
|
|
406
|
-
choices: [
|
|
407
|
-
{
|
|
408
|
-
message: {
|
|
409
|
-
content: '{"response": "incomplete json' // Invalid JSON - unterminated string
|
|
410
|
-
},
|
|
411
|
-
finish_reason: 'stop'
|
|
412
|
-
}
|
|
413
|
-
],
|
|
414
|
-
usage: {
|
|
415
|
-
prompt_tokens: 10,
|
|
416
|
-
completion_tokens: 20,
|
|
417
|
-
total_tokens: 30
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
mockOpenAICreate.mockResolvedValueOnce(invalidJsonResponse)
|
|
422
|
-
|
|
423
|
-
await expect(adapter.generate(baseRequest)).rejects.toThrow(LLMResponseParseError)
|
|
424
|
-
})
|
|
425
|
-
|
|
426
|
-
it('includes truncated raw content in LLMResponseParseError context', async () => {
|
|
427
|
-
const adapter = new OpenAIAdapter({
|
|
428
|
-
apiKey: 'test-key',
|
|
429
|
-
model: 'gpt-5-mini'
|
|
430
|
-
})
|
|
431
|
-
|
|
432
|
-
const invalidJsonResponse = {
|
|
433
|
-
choices: [
|
|
434
|
-
{
|
|
435
|
-
message: {
|
|
436
|
-
content: '{"response": "incomplete'
|
|
437
|
-
},
|
|
438
|
-
finish_reason: 'stop'
|
|
439
|
-
}
|
|
440
|
-
],
|
|
441
|
-
usage: {
|
|
442
|
-
prompt_tokens: 10,
|
|
443
|
-
completion_tokens: 20,
|
|
444
|
-
total_tokens: 30
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
mockOpenAICreate.mockResolvedValueOnce(invalidJsonResponse)
|
|
449
|
-
|
|
450
|
-
try {
|
|
451
|
-
await adapter.generate(baseRequest)
|
|
452
|
-
expect.fail('Should have thrown')
|
|
453
|
-
} catch (error) {
|
|
454
|
-
expect(error).toBeInstanceOf(LLMResponseParseError)
|
|
455
|
-
const parseError = error as LLMResponseParseError
|
|
456
|
-
expect(parseError.context).toHaveProperty('rawContent')
|
|
457
|
-
expect(parseError.context).toHaveProperty('parseError')
|
|
458
|
-
expect(parseError.message).toContain('Failed to parse LLM response as JSON')
|
|
459
|
-
}
|
|
460
|
-
})
|
|
461
|
-
|
|
462
|
-
it('parses valid JSON successfully', async () => {
|
|
463
|
-
const adapter = new OpenAIAdapter({
|
|
464
|
-
apiKey: 'test-key',
|
|
465
|
-
model: 'gpt-5-mini'
|
|
466
|
-
})
|
|
467
|
-
|
|
468
|
-
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
469
|
-
|
|
470
|
-
const result = await adapter.generate(baseRequest)
|
|
471
|
-
expect(result.output).toEqual({ response: 'Hello! How can I help you?' })
|
|
472
|
-
})
|
|
473
|
-
})
|
|
474
|
-
|
|
475
|
-
describe('Error Handling with Better Stack Logging', () => {
|
|
476
|
-
let mockBslogExternalServiceError: ReturnType<typeof vi.fn>
|
|
477
|
-
let mockIsSystemError: ReturnType<typeof vi.fn>
|
|
478
|
-
|
|
479
|
-
beforeEach(() => {
|
|
480
|
-
// Mock Better Stack logger functions
|
|
481
|
-
mockBslogExternalServiceError = vi.fn()
|
|
482
|
-
mockIsSystemError = vi.fn()
|
|
483
|
-
|
|
484
|
-
// Mock the betterstack-logger module
|
|
485
|
-
vi.doMock('../../../utils/betterstack-logger.js', () => ({
|
|
486
|
-
bslogExternalServiceError: mockBslogExternalServiceError,
|
|
487
|
-
isSystemError: mockIsSystemError
|
|
488
|
-
}))
|
|
489
|
-
})
|
|
490
|
-
|
|
491
|
-
it('logs system errors (5xx) to Better Stack', async () => {
|
|
492
|
-
const adapter = new OpenAIAdapter({
|
|
493
|
-
apiKey: 'test-key',
|
|
494
|
-
model: 'gpt-5-mini',
|
|
495
|
-
modelOptions: {
|
|
496
|
-
reasoning_effort: 'low'
|
|
497
|
-
}
|
|
498
|
-
})
|
|
499
|
-
|
|
500
|
-
const systemError = new Error('OpenAI service unavailable')
|
|
501
|
-
Object.assign(systemError, { status: 503 })
|
|
502
|
-
|
|
503
|
-
mockIsSystemError.mockReturnValue(true)
|
|
504
|
-
mockOpenAICreate.mockRejectedValueOnce(systemError)
|
|
505
|
-
|
|
506
|
-
await expect(adapter.generate(baseRequest)).rejects.toThrow('OpenAI service unavailable')
|
|
507
|
-
|
|
508
|
-
// Verify error was re-thrown
|
|
509
|
-
expect(mockOpenAICreate).toHaveBeenCalledTimes(1)
|
|
510
|
-
})
|
|
511
|
-
|
|
512
|
-
it('does not log user errors (4xx) to Better Stack', async () => {
|
|
513
|
-
const adapter = new OpenAIAdapter({
|
|
514
|
-
apiKey: 'test-key',
|
|
515
|
-
model: 'gpt-5-mini'
|
|
516
|
-
})
|
|
517
|
-
|
|
518
|
-
const userError = new Error('Invalid API key')
|
|
519
|
-
Object.assign(userError, { status: 401 })
|
|
520
|
-
|
|
521
|
-
mockIsSystemError.mockReturnValue(false)
|
|
522
|
-
mockOpenAICreate.mockRejectedValueOnce(userError)
|
|
523
|
-
|
|
524
|
-
await expect(adapter.generate(baseRequest)).rejects.toThrow('Invalid API key')
|
|
525
|
-
|
|
526
|
-
// Verify error was re-thrown
|
|
527
|
-
expect(mockOpenAICreate).toHaveBeenCalledTimes(1)
|
|
528
|
-
})
|
|
529
|
-
|
|
530
|
-
it('includes rich context in Better Stack logs', async () => {
|
|
531
|
-
const adapter = new OpenAIAdapter({
|
|
532
|
-
apiKey: 'test-key',
|
|
533
|
-
model: 'gpt-5',
|
|
534
|
-
modelOptions: {
|
|
535
|
-
reasoning_effort: 'medium'
|
|
536
|
-
}
|
|
537
|
-
})
|
|
538
|
-
|
|
539
|
-
const systemError = new Error('Rate limit exceeded')
|
|
540
|
-
Object.assign(systemError, { status: 529 })
|
|
541
|
-
|
|
542
|
-
mockIsSystemError.mockReturnValue(true)
|
|
543
|
-
mockOpenAICreate.mockRejectedValueOnce(systemError)
|
|
544
|
-
|
|
545
|
-
await expect(adapter.generate(baseRequest)).rejects.toThrow('Rate limit exceeded')
|
|
546
|
-
|
|
547
|
-
// Verify error was re-thrown
|
|
548
|
-
expect(mockOpenAICreate).toHaveBeenCalledTimes(1)
|
|
549
|
-
})
|
|
550
|
-
})
|
|
551
|
-
})
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI Adapter Tests
|
|
3
|
+
* Verifies model options handling and configuration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
7
|
+
import { OpenAIAdapter } from '../server/openai'
|
|
8
|
+
import { LLMResponseParseError } from '../../errors'
|
|
9
|
+
import type { LLMGenerateRequest } from '../../types'
|
|
10
|
+
|
|
11
|
+
// Mock OpenAI SDK
|
|
12
|
+
vi.mock('openai', () => {
|
|
13
|
+
return {
|
|
14
|
+
default: vi.fn().mockImplementation(() => ({
|
|
15
|
+
chat: {
|
|
16
|
+
completions: {
|
|
17
|
+
create: vi.fn()
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}))
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
describe('OpenAIAdapter', () => {
|
|
25
|
+
const mockOpenAICreate = vi.fn()
|
|
26
|
+
|
|
27
|
+
beforeEach(async () => {
|
|
28
|
+
vi.clearAllMocks()
|
|
29
|
+
|
|
30
|
+
// Get the mock OpenAI constructor
|
|
31
|
+
const OpenAI = (await import('openai')).default as unknown as { mockImplementation: (fn: () => unknown) => void }
|
|
32
|
+
OpenAI.mockImplementation(() => ({
|
|
33
|
+
chat: {
|
|
34
|
+
completions: {
|
|
35
|
+
create: mockOpenAICreate
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}))
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const baseRequest: LLMGenerateRequest = {
|
|
42
|
+
messages: [
|
|
43
|
+
{ role: 'system', content: 'You are a helpful assistant' },
|
|
44
|
+
{ role: 'user', content: 'Hello' }
|
|
45
|
+
],
|
|
46
|
+
responseSchema: {
|
|
47
|
+
type: 'object',
|
|
48
|
+
properties: {
|
|
49
|
+
response: { type: 'string' }
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
maxOutputTokens: 1000
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const mockOpenAIResponse = {
|
|
56
|
+
choices: [
|
|
57
|
+
{
|
|
58
|
+
message: {
|
|
59
|
+
content: JSON.stringify({ response: 'Hello! How can I help you?' })
|
|
60
|
+
},
|
|
61
|
+
finish_reason: 'stop'
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
usage: {
|
|
65
|
+
prompt_tokens: 10,
|
|
66
|
+
completion_tokens: 20,
|
|
67
|
+
total_tokens: 30
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
describe('Model Options - Basic Configuration', () => {
|
|
72
|
+
it('creates adapter without model options', async () => {
|
|
73
|
+
const adapter = new OpenAIAdapter({
|
|
74
|
+
apiKey: 'test-key',
|
|
75
|
+
model: 'gpt-5-mini'
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
79
|
+
|
|
80
|
+
await adapter.generate(baseRequest)
|
|
81
|
+
|
|
82
|
+
// Verify no model options were passed to OpenAI
|
|
83
|
+
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
84
|
+
expect(createParams).not.toHaveProperty('reasoning_effort')
|
|
85
|
+
expect(createParams).not.toHaveProperty('verbosity')
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('creates adapter with reasoning_effort option', async () => {
|
|
89
|
+
const adapter = new OpenAIAdapter({
|
|
90
|
+
apiKey: 'test-key',
|
|
91
|
+
model: 'gpt-5',
|
|
92
|
+
modelOptions: {
|
|
93
|
+
reasoning_effort: 'low'
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
98
|
+
|
|
99
|
+
await adapter.generate(baseRequest)
|
|
100
|
+
|
|
101
|
+
// Verify reasoning_effort was passed to OpenAI
|
|
102
|
+
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
103
|
+
expect(createParams).toHaveProperty('reasoning_effort', 'low')
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('creates adapter with verbosity option', async () => {
|
|
107
|
+
const adapter = new OpenAIAdapter({
|
|
108
|
+
apiKey: 'test-key',
|
|
109
|
+
model: 'gpt-5-mini',
|
|
110
|
+
modelOptions: {
|
|
111
|
+
verbosity: 'low'
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
116
|
+
|
|
117
|
+
await adapter.generate(baseRequest)
|
|
118
|
+
|
|
119
|
+
// Verify verbosity was passed to OpenAI
|
|
120
|
+
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
121
|
+
expect(createParams).toHaveProperty('verbosity', 'low')
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('creates adapter with both reasoning_effort and verbosity options', async () => {
|
|
125
|
+
const adapter = new OpenAIAdapter({
|
|
126
|
+
apiKey: 'test-key',
|
|
127
|
+
model: 'gpt-5',
|
|
128
|
+
modelOptions: {
|
|
129
|
+
reasoning_effort: 'medium',
|
|
130
|
+
verbosity: 'high'
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
135
|
+
|
|
136
|
+
await adapter.generate(baseRequest)
|
|
137
|
+
|
|
138
|
+
// Verify both options were passed to OpenAI
|
|
139
|
+
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
140
|
+
expect(createParams).toHaveProperty('reasoning_effort', 'medium')
|
|
141
|
+
expect(createParams).toHaveProperty('verbosity', 'high')
|
|
142
|
+
})
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
describe('Model Options - Option Values', () => {
|
|
146
|
+
it('supports minimal reasoning effort', async () => {
|
|
147
|
+
const adapter = new OpenAIAdapter({
|
|
148
|
+
apiKey: 'test-key',
|
|
149
|
+
model: 'gpt-5',
|
|
150
|
+
modelOptions: {
|
|
151
|
+
reasoning_effort: 'minimal'
|
|
152
|
+
}
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
156
|
+
|
|
157
|
+
await adapter.generate(baseRequest)
|
|
158
|
+
|
|
159
|
+
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
160
|
+
expect(createParams.reasoning_effort).toBe('minimal')
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
it('supports low reasoning effort', async () => {
|
|
164
|
+
const adapter = new OpenAIAdapter({
|
|
165
|
+
apiKey: 'test-key',
|
|
166
|
+
model: 'gpt-5',
|
|
167
|
+
modelOptions: {
|
|
168
|
+
reasoning_effort: 'low'
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
173
|
+
|
|
174
|
+
await adapter.generate(baseRequest)
|
|
175
|
+
|
|
176
|
+
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
177
|
+
expect(createParams.reasoning_effort).toBe('low')
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it('supports medium reasoning effort', async () => {
|
|
181
|
+
const adapter = new OpenAIAdapter({
|
|
182
|
+
apiKey: 'test-key',
|
|
183
|
+
model: 'gpt-5',
|
|
184
|
+
modelOptions: {
|
|
185
|
+
reasoning_effort: 'medium'
|
|
186
|
+
}
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
190
|
+
|
|
191
|
+
await adapter.generate(baseRequest)
|
|
192
|
+
|
|
193
|
+
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
194
|
+
expect(createParams.reasoning_effort).toBe('medium')
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
it('supports high reasoning effort', async () => {
|
|
198
|
+
const adapter = new OpenAIAdapter({
|
|
199
|
+
apiKey: 'test-key',
|
|
200
|
+
model: 'gpt-5',
|
|
201
|
+
modelOptions: {
|
|
202
|
+
reasoning_effort: 'high'
|
|
203
|
+
}
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
207
|
+
|
|
208
|
+
await adapter.generate(baseRequest)
|
|
209
|
+
|
|
210
|
+
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
211
|
+
expect(createParams.reasoning_effort).toBe('high')
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
it('supports low verbosity', async () => {
|
|
215
|
+
const adapter = new OpenAIAdapter({
|
|
216
|
+
apiKey: 'test-key',
|
|
217
|
+
model: 'gpt-5-mini',
|
|
218
|
+
modelOptions: {
|
|
219
|
+
verbosity: 'low'
|
|
220
|
+
}
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
224
|
+
|
|
225
|
+
await adapter.generate(baseRequest)
|
|
226
|
+
|
|
227
|
+
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
228
|
+
expect(createParams.verbosity).toBe('low')
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
it('supports medium verbosity', async () => {
|
|
232
|
+
const adapter = new OpenAIAdapter({
|
|
233
|
+
apiKey: 'test-key',
|
|
234
|
+
model: 'gpt-5-mini',
|
|
235
|
+
modelOptions: {
|
|
236
|
+
verbosity: 'medium'
|
|
237
|
+
}
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
241
|
+
|
|
242
|
+
await adapter.generate(baseRequest)
|
|
243
|
+
|
|
244
|
+
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
245
|
+
expect(createParams.verbosity).toBe('medium')
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
it('supports high verbosity', async () => {
|
|
249
|
+
const adapter = new OpenAIAdapter({
|
|
250
|
+
apiKey: 'test-key',
|
|
251
|
+
model: 'gpt-5-mini',
|
|
252
|
+
modelOptions: {
|
|
253
|
+
verbosity: 'high'
|
|
254
|
+
}
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
258
|
+
|
|
259
|
+
await adapter.generate(baseRequest)
|
|
260
|
+
|
|
261
|
+
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
262
|
+
expect(createParams.verbosity).toBe('high')
|
|
263
|
+
})
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
describe('Model Options - Backward Compatibility', () => {
|
|
267
|
+
it('works with existing code that does not provide model options', async () => {
|
|
268
|
+
const adapter = new OpenAIAdapter({
|
|
269
|
+
apiKey: 'test-key',
|
|
270
|
+
model: 'gpt-5-mini'
|
|
271
|
+
// No modelOptions field
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
275
|
+
|
|
276
|
+
const response = await adapter.generate(baseRequest)
|
|
277
|
+
|
|
278
|
+
// Should complete successfully
|
|
279
|
+
expect(response.output).toEqual({ response: 'Hello! How can I help you?' })
|
|
280
|
+
expect(response.usage).toEqual({
|
|
281
|
+
inputTokens: 10,
|
|
282
|
+
outputTokens: 20,
|
|
283
|
+
totalTokens: 30
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
// No model options should be in the API call
|
|
287
|
+
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
288
|
+
expect(createParams).not.toHaveProperty('reasoning')
|
|
289
|
+
expect(createParams).not.toHaveProperty('text')
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
it('handles empty model options object', async () => {
|
|
293
|
+
const adapter = new OpenAIAdapter({
|
|
294
|
+
apiKey: 'test-key',
|
|
295
|
+
model: 'gpt-5-mini',
|
|
296
|
+
modelOptions: {}
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
300
|
+
|
|
301
|
+
await adapter.generate(baseRequest)
|
|
302
|
+
|
|
303
|
+
// No model options should be in the API call
|
|
304
|
+
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
305
|
+
expect(createParams).not.toHaveProperty('reasoning_effort')
|
|
306
|
+
expect(createParams).not.toHaveProperty('verbosity')
|
|
307
|
+
})
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
describe('Model Options - Integration with Other Parameters', () => {
|
|
311
|
+
it('combines model options with temperature and maxOutputTokens', async () => {
|
|
312
|
+
const adapter = new OpenAIAdapter({
|
|
313
|
+
apiKey: 'test-key',
|
|
314
|
+
model: 'gpt-5',
|
|
315
|
+
modelOptions: {
|
|
316
|
+
reasoning_effort: 'low',
|
|
317
|
+
verbosity: 'low'
|
|
318
|
+
}
|
|
319
|
+
})
|
|
320
|
+
|
|
321
|
+
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
322
|
+
|
|
323
|
+
await adapter.generate({
|
|
324
|
+
...baseRequest,
|
|
325
|
+
temperature: 0.7,
|
|
326
|
+
topP: 0.9,
|
|
327
|
+
maxOutputTokens: 2000
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
331
|
+
|
|
332
|
+
// Standard parameters
|
|
333
|
+
expect(createParams.temperature).toBe(0.7)
|
|
334
|
+
expect(createParams.top_p).toBe(0.9)
|
|
335
|
+
expect(createParams.max_completion_tokens).toBe(2000)
|
|
336
|
+
|
|
337
|
+
// Model options
|
|
338
|
+
expect(createParams.reasoning_effort).toBe('low')
|
|
339
|
+
expect(createParams.verbosity).toBe('low')
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
it('preserves standard OpenAI parameters when model options present', async () => {
|
|
343
|
+
const adapter = new OpenAIAdapter({
|
|
344
|
+
apiKey: 'test-key',
|
|
345
|
+
model: 'gpt-5',
|
|
346
|
+
modelOptions: {
|
|
347
|
+
reasoning_effort: 'high'
|
|
348
|
+
}
|
|
349
|
+
})
|
|
350
|
+
|
|
351
|
+
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
352
|
+
|
|
353
|
+
await adapter.generate(baseRequest)
|
|
354
|
+
|
|
355
|
+
const createParams = mockOpenAICreate.mock.calls[0][0]
|
|
356
|
+
|
|
357
|
+
// Standard parameters should still be present
|
|
358
|
+
expect(createParams.model).toBe('gpt-5')
|
|
359
|
+
expect(createParams.messages).toHaveLength(2)
|
|
360
|
+
expect(createParams.max_completion_tokens).toBe(1000)
|
|
361
|
+
expect(createParams.response_format).toEqual({
|
|
362
|
+
type: 'json_schema',
|
|
363
|
+
json_schema: {
|
|
364
|
+
name: 'llm_response',
|
|
365
|
+
schema: baseRequest.responseSchema
|
|
366
|
+
}
|
|
367
|
+
})
|
|
368
|
+
})
|
|
369
|
+
})
|
|
370
|
+
|
|
371
|
+
describe('Model Options - Multiple Calls', () => {
|
|
372
|
+
it('applies same model options to multiple generate calls', async () => {
|
|
373
|
+
const adapter = new OpenAIAdapter({
|
|
374
|
+
apiKey: 'test-key',
|
|
375
|
+
model: 'gpt-5',
|
|
376
|
+
modelOptions: {
|
|
377
|
+
reasoning_effort: 'low',
|
|
378
|
+
verbosity: 'low'
|
|
379
|
+
}
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
mockOpenAICreate.mockResolvedValue(mockOpenAIResponse)
|
|
383
|
+
|
|
384
|
+
// First call
|
|
385
|
+
await adapter.generate(baseRequest)
|
|
386
|
+
const firstCallParams = mockOpenAICreate.mock.calls[0][0]
|
|
387
|
+
expect(firstCallParams.reasoning_effort).toBe('low')
|
|
388
|
+
expect(firstCallParams.verbosity).toBe('low')
|
|
389
|
+
|
|
390
|
+
// Second call
|
|
391
|
+
await adapter.generate(baseRequest)
|
|
392
|
+
const secondCallParams = mockOpenAICreate.mock.calls[1][0]
|
|
393
|
+
expect(secondCallParams.reasoning_effort).toBe('low')
|
|
394
|
+
expect(secondCallParams.verbosity).toBe('low')
|
|
395
|
+
})
|
|
396
|
+
})
|
|
397
|
+
|
|
398
|
+
describe('JSON Parse Error Handling', () => {
|
|
399
|
+
it('throws LLMResponseParseError when response is invalid JSON', async () => {
|
|
400
|
+
const adapter = new OpenAIAdapter({
|
|
401
|
+
apiKey: 'test-key',
|
|
402
|
+
model: 'gpt-5-mini'
|
|
403
|
+
})
|
|
404
|
+
|
|
405
|
+
const invalidJsonResponse = {
|
|
406
|
+
choices: [
|
|
407
|
+
{
|
|
408
|
+
message: {
|
|
409
|
+
content: '{"response": "incomplete json' // Invalid JSON - unterminated string
|
|
410
|
+
},
|
|
411
|
+
finish_reason: 'stop'
|
|
412
|
+
}
|
|
413
|
+
],
|
|
414
|
+
usage: {
|
|
415
|
+
prompt_tokens: 10,
|
|
416
|
+
completion_tokens: 20,
|
|
417
|
+
total_tokens: 30
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
mockOpenAICreate.mockResolvedValueOnce(invalidJsonResponse)
|
|
422
|
+
|
|
423
|
+
await expect(adapter.generate(baseRequest)).rejects.toThrow(LLMResponseParseError)
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
it('includes truncated raw content in LLMResponseParseError context', async () => {
|
|
427
|
+
const adapter = new OpenAIAdapter({
|
|
428
|
+
apiKey: 'test-key',
|
|
429
|
+
model: 'gpt-5-mini'
|
|
430
|
+
})
|
|
431
|
+
|
|
432
|
+
const invalidJsonResponse = {
|
|
433
|
+
choices: [
|
|
434
|
+
{
|
|
435
|
+
message: {
|
|
436
|
+
content: '{"response": "incomplete'
|
|
437
|
+
},
|
|
438
|
+
finish_reason: 'stop'
|
|
439
|
+
}
|
|
440
|
+
],
|
|
441
|
+
usage: {
|
|
442
|
+
prompt_tokens: 10,
|
|
443
|
+
completion_tokens: 20,
|
|
444
|
+
total_tokens: 30
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
mockOpenAICreate.mockResolvedValueOnce(invalidJsonResponse)
|
|
449
|
+
|
|
450
|
+
try {
|
|
451
|
+
await adapter.generate(baseRequest)
|
|
452
|
+
expect.fail('Should have thrown')
|
|
453
|
+
} catch (error) {
|
|
454
|
+
expect(error).toBeInstanceOf(LLMResponseParseError)
|
|
455
|
+
const parseError = error as LLMResponseParseError
|
|
456
|
+
expect(parseError.context).toHaveProperty('rawContent')
|
|
457
|
+
expect(parseError.context).toHaveProperty('parseError')
|
|
458
|
+
expect(parseError.message).toContain('Failed to parse LLM response as JSON')
|
|
459
|
+
}
|
|
460
|
+
})
|
|
461
|
+
|
|
462
|
+
it('parses valid JSON successfully', async () => {
|
|
463
|
+
const adapter = new OpenAIAdapter({
|
|
464
|
+
apiKey: 'test-key',
|
|
465
|
+
model: 'gpt-5-mini'
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
mockOpenAICreate.mockResolvedValueOnce(mockOpenAIResponse)
|
|
469
|
+
|
|
470
|
+
const result = await adapter.generate(baseRequest)
|
|
471
|
+
expect(result.output).toEqual({ response: 'Hello! How can I help you?' })
|
|
472
|
+
})
|
|
473
|
+
})
|
|
474
|
+
|
|
475
|
+
describe('Error Handling with Better Stack Logging', () => {
|
|
476
|
+
let mockBslogExternalServiceError: ReturnType<typeof vi.fn>
|
|
477
|
+
let mockIsSystemError: ReturnType<typeof vi.fn>
|
|
478
|
+
|
|
479
|
+
beforeEach(() => {
|
|
480
|
+
// Mock Better Stack logger functions
|
|
481
|
+
mockBslogExternalServiceError = vi.fn()
|
|
482
|
+
mockIsSystemError = vi.fn()
|
|
483
|
+
|
|
484
|
+
// Mock the betterstack-logger module
|
|
485
|
+
vi.doMock('../../../utils/betterstack-logger.js', () => ({
|
|
486
|
+
bslogExternalServiceError: mockBslogExternalServiceError,
|
|
487
|
+
isSystemError: mockIsSystemError
|
|
488
|
+
}))
|
|
489
|
+
})
|
|
490
|
+
|
|
491
|
+
it('logs system errors (5xx) to Better Stack', async () => {
|
|
492
|
+
const adapter = new OpenAIAdapter({
|
|
493
|
+
apiKey: 'test-key',
|
|
494
|
+
model: 'gpt-5-mini',
|
|
495
|
+
modelOptions: {
|
|
496
|
+
reasoning_effort: 'low'
|
|
497
|
+
}
|
|
498
|
+
})
|
|
499
|
+
|
|
500
|
+
const systemError = new Error('OpenAI service unavailable')
|
|
501
|
+
Object.assign(systemError, { status: 503 })
|
|
502
|
+
|
|
503
|
+
mockIsSystemError.mockReturnValue(true)
|
|
504
|
+
mockOpenAICreate.mockRejectedValueOnce(systemError)
|
|
505
|
+
|
|
506
|
+
await expect(adapter.generate(baseRequest)).rejects.toThrow('OpenAI service unavailable')
|
|
507
|
+
|
|
508
|
+
// Verify error was re-thrown
|
|
509
|
+
expect(mockOpenAICreate).toHaveBeenCalledTimes(1)
|
|
510
|
+
})
|
|
511
|
+
|
|
512
|
+
it('does not log user errors (4xx) to Better Stack', async () => {
|
|
513
|
+
const adapter = new OpenAIAdapter({
|
|
514
|
+
apiKey: 'test-key',
|
|
515
|
+
model: 'gpt-5-mini'
|
|
516
|
+
})
|
|
517
|
+
|
|
518
|
+
const userError = new Error('Invalid API key')
|
|
519
|
+
Object.assign(userError, { status: 401 })
|
|
520
|
+
|
|
521
|
+
mockIsSystemError.mockReturnValue(false)
|
|
522
|
+
mockOpenAICreate.mockRejectedValueOnce(userError)
|
|
523
|
+
|
|
524
|
+
await expect(adapter.generate(baseRequest)).rejects.toThrow('Invalid API key')
|
|
525
|
+
|
|
526
|
+
// Verify error was re-thrown
|
|
527
|
+
expect(mockOpenAICreate).toHaveBeenCalledTimes(1)
|
|
528
|
+
})
|
|
529
|
+
|
|
530
|
+
it('includes rich context in Better Stack logs', async () => {
|
|
531
|
+
const adapter = new OpenAIAdapter({
|
|
532
|
+
apiKey: 'test-key',
|
|
533
|
+
model: 'gpt-5',
|
|
534
|
+
modelOptions: {
|
|
535
|
+
reasoning_effort: 'medium'
|
|
536
|
+
}
|
|
537
|
+
})
|
|
538
|
+
|
|
539
|
+
const systemError = new Error('Rate limit exceeded')
|
|
540
|
+
Object.assign(systemError, { status: 529 })
|
|
541
|
+
|
|
542
|
+
mockIsSystemError.mockReturnValue(true)
|
|
543
|
+
mockOpenAICreate.mockRejectedValueOnce(systemError)
|
|
544
|
+
|
|
545
|
+
await expect(adapter.generate(baseRequest)).rejects.toThrow('Rate limit exceeded')
|
|
546
|
+
|
|
547
|
+
// Verify error was re-thrown
|
|
548
|
+
expect(mockOpenAICreate).toHaveBeenCalledTimes(1)
|
|
549
|
+
})
|
|
550
|
+
})
|
|
551
|
+
})
|