@llumiverse/drivers 1.2.0 → 1.4.0-dev.20260629.090753Z
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/lib/{types/adobe → adobe}/firefly.d.ts +2 -1
- package/lib/adobe/firefly.d.ts.map +1 -0
- package/lib/adobe/firefly.js +120 -0
- package/lib/adobe/firefly.js.map +1 -0
- package/lib/anthropic/index.d.ts +23 -0
- package/lib/anthropic/index.d.ts.map +1 -0
- package/lib/anthropic/index.js +68 -0
- package/lib/anthropic/index.js.map +1 -0
- package/lib/{types/azure → azure}/azure_foundry.d.ts +9 -7
- package/lib/azure/azure_foundry.d.ts.map +1 -0
- package/lib/{esm/azure → azure}/azure_foundry.js +138 -124
- package/lib/azure/azure_foundry.js.map +1 -0
- package/lib/{types/bedrock → bedrock}/converse.d.ts +2 -2
- package/lib/bedrock/converse.d.ts.map +1 -0
- package/lib/{esm/bedrock → bedrock}/converse.js +86 -79
- package/lib/bedrock/converse.js.map +1 -0
- package/lib/bedrock/embeddings.d.ts +4 -0
- package/lib/bedrock/embeddings.d.ts.map +1 -0
- package/lib/bedrock/embeddings.js +359 -0
- package/lib/bedrock/embeddings.js.map +1 -0
- package/lib/{types/bedrock → bedrock}/index.d.ts +33 -9
- package/lib/bedrock/index.d.ts.map +1 -0
- package/lib/{esm/bedrock → bedrock}/index.js +599 -324
- package/lib/bedrock/index.js.map +1 -0
- package/lib/{types/bedrock → bedrock}/nova-image-payload.d.ts +5 -5
- package/lib/bedrock/nova-image-payload.d.ts.map +1 -0
- package/lib/{esm/bedrock → bedrock}/nova-image-payload.js +31 -36
- package/lib/bedrock/nova-image-payload.js.map +1 -0
- package/lib/{types/bedrock → bedrock}/payloads.d.ts +1 -1
- package/lib/bedrock/payloads.d.ts.map +1 -0
- package/lib/{cjs/bedrock → bedrock}/payloads.js.map +1 -1
- package/lib/{types/bedrock → bedrock}/s3.d.ts +3 -2
- package/lib/bedrock/s3.d.ts.map +1 -0
- package/lib/{esm/bedrock → bedrock}/s3.js +27 -23
- package/lib/bedrock/s3.js.map +1 -0
- package/lib/{types/bedrock → bedrock}/twelvelabs.d.ts +8 -9
- package/lib/bedrock/twelvelabs.d.ts.map +1 -0
- package/lib/{esm/bedrock → bedrock}/twelvelabs.js +17 -21
- package/lib/bedrock/twelvelabs.js.map +1 -0
- package/lib/{types/groq → groq}/index.d.ts +6 -4
- package/lib/groq/index.d.ts.map +1 -0
- package/lib/{esm/groq → groq}/index.js +72 -39
- package/lib/groq/index.js.map +1 -0
- package/lib/{types/huggingface_ie.d.ts → huggingface_ie.d.ts} +4 -3
- package/lib/huggingface_ie.d.ts.map +1 -0
- package/lib/{esm/huggingface_ie.js → huggingface_ie.js} +32 -31
- package/lib/huggingface_ie.js.map +1 -0
- package/lib/index.d.ts +16 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +16 -0
- package/lib/index.js.map +1 -0
- package/lib/{types/mistral → mistral}/index.d.ts +6 -5
- package/lib/mistral/index.d.ts.map +1 -0
- package/lib/{esm/mistral → mistral}/index.js +66 -38
- package/lib/mistral/index.js.map +1 -0
- package/lib/{types/mistral → mistral}/types.d.ts +1 -0
- package/lib/mistral/types.d.ts.map +1 -0
- package/lib/mistral/types.js.map +1 -0
- package/lib/{types/openai → openai}/azure_openai.d.ts +5 -4
- package/lib/openai/azure_openai.d.ts.map +1 -0
- package/lib/{esm/openai → openai}/azure_openai.js +17 -14
- package/lib/openai/azure_openai.js.map +1 -0
- package/lib/openai/drivers.d.ts +4 -0
- package/lib/openai/drivers.d.ts.map +1 -0
- package/lib/openai/drivers.js +9 -0
- package/lib/openai/drivers.js.map +1 -0
- package/lib/{types/openai → openai}/index.d.ts +17 -5
- package/lib/openai/index.d.ts.map +1 -0
- package/lib/{esm/openai → openai}/index.js +254 -148
- package/lib/openai/index.js.map +1 -0
- package/lib/{types/openai → openai}/openai.d.ts +3 -3
- package/lib/openai/openai.d.ts.map +1 -0
- package/lib/openai/openai.js +15 -0
- package/lib/openai/openai.js.map +1 -0
- package/lib/{types/openai → openai}/openai_compatible.d.ts +3 -3
- package/lib/openai/openai_compatible.d.ts.map +1 -0
- package/lib/{esm/openai → openai}/openai_compatible.js +14 -11
- package/lib/openai/openai_compatible.js.map +1 -0
- package/lib/{types/openai → openai}/openai_format.d.ts +3 -2
- package/lib/openai/openai_format.d.ts.map +1 -0
- package/lib/{esm/openai → openai}/openai_format.js +66 -24
- package/lib/openai/openai_format.js.map +1 -0
- package/lib/{types/replicate.d.ts → replicate.d.ts} +3 -2
- package/lib/replicate.d.ts.map +1 -0
- package/lib/{esm/replicate.js → replicate.js} +59 -49
- package/lib/replicate.js.map +1 -0
- package/lib/shared/claude-messages.d.ts +99 -0
- package/lib/shared/claude-messages.d.ts.map +1 -0
- package/lib/shared/claude-messages.js +869 -0
- package/lib/shared/claude-messages.js.map +1 -0
- package/lib/{types/shared → shared}/claude-thinking.d.ts +1 -1
- package/lib/shared/claude-thinking.d.ts.map +1 -0
- package/lib/{esm/shared → shared}/claude-thinking.js +5 -7
- package/lib/shared/claude-thinking.js.map +1 -0
- package/lib/shared/debug-prompt.d.ts +5 -0
- package/lib/shared/debug-prompt.d.ts.map +1 -0
- package/lib/shared/debug-prompt.js +25 -0
- package/lib/shared/debug-prompt.js.map +1 -0
- package/lib/{types/test-driver → test-driver}/TestErrorCompletionStream.d.ts +1 -1
- package/lib/test-driver/TestErrorCompletionStream.d.ts.map +1 -0
- package/lib/test-driver/TestErrorCompletionStream.js +16 -0
- package/lib/test-driver/TestErrorCompletionStream.js.map +1 -0
- package/lib/{types/test-driver → test-driver}/TestValidationErrorCompletionStream.d.ts +1 -1
- package/lib/test-driver/TestValidationErrorCompletionStream.d.ts.map +1 -0
- package/lib/{esm/test-driver → test-driver}/TestValidationErrorCompletionStream.js +4 -4
- package/lib/test-driver/TestValidationErrorCompletionStream.js.map +1 -0
- package/lib/{types/test-driver → test-driver}/index.d.ts +3 -3
- package/lib/test-driver/index.d.ts.map +1 -0
- package/lib/{esm/test-driver → test-driver}/index.js +20 -20
- package/lib/test-driver/index.js.map +1 -0
- package/lib/{types/test-driver → test-driver}/utils.d.ts +1 -1
- package/lib/test-driver/utils.d.ts.map +1 -0
- package/lib/{esm/test-driver → test-driver}/utils.js +4 -4
- package/lib/test-driver/utils.js.map +1 -0
- package/lib/togetherai/index.d.ts +15 -0
- package/lib/togetherai/index.d.ts.map +1 -0
- package/lib/togetherai/index.js +54 -0
- package/lib/togetherai/index.js.map +1 -0
- package/lib/{types/togetherai → togetherai}/interfaces.d.ts +1 -1
- package/lib/togetherai/interfaces.d.ts.map +1 -0
- package/lib/{cjs/watsonx → togetherai}/interfaces.js.map +1 -1
- package/lib/vertexai/debug.d.ts +2 -0
- package/lib/vertexai/debug.d.ts.map +1 -0
- package/lib/{esm/vertexai → vertexai}/debug.js +1 -1
- package/lib/vertexai/debug.js.map +1 -0
- package/lib/vertexai/embeddings/embed-legacy-multimodal.d.ts +9 -0
- package/lib/vertexai/embeddings/embed-legacy-multimodal.d.ts.map +1 -0
- package/lib/vertexai/embeddings/embed-legacy-multimodal.js +141 -0
- package/lib/vertexai/embeddings/embed-legacy-multimodal.js.map +1 -0
- package/lib/vertexai/embeddings/embed.d.ts +13 -0
- package/lib/vertexai/embeddings/embed.d.ts.map +1 -0
- package/lib/vertexai/embeddings/embed.js +162 -0
- package/lib/vertexai/embeddings/embed.js.map +1 -0
- package/lib/vertexai/embeddings/source-utils.d.ts +9 -0
- package/lib/vertexai/embeddings/source-utils.d.ts.map +1 -0
- package/lib/vertexai/embeddings/source-utils.js +18 -0
- package/lib/vertexai/embeddings/source-utils.js.map +1 -0
- package/lib/{types/vertexai → vertexai}/index.d.ts +31 -19
- package/lib/vertexai/index.d.ts.map +1 -0
- package/lib/{esm/vertexai → vertexai}/index.js +221 -159
- package/lib/vertexai/index.js.map +1 -0
- package/lib/vertexai/models/claude.d.ts +16 -0
- package/lib/vertexai/models/claude.d.ts.map +1 -0
- package/lib/vertexai/models/claude.js +65 -0
- package/lib/vertexai/models/claude.js.map +1 -0
- package/lib/{types/vertexai → vertexai}/models/gemini.d.ts +29 -5
- package/lib/vertexai/models/gemini.d.ts.map +1 -0
- package/lib/{esm/vertexai → vertexai}/models/gemini.js +255 -127
- package/lib/vertexai/models/gemini.js.map +1 -0
- package/lib/{types/vertexai → vertexai}/models/imagen.d.ts +12 -11
- package/lib/vertexai/models/imagen.d.ts.map +1 -0
- package/lib/{esm/vertexai → vertexai}/models/imagen.js +74 -47
- package/lib/vertexai/models/imagen.js.map +1 -0
- package/lib/{types/vertexai → vertexai}/models/llama.d.ts +4 -4
- package/lib/vertexai/models/llama.d.ts.map +1 -0
- package/lib/{esm/vertexai → vertexai}/models/llama.js +37 -33
- package/lib/vertexai/models/llama.js.map +1 -0
- package/lib/vertexai/models.d.ts +21 -0
- package/lib/vertexai/models.d.ts.map +1 -0
- package/lib/{esm/vertexai → vertexai}/models.js +12 -9
- package/lib/vertexai/models.js.map +1 -0
- package/lib/{types/watsonx → watsonx}/index.d.ts +4 -3
- package/lib/watsonx/index.d.ts.map +1 -0
- package/lib/{esm/watsonx → watsonx}/index.js +69 -41
- package/lib/watsonx/index.js.map +1 -0
- package/lib/watsonx/interfaces.d.ts.map +1 -0
- package/lib/{esm/watsonx → watsonx}/interfaces.js.map +1 -1
- package/lib/{types/xai → xai}/index.d.ts +4 -4
- package/lib/xai/index.d.ts.map +1 -0
- package/lib/{esm/xai → xai}/index.js +22 -18
- package/lib/xai/index.js.map +1 -0
- package/package.json +88 -36
- package/src/adobe/firefly.ts +60 -43
- package/src/anthropic/index.ts +121 -0
- package/src/azure/azure_foundry.ts +210 -165
- package/src/bedrock/converse.ts +124 -101
- package/src/bedrock/embeddings.test.ts +508 -0
- package/src/bedrock/embeddings.ts +517 -0
- package/src/bedrock/error-handling.test.ts +62 -14
- package/src/bedrock/index.ts +832 -466
- package/src/bedrock/nova-image-payload.ts +98 -91
- package/src/bedrock/payloads.ts +9 -9
- package/src/bedrock/s3.test.ts +39 -0
- package/src/bedrock/s3.ts +57 -38
- package/src/bedrock/streaming-tool-use.test.ts +86 -69
- package/src/bedrock/twelvelabs.ts +35 -32
- package/src/groq/index.ts +143 -60
- package/src/http-timeout-wiring.test.ts +263 -0
- package/src/huggingface_ie.ts +45 -61
- package/src/index.ts +15 -15
- package/src/mistral/index.ts +97 -48
- package/src/mistral/types.ts +13 -12
- package/src/openai/azure_openai.ts +35 -27
- package/src/openai/drivers.ts +9 -0
- package/src/openai/error-handling.test.ts +308 -113
- package/src/openai/index.ts +389 -254
- package/src/openai/openai.ts +6 -13
- package/src/openai/openai_compatible.ts +35 -25
- package/src/openai/openai_format.ts +90 -40
- package/src/replicate.ts +108 -86
- package/src/shared/claude-messages.test.ts +37 -0
- package/src/shared/claude-messages.ts +1085 -0
- package/src/shared/claude-thinking.ts +6 -12
- package/src/shared/debug-prompt.ts +28 -0
- package/src/test-driver/TestErrorCompletionStream.ts +9 -10
- package/src/test-driver/TestValidationErrorCompletionStream.ts +9 -10
- package/src/test-driver/index.ts +43 -27
- package/src/test-driver/utils.ts +8 -8
- package/src/togetherai/index.test.ts +32 -0
- package/src/togetherai/index.ts +64 -128
- package/src/togetherai/interfaces.ts +5 -5
- package/src/vertexai/debug.ts +3 -3
- package/src/vertexai/embeddings/embed-legacy-multimodal.ts +211 -0
- package/src/vertexai/embeddings/embed.ts +206 -0
- package/src/vertexai/embeddings/embeddings.test.ts +497 -0
- package/src/vertexai/embeddings/source-utils.test.ts +54 -0
- package/src/vertexai/embeddings/source-utils.ts +29 -0
- package/src/vertexai/index.ts +339 -253
- package/src/vertexai/models/claude-error-handling.test.ts +86 -39
- package/src/vertexai/models/claude-streaming-spacing.test.ts +82 -75
- package/src/vertexai/models/claude.ts +84 -1041
- package/src/vertexai/models/gemini-conversation-mutation.test.ts +65 -23
- package/src/vertexai/models/gemini-error-handling.test.ts +126 -10
- package/src/vertexai/models/gemini-orphaned-tool-result.test.ts +118 -0
- package/src/vertexai/models/gemini.ts +372 -182
- package/src/vertexai/models/imagen.ts +164 -100
- package/src/vertexai/models/llama.ts +70 -48
- package/src/vertexai/models.ts +36 -16
- package/src/watsonx/index.ts +101 -65
- package/src/watsonx/interfaces.ts +10 -15
- package/src/xai/index.ts +30 -30
- package/lib/cjs/adobe/firefly.js +0 -120
- package/lib/cjs/adobe/firefly.js.map +0 -1
- package/lib/cjs/azure/azure_foundry.js +0 -432
- package/lib/cjs/azure/azure_foundry.js.map +0 -1
- package/lib/cjs/bedrock/converse.js +0 -359
- package/lib/cjs/bedrock/converse.js.map +0 -1
- package/lib/cjs/bedrock/index.js +0 -1508
- package/lib/cjs/bedrock/index.js.map +0 -1
- package/lib/cjs/bedrock/nova-image-payload.js +0 -207
- package/lib/cjs/bedrock/nova-image-payload.js.map +0 -1
- package/lib/cjs/bedrock/payloads.js +0 -3
- package/lib/cjs/bedrock/s3.js +0 -107
- package/lib/cjs/bedrock/s3.js.map +0 -1
- package/lib/cjs/bedrock/twelvelabs.js +0 -87
- package/lib/cjs/bedrock/twelvelabs.js.map +0 -1
- package/lib/cjs/groq/index.js +0 -328
- package/lib/cjs/groq/index.js.map +0 -1
- package/lib/cjs/huggingface_ie.js +0 -201
- package/lib/cjs/huggingface_ie.js.map +0 -1
- package/lib/cjs/index.js +0 -31
- package/lib/cjs/index.js.map +0 -1
- package/lib/cjs/mistral/index.js +0 -176
- package/lib/cjs/mistral/index.js.map +0 -1
- package/lib/cjs/mistral/types.js +0 -83
- package/lib/cjs/mistral/types.js.map +0 -1
- package/lib/cjs/openai/azure_openai.js +0 -72
- package/lib/cjs/openai/azure_openai.js.map +0 -1
- package/lib/cjs/openai/index.js +0 -1129
- package/lib/cjs/openai/index.js.map +0 -1
- package/lib/cjs/openai/openai.js +0 -21
- package/lib/cjs/openai/openai.js.map +0 -1
- package/lib/cjs/openai/openai_compatible.js +0 -63
- package/lib/cjs/openai/openai_compatible.js.map +0 -1
- package/lib/cjs/openai/openai_format.js +0 -131
- package/lib/cjs/openai/openai_format.js.map +0 -1
- package/lib/cjs/package.json +0 -3
- package/lib/cjs/replicate.js +0 -275
- package/lib/cjs/replicate.js.map +0 -1
- package/lib/cjs/shared/claude-thinking.js +0 -60
- package/lib/cjs/shared/claude-thinking.js.map +0 -1
- package/lib/cjs/test-driver/TestErrorCompletionStream.js +0 -20
- package/lib/cjs/test-driver/TestErrorCompletionStream.js.map +0 -1
- package/lib/cjs/test-driver/TestValidationErrorCompletionStream.js +0 -24
- package/lib/cjs/test-driver/TestValidationErrorCompletionStream.js.map +0 -1
- package/lib/cjs/test-driver/index.js +0 -109
- package/lib/cjs/test-driver/index.js.map +0 -1
- package/lib/cjs/test-driver/utils.js +0 -30
- package/lib/cjs/test-driver/utils.js.map +0 -1
- package/lib/cjs/togetherai/index.js +0 -126
- package/lib/cjs/togetherai/index.js.map +0 -1
- package/lib/cjs/togetherai/interfaces.js +0 -3
- package/lib/cjs/togetherai/interfaces.js.map +0 -1
- package/lib/cjs/vertexai/debug.js +0 -12
- package/lib/cjs/vertexai/debug.js.map +0 -1
- package/lib/cjs/vertexai/embeddings/embeddings-image.js +0 -27
- package/lib/cjs/vertexai/embeddings/embeddings-image.js.map +0 -1
- package/lib/cjs/vertexai/embeddings/embeddings-text.js +0 -23
- package/lib/cjs/vertexai/embeddings/embeddings-text.js.map +0 -1
- package/lib/cjs/vertexai/index.js +0 -644
- package/lib/cjs/vertexai/index.js.map +0 -1
- package/lib/cjs/vertexai/models/claude.js +0 -917
- package/lib/cjs/vertexai/models/claude.js.map +0 -1
- package/lib/cjs/vertexai/models/gemini.js +0 -858
- package/lib/cjs/vertexai/models/gemini.js.map +0 -1
- package/lib/cjs/vertexai/models/imagen.js +0 -303
- package/lib/cjs/vertexai/models/imagen.js.map +0 -1
- package/lib/cjs/vertexai/models/llama.js +0 -183
- package/lib/cjs/vertexai/models/llama.js.map +0 -1
- package/lib/cjs/vertexai/models.js +0 -35
- package/lib/cjs/vertexai/models.js.map +0 -1
- package/lib/cjs/watsonx/index.js +0 -161
- package/lib/cjs/watsonx/index.js.map +0 -1
- package/lib/cjs/watsonx/interfaces.js +0 -3
- package/lib/cjs/xai/index.js +0 -65
- package/lib/cjs/xai/index.js.map +0 -1
- package/lib/esm/adobe/firefly.js +0 -116
- package/lib/esm/adobe/firefly.js.map +0 -1
- package/lib/esm/azure/azure_foundry.js.map +0 -1
- package/lib/esm/bedrock/converse.js.map +0 -1
- package/lib/esm/bedrock/index.js.map +0 -1
- package/lib/esm/bedrock/nova-image-payload.js.map +0 -1
- package/lib/esm/bedrock/payloads.js.map +0 -1
- package/lib/esm/bedrock/s3.js.map +0 -1
- package/lib/esm/bedrock/twelvelabs.js.map +0 -1
- package/lib/esm/groq/index.js.map +0 -1
- package/lib/esm/huggingface_ie.js.map +0 -1
- package/lib/esm/index.js +0 -15
- package/lib/esm/index.js.map +0 -1
- package/lib/esm/mistral/index.js.map +0 -1
- package/lib/esm/mistral/types.js.map +0 -1
- package/lib/esm/openai/azure_openai.js.map +0 -1
- package/lib/esm/openai/index.js.map +0 -1
- package/lib/esm/openai/openai.js +0 -14
- package/lib/esm/openai/openai.js.map +0 -1
- package/lib/esm/openai/openai_compatible.js.map +0 -1
- package/lib/esm/openai/openai_format.js.map +0 -1
- package/lib/esm/replicate.js.map +0 -1
- package/lib/esm/shared/claude-thinking.js.map +0 -1
- package/lib/esm/test-driver/TestErrorCompletionStream.js +0 -16
- package/lib/esm/test-driver/TestErrorCompletionStream.js.map +0 -1
- package/lib/esm/test-driver/TestValidationErrorCompletionStream.js.map +0 -1
- package/lib/esm/test-driver/index.js.map +0 -1
- package/lib/esm/test-driver/utils.js.map +0 -1
- package/lib/esm/togetherai/index.js +0 -122
- package/lib/esm/togetherai/index.js.map +0 -1
- package/lib/esm/togetherai/interfaces.js.map +0 -1
- package/lib/esm/vertexai/debug.js.map +0 -1
- package/lib/esm/vertexai/embeddings/embeddings-image.js +0 -24
- package/lib/esm/vertexai/embeddings/embeddings-image.js.map +0 -1
- package/lib/esm/vertexai/embeddings/embeddings-text.js +0 -20
- package/lib/esm/vertexai/embeddings/embeddings-text.js.map +0 -1
- package/lib/esm/vertexai/index.js.map +0 -1
- package/lib/esm/vertexai/models/claude.js +0 -906
- package/lib/esm/vertexai/models/claude.js.map +0 -1
- package/lib/esm/vertexai/models/gemini.js.map +0 -1
- package/lib/esm/vertexai/models/imagen.js.map +0 -1
- package/lib/esm/vertexai/models/llama.js.map +0 -1
- package/lib/esm/vertexai/models.js.map +0 -1
- package/lib/esm/watsonx/index.js.map +0 -1
- package/lib/esm/xai/index.js.map +0 -1
- package/lib/types/adobe/firefly.d.ts.map +0 -1
- package/lib/types/azure/azure_foundry.d.ts.map +0 -1
- package/lib/types/bedrock/converse.d.ts.map +0 -1
- package/lib/types/bedrock/index.d.ts.map +0 -1
- package/lib/types/bedrock/nova-image-payload.d.ts.map +0 -1
- package/lib/types/bedrock/payloads.d.ts.map +0 -1
- package/lib/types/bedrock/s3.d.ts.map +0 -1
- package/lib/types/bedrock/twelvelabs.d.ts.map +0 -1
- package/lib/types/groq/index.d.ts.map +0 -1
- package/lib/types/huggingface_ie.d.ts.map +0 -1
- package/lib/types/index.d.ts +0 -15
- package/lib/types/index.d.ts.map +0 -1
- package/lib/types/mistral/index.d.ts.map +0 -1
- package/lib/types/mistral/types.d.ts.map +0 -1
- package/lib/types/openai/azure_openai.d.ts.map +0 -1
- package/lib/types/openai/index.d.ts.map +0 -1
- package/lib/types/openai/openai.d.ts.map +0 -1
- package/lib/types/openai/openai_compatible.d.ts.map +0 -1
- package/lib/types/openai/openai_format.d.ts.map +0 -1
- package/lib/types/replicate.d.ts.map +0 -1
- package/lib/types/shared/claude-thinking.d.ts.map +0 -1
- package/lib/types/test-driver/TestErrorCompletionStream.d.ts.map +0 -1
- package/lib/types/test-driver/TestValidationErrorCompletionStream.d.ts.map +0 -1
- package/lib/types/test-driver/index.d.ts.map +0 -1
- package/lib/types/test-driver/utils.d.ts.map +0 -1
- package/lib/types/togetherai/index.d.ts +0 -23
- package/lib/types/togetherai/index.d.ts.map +0 -1
- package/lib/types/togetherai/interfaces.d.ts.map +0 -1
- package/lib/types/vertexai/debug.d.ts +0 -2
- package/lib/types/vertexai/debug.d.ts.map +0 -1
- package/lib/types/vertexai/embeddings/embeddings-image.d.ts +0 -11
- package/lib/types/vertexai/embeddings/embeddings-image.d.ts.map +0 -1
- package/lib/types/vertexai/embeddings/embeddings-text.d.ts +0 -10
- package/lib/types/vertexai/embeddings/embeddings-text.d.ts.map +0 -1
- package/lib/types/vertexai/index.d.ts.map +0 -1
- package/lib/types/vertexai/models/claude.d.ts +0 -119
- package/lib/types/vertexai/models/claude.d.ts.map +0 -1
- package/lib/types/vertexai/models/gemini.d.ts.map +0 -1
- package/lib/types/vertexai/models/imagen.d.ts.map +0 -1
- package/lib/types/vertexai/models/llama.d.ts.map +0 -1
- package/lib/types/vertexai/models.d.ts +0 -20
- package/lib/types/vertexai/models.d.ts.map +0 -1
- package/lib/types/watsonx/index.d.ts.map +0 -1
- package/lib/types/watsonx/interfaces.d.ts.map +0 -1
- package/lib/types/xai/index.d.ts.map +0 -1
- package/src/vertexai/embeddings/embeddings-image.ts +0 -52
- package/src/vertexai/embeddings/embeddings-text.ts +0 -54
- /package/lib/{esm/bedrock → bedrock}/payloads.js +0 -0
- /package/lib/{esm/mistral → mistral}/types.js +0 -0
- /package/lib/{esm/togetherai → togetherai}/interfaces.js +0 -0
- /package/lib/{types/watsonx → watsonx}/interfaces.d.ts +0 -0
- /package/lib/{esm/watsonx → watsonx}/interfaces.js +0 -0
package/src/openai/index.ts
CHANGED
|
@@ -1,39 +1,44 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
EmbeddingsOptions,
|
|
10
|
-
EmbeddingsResult,
|
|
11
|
-
ExecutionOptions,
|
|
12
|
-
ExecutionTokenUsage,
|
|
2
|
+
type AIModel,
|
|
3
|
+
type Completion,
|
|
4
|
+
type CompletionChunkObject,
|
|
5
|
+
type CompletionResult,
|
|
6
|
+
type DataSource,
|
|
7
|
+
type DriverOptions,
|
|
8
|
+
type EmbeddingResultItem,
|
|
9
|
+
type EmbeddingsOptions,
|
|
10
|
+
type EmbeddingsResult,
|
|
11
|
+
type ExecutionOptions,
|
|
12
|
+
type ExecutionTokenUsage,
|
|
13
13
|
getConversationMeta,
|
|
14
14
|
getModelCapabilities,
|
|
15
15
|
incrementConversationTurn,
|
|
16
|
-
JSONSchema,
|
|
16
|
+
type JSONSchema,
|
|
17
17
|
LlumiverseError,
|
|
18
|
-
LlumiverseErrorContext,
|
|
19
|
-
modelModalitiesToArray,
|
|
18
|
+
type LlumiverseErrorContext,
|
|
20
19
|
ModelType,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
modelModalitiesToArray,
|
|
21
|
+
normalizeEmbeddingsOptions,
|
|
22
|
+
OPENAI_DEFAULT_EMBEDDING_MODEL,
|
|
23
|
+
type OpenAiDalleOptions,
|
|
24
|
+
type OpenAiGptImageOptions,
|
|
25
|
+
type Providers,
|
|
24
26
|
stripBase64ImagesFromConversation,
|
|
25
27
|
stripHeartbeatsFromConversation,
|
|
26
28
|
supportsToolUse,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
type TextFallbackOptions,
|
|
30
|
+
type ToolDefinition,
|
|
31
|
+
type ToolUse,
|
|
32
|
+
type TrainingJob,
|
|
30
33
|
TrainingJobStatus,
|
|
31
|
-
TrainingOptions,
|
|
32
|
-
TrainingPromptOptions,
|
|
34
|
+
type TrainingOptions,
|
|
35
|
+
type TrainingPromptOptions,
|
|
33
36
|
truncateLargeTextInConversation,
|
|
34
37
|
unwrapConversationArray,
|
|
35
|
-
} from
|
|
36
|
-
import
|
|
38
|
+
} from '@llumiverse/core';
|
|
39
|
+
import { AbstractDriver } from '@llumiverse/core/driver';
|
|
40
|
+
import type OpenAI from 'openai';
|
|
41
|
+
import type { AzureOpenAI } from 'openai';
|
|
37
42
|
import {
|
|
38
43
|
APIConnectionError,
|
|
39
44
|
APIConnectionTimeoutError,
|
|
@@ -50,23 +55,49 @@ import {
|
|
|
50
55
|
RateLimitError,
|
|
51
56
|
UnprocessableEntityError,
|
|
52
57
|
} from 'openai/error';
|
|
53
|
-
import { formatOpenAILikeMultimodalPrompt } from
|
|
58
|
+
import { formatOpenAILikeMultimodalPrompt } from './openai_format.js';
|
|
54
59
|
|
|
55
60
|
// Response API types
|
|
56
61
|
type ResponseInputItem = OpenAI.Responses.ResponseInputItem;
|
|
57
62
|
type EasyInputMessage = OpenAI.Responses.EasyInputMessage;
|
|
63
|
+
type OpenAIRequestOptions = Partial<TextFallbackOptions> & {
|
|
64
|
+
image_detail?: 'low' | 'high' | 'auto';
|
|
65
|
+
effort?: string;
|
|
66
|
+
reasoning_effort?: string;
|
|
67
|
+
};
|
|
68
|
+
type OpenAIErrorWithStatus = Error & { status?: unknown };
|
|
69
|
+
type OpenAIUsageWithProviderDetails = OpenAI.Responses.ResponseUsage & {
|
|
70
|
+
cached_tokens?: number | null;
|
|
71
|
+
prompt_tokens_details?: {
|
|
72
|
+
cached_tokens?: number | null;
|
|
73
|
+
} | null;
|
|
74
|
+
};
|
|
75
|
+
type MutableRoleItem = { role: 'user' | 'developer' | 'system' | 'assistant' };
|
|
76
|
+
type MutableInputImagePart = { type: string; detail?: string };
|
|
77
|
+
type OpenAIFunctionItem = ResponseInputItem & {
|
|
78
|
+
type?: string;
|
|
79
|
+
name?: string;
|
|
80
|
+
arguments?: string;
|
|
81
|
+
output?: string;
|
|
82
|
+
};
|
|
58
83
|
|
|
59
84
|
// Helper function to convert string to CompletionResult[]
|
|
60
85
|
function textToCompletionResult(text: string): CompletionResult[] {
|
|
61
|
-
return text ? [{ type:
|
|
86
|
+
return text ? [{ type: 'text', value: text }] : [];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function hasNumericStatus(error: unknown): boolean {
|
|
90
|
+
return error instanceof Error && typeof (error as OpenAIErrorWithStatus).status === 'number';
|
|
62
91
|
}
|
|
63
92
|
|
|
64
93
|
function isOpenAIReasoningModel(model: string): boolean {
|
|
65
94
|
const normalized = model.toLowerCase();
|
|
66
|
-
return
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
95
|
+
return (
|
|
96
|
+
normalized.includes('o1') ||
|
|
97
|
+
normalized.includes('o3') ||
|
|
98
|
+
normalized.includes('o4') ||
|
|
99
|
+
normalized.includes('gpt-5')
|
|
100
|
+
);
|
|
70
101
|
}
|
|
71
102
|
|
|
72
103
|
function isGpt5ProModel(model: string): boolean {
|
|
@@ -74,33 +105,35 @@ function isGpt5ProModel(model: string): boolean {
|
|
|
74
105
|
return /^gpt-5(?:\.\d+)?-pro/.test(modelName);
|
|
75
106
|
}
|
|
76
107
|
|
|
77
|
-
function openAIReasoningEffort(model: string, effort: string | undefined):
|
|
108
|
+
function openAIReasoningEffort(model: string, effort: string | undefined): 'low' | 'medium' | 'high' | undefined {
|
|
78
109
|
if (!effort || !isOpenAIReasoningModel(model)) {
|
|
79
110
|
return undefined;
|
|
80
111
|
}
|
|
81
112
|
if (isGpt5ProModel(model)) {
|
|
82
|
-
return
|
|
113
|
+
return 'high';
|
|
83
114
|
}
|
|
84
|
-
return effort ===
|
|
115
|
+
return effort === 'low' || effort === 'medium' || effort === 'high' ? effort : undefined;
|
|
85
116
|
}
|
|
86
117
|
|
|
87
118
|
//TODO: Do we need a list?, replace with if statements and modernize?
|
|
88
119
|
const supportFineTunning = new Set([
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
120
|
+
'gpt-3.5-turbo-1106',
|
|
121
|
+
'gpt-3.5-turbo-0613',
|
|
122
|
+
'babbage-002',
|
|
123
|
+
'davinci-002',
|
|
124
|
+
'gpt-4-0613',
|
|
94
125
|
]);
|
|
95
126
|
|
|
96
|
-
export interface BaseOpenAIDriverOptions extends DriverOptions {
|
|
97
|
-
}
|
|
127
|
+
export interface BaseOpenAIDriverOptions extends DriverOptions {}
|
|
98
128
|
|
|
99
|
-
export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
129
|
+
export abstract class BaseOpenAIDriver extends AbstractDriver<BaseOpenAIDriverOptions, ResponseInputItem[]> {
|
|
130
|
+
abstract provider:
|
|
131
|
+
| Providers.openai
|
|
132
|
+
| Providers.azure_openai
|
|
133
|
+
| Providers.xai
|
|
134
|
+
| Providers.azure_foundry
|
|
135
|
+
| Providers.togetherai
|
|
136
|
+
| Providers.openai_compatible;
|
|
104
137
|
abstract service: OpenAI | AzureOpenAI;
|
|
105
138
|
|
|
106
139
|
constructor(opts: BaseOpenAIDriverOptions) {
|
|
@@ -108,10 +141,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
108
141
|
this.formatPrompt = formatOpenAILikeMultimodalPrompt;
|
|
109
142
|
}
|
|
110
143
|
|
|
111
|
-
extractDataFromResponse(
|
|
112
|
-
_options: ExecutionOptions,
|
|
113
|
-
result: OpenAI.Responses.Response
|
|
114
|
-
): Completion {
|
|
144
|
+
extractDataFromResponse(_options: ExecutionOptions, result: OpenAI.Responses.Response): Completion {
|
|
115
145
|
const tokenInfo = mapUsage(result.usage);
|
|
116
146
|
|
|
117
147
|
const tools = collectTools(result.output);
|
|
@@ -119,8 +149,8 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
119
149
|
const allResults = extractCompletionResults(result.output);
|
|
120
150
|
|
|
121
151
|
if (allResults.length === 0 && !tools) {
|
|
122
|
-
this.logger.error({ result },
|
|
123
|
-
throw new Error(
|
|
152
|
+
this.logger.error({ result }, '[OpenAI] Response is not valid');
|
|
153
|
+
throw new Error('Response is not valid: no data');
|
|
124
154
|
}
|
|
125
155
|
|
|
126
156
|
return {
|
|
@@ -131,17 +161,22 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
131
161
|
};
|
|
132
162
|
}
|
|
133
163
|
|
|
134
|
-
async requestTextCompletionStream(
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
164
|
+
async requestTextCompletionStream(
|
|
165
|
+
prompt: ResponseInputItem[],
|
|
166
|
+
options: ExecutionOptions,
|
|
167
|
+
): Promise<AsyncIterable<CompletionChunkObject>> {
|
|
168
|
+
if (
|
|
169
|
+
options.model_options?._option_id !== undefined &&
|
|
170
|
+
options.model_options?._option_id !== 'openai-text' &&
|
|
171
|
+
options.model_options?._option_id !== 'openai-thinking' &&
|
|
172
|
+
options.model_options?._option_id !== 'text-fallback'
|
|
173
|
+
) {
|
|
174
|
+
this.logger.debug({ options: options.model_options }, 'Unexpected option id');
|
|
140
175
|
}
|
|
141
176
|
|
|
142
177
|
// Include conversation history (same as non-streaming)
|
|
143
178
|
// Fix orphaned function_call items (can occur when agent is stopped mid-tool-execution)
|
|
144
|
-
let conversation = fixOrphanedToolUse(updateConversation(options.conversation, prompt));
|
|
179
|
+
let conversation = fixOrphanedToolResults(fixOrphanedToolUse(updateConversation(options.conversation, prompt)));
|
|
145
180
|
|
|
146
181
|
const toolDefs = getToolDefinitions(options.tools);
|
|
147
182
|
const useTools: boolean = toolDefs ? supportsToolUse(options.model, this.provider, true) : false;
|
|
@@ -154,17 +189,16 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
154
189
|
|
|
155
190
|
convertRoles(prompt, options.model);
|
|
156
191
|
|
|
157
|
-
const model_options = options.model_options as
|
|
158
|
-
insert_image_detail(prompt, model_options?.image_detail ??
|
|
192
|
+
const model_options = options.model_options as OpenAIRequestOptions | undefined;
|
|
193
|
+
insert_image_detail(prompt, model_options?.image_detail ?? 'auto');
|
|
159
194
|
|
|
160
|
-
let parsedSchema: JSONSchema | undefined
|
|
195
|
+
let parsedSchema: JSONSchema | undefined;
|
|
161
196
|
let strictMode = false;
|
|
162
|
-
if (options.result_schema && supportsSchema(options.model)) {
|
|
197
|
+
if (options.result_schema && supportsSchema(options.model, this.provider)) {
|
|
163
198
|
try {
|
|
164
199
|
parsedSchema = openAISchemaFormat(options.result_schema);
|
|
165
200
|
strictMode = true;
|
|
166
|
-
}
|
|
167
|
-
catch (e) {
|
|
201
|
+
} catch {
|
|
168
202
|
parsedSchema = limitedSchemaFormat(options.result_schema);
|
|
169
203
|
strictMode = false;
|
|
170
204
|
}
|
|
@@ -183,36 +217,41 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
183
217
|
top_p: isReasoningModel ? undefined : model_options?.top_p,
|
|
184
218
|
max_output_tokens: model_options?.max_tokens,
|
|
185
219
|
tools: useTools ? toolDefs : undefined,
|
|
186
|
-
text: parsedSchema
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
220
|
+
text: parsedSchema
|
|
221
|
+
? {
|
|
222
|
+
format: {
|
|
223
|
+
type: 'json_schema',
|
|
224
|
+
name: 'format_output',
|
|
225
|
+
schema: parsedSchema,
|
|
226
|
+
strict: strictMode,
|
|
227
|
+
},
|
|
228
|
+
}
|
|
229
|
+
: undefined,
|
|
194
230
|
});
|
|
195
231
|
|
|
196
232
|
return mapResponseStream(stream);
|
|
197
233
|
}
|
|
198
234
|
|
|
199
235
|
async requestTextCompletion(prompt: ResponseInputItem[], options: ExecutionOptions): Promise<Completion> {
|
|
200
|
-
if (
|
|
201
|
-
options.model_options?._option_id !==
|
|
202
|
-
options.model_options?._option_id !==
|
|
203
|
-
|
|
236
|
+
if (
|
|
237
|
+
options.model_options?._option_id !== undefined &&
|
|
238
|
+
options.model_options?._option_id !== 'openai-text' &&
|
|
239
|
+
options.model_options?._option_id !== 'openai-thinking' &&
|
|
240
|
+
options.model_options?._option_id !== 'text-fallback'
|
|
241
|
+
) {
|
|
242
|
+
this.logger.debug({ options: options.model_options }, 'Unexpected option id');
|
|
204
243
|
}
|
|
205
244
|
|
|
206
245
|
convertRoles(prompt, options.model);
|
|
207
246
|
|
|
208
|
-
const model_options = options.model_options as
|
|
209
|
-
insert_image_detail(prompt, model_options?.image_detail ??
|
|
247
|
+
const model_options = options.model_options as OpenAIRequestOptions | undefined;
|
|
248
|
+
insert_image_detail(prompt, model_options?.image_detail ?? 'auto');
|
|
210
249
|
|
|
211
250
|
const toolDefs = getToolDefinitions(options.tools);
|
|
212
251
|
const useTools: boolean = toolDefs ? supportsToolUse(options.model, this.provider) : false;
|
|
213
252
|
|
|
214
253
|
// Fix orphaned function_call items (can occur when agent is stopped mid-tool-execution)
|
|
215
|
-
let conversation = fixOrphanedToolUse(updateConversation(options.conversation, prompt));
|
|
254
|
+
let conversation = fixOrphanedToolResults(fixOrphanedToolUse(updateConversation(options.conversation, prompt)));
|
|
216
255
|
|
|
217
256
|
// When no tools are provided but conversation contains function_call/function_call_output
|
|
218
257
|
// items (e.g. checkpoint summary calls), convert them to text to avoid API errors
|
|
@@ -220,14 +259,13 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
220
259
|
conversation = convertOpenAIFunctionItemsToText(conversation);
|
|
221
260
|
}
|
|
222
261
|
|
|
223
|
-
let parsedSchema: JSONSchema | undefined
|
|
262
|
+
let parsedSchema: JSONSchema | undefined;
|
|
224
263
|
let strictMode = false;
|
|
225
|
-
if (options.result_schema && supportsSchema(options.model)) {
|
|
264
|
+
if (options.result_schema && supportsSchema(options.model, this.provider)) {
|
|
226
265
|
try {
|
|
227
266
|
parsedSchema = openAISchemaFormat(options.result_schema);
|
|
228
267
|
strictMode = true;
|
|
229
|
-
}
|
|
230
|
-
catch (e) {
|
|
268
|
+
} catch {
|
|
231
269
|
parsedSchema = limitedSchemaFormat(options.result_schema);
|
|
232
270
|
strictMode = false;
|
|
233
271
|
}
|
|
@@ -246,14 +284,16 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
246
284
|
top_p: isReasoningModel ? undefined : model_options?.top_p,
|
|
247
285
|
max_output_tokens: model_options?.max_tokens, //TODO: use max_tokens for older models, currently relying on OpenAI to handle it
|
|
248
286
|
tools: useTools ? toolDefs : undefined,
|
|
249
|
-
text: parsedSchema
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
287
|
+
text: parsedSchema
|
|
288
|
+
? {
|
|
289
|
+
format: {
|
|
290
|
+
type: 'json_schema',
|
|
291
|
+
name: 'format_output',
|
|
292
|
+
schema: parsedSchema,
|
|
293
|
+
strict: strictMode,
|
|
294
|
+
},
|
|
295
|
+
}
|
|
296
|
+
: undefined,
|
|
257
297
|
});
|
|
258
298
|
|
|
259
299
|
const completion = this.extractDataFromResponse(options, res);
|
|
@@ -271,7 +311,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
271
311
|
const stripOptions = {
|
|
272
312
|
keepForTurns: options.stripImagesAfterTurns ?? Infinity,
|
|
273
313
|
currentTurn,
|
|
274
|
-
textMaxTokens: options.stripTextMaxTokens
|
|
314
|
+
textMaxTokens: options.stripTextMaxTokens,
|
|
275
315
|
};
|
|
276
316
|
let processedConversation = stripBase64ImagesFromConversation(conversation, stripOptions);
|
|
277
317
|
|
|
@@ -291,14 +331,15 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
291
331
|
|
|
292
332
|
protected canStream(_options: ExecutionOptions): Promise<boolean> {
|
|
293
333
|
// Image generation models don't support streaming
|
|
294
|
-
if (
|
|
295
|
-
|
|
296
|
-
|
|
334
|
+
if (
|
|
335
|
+
_options.model.includes('dall-e') ||
|
|
336
|
+
_options.model.includes('gpt-image') ||
|
|
337
|
+
_options.model.includes('chatgpt-image')
|
|
338
|
+
) {
|
|
297
339
|
return Promise.resolve(false);
|
|
298
340
|
}
|
|
299
341
|
|
|
300
|
-
if (_options.model.includes(
|
|
301
|
-
&& !(_options.model.includes("mini") || _options.model.includes("preview"))) {
|
|
342
|
+
if (_options.model.includes('o1') && !(_options.model.includes('mini') || _options.model.includes('preview'))) {
|
|
302
343
|
//o1 full does not support streaming
|
|
303
344
|
//TODO: Update when OpenAI adds support for streaming, last check 16/02/2025
|
|
304
345
|
return Promise.resolve(false);
|
|
@@ -314,7 +355,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
314
355
|
prompt: ResponseInputItem[],
|
|
315
356
|
result: unknown[],
|
|
316
357
|
toolUse: unknown[] | undefined,
|
|
317
|
-
options: ExecutionOptions
|
|
358
|
+
options: ExecutionOptions,
|
|
318
359
|
): ResponseInputItem[] | undefined {
|
|
319
360
|
// Build assistant message from accumulated CompletionResult[]
|
|
320
361
|
const completionResults = result as CompletionResult[];
|
|
@@ -327,6 +368,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
327
368
|
// Add assistant message as EasyInputMessage
|
|
328
369
|
if (textContent) {
|
|
329
370
|
const assistantMessage: EasyInputMessage = {
|
|
371
|
+
type: 'message',
|
|
330
372
|
role: 'assistant',
|
|
331
373
|
content: textContent,
|
|
332
374
|
};
|
|
@@ -335,7 +377,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
335
377
|
|
|
336
378
|
// Add function calls as separate items (Response API format)
|
|
337
379
|
if (toolUse && toolUse.length > 0) {
|
|
338
|
-
const functionCalls: OpenAI.Responses.ResponseFunctionToolCall[] = (toolUse as ToolUse[]).map(t => ({
|
|
380
|
+
const functionCalls: OpenAI.Responses.ResponseFunctionToolCall[] = (toolUse as ToolUse[]).map((t) => ({
|
|
339
381
|
type: 'function_call' as const,
|
|
340
382
|
call_id: t.id,
|
|
341
383
|
name: t.tool_name,
|
|
@@ -352,7 +394,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
352
394
|
const stripOptions = {
|
|
353
395
|
keepForTurns: options.stripImagesAfterTurns ?? Infinity,
|
|
354
396
|
currentTurn,
|
|
355
|
-
textMaxTokens: options.stripTextMaxTokens
|
|
397
|
+
textMaxTokens: options.stripTextMaxTokens,
|
|
356
398
|
};
|
|
357
399
|
let processedConversation = stripBase64ImagesFromConversation(conversation, stripOptions);
|
|
358
400
|
processedConversation = truncateLargeTextInConversation(processedConversation, stripOptions);
|
|
@@ -365,11 +407,11 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
365
407
|
}
|
|
366
408
|
|
|
367
409
|
createTrainingPrompt(options: TrainingPromptOptions): Promise<string> {
|
|
368
|
-
if (options.model.includes(
|
|
410
|
+
if (options.model.includes('gpt')) {
|
|
369
411
|
return super.createTrainingPrompt(options);
|
|
370
412
|
} else {
|
|
371
413
|
// babbage, davinci not yet implemented
|
|
372
|
-
throw new Error(
|
|
414
|
+
throw new Error(`Unsupported model for training: ${options.model}`);
|
|
373
415
|
}
|
|
374
416
|
}
|
|
375
417
|
|
|
@@ -377,14 +419,14 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
377
419
|
const url = await dataset.getURL();
|
|
378
420
|
const file = await this.service.files.create({
|
|
379
421
|
file: await fetch(url),
|
|
380
|
-
purpose:
|
|
422
|
+
purpose: 'fine-tune',
|
|
381
423
|
});
|
|
382
424
|
|
|
383
425
|
const job = await this.service.fineTuning.jobs.create({
|
|
384
426
|
training_file: file.id,
|
|
385
427
|
model: options.model,
|
|
386
|
-
hyperparameters: options.params
|
|
387
|
-
})
|
|
428
|
+
hyperparameters: options.params,
|
|
429
|
+
});
|
|
388
430
|
|
|
389
431
|
return jobInfo(job);
|
|
390
432
|
}
|
|
@@ -405,7 +447,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
405
447
|
try {
|
|
406
448
|
await this.service.models.list();
|
|
407
449
|
return true;
|
|
408
|
-
} catch
|
|
450
|
+
} catch {
|
|
409
451
|
return false;
|
|
410
452
|
}
|
|
411
453
|
}
|
|
@@ -423,9 +465,21 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
423
465
|
|
|
424
466
|
//Some of these use the completions API instead of the chat completions API.
|
|
425
467
|
//Others are for non-text input modalities. Therefore common to both.
|
|
426
|
-
const wordBlacklist = [
|
|
427
|
-
|
|
428
|
-
|
|
468
|
+
const wordBlacklist = [
|
|
469
|
+
'embed',
|
|
470
|
+
'whisper',
|
|
471
|
+
'transcribe',
|
|
472
|
+
'audio',
|
|
473
|
+
'moderation',
|
|
474
|
+
'tts',
|
|
475
|
+
'realtime',
|
|
476
|
+
'babbage',
|
|
477
|
+
'davinci',
|
|
478
|
+
'codex',
|
|
479
|
+
'o1-pro',
|
|
480
|
+
'computer-use',
|
|
481
|
+
'sora',
|
|
482
|
+
];
|
|
429
483
|
|
|
430
484
|
//OpenAI has very little information, filtering based on name.
|
|
431
485
|
result = result.filter((m) => {
|
|
@@ -433,61 +487,84 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
433
487
|
});
|
|
434
488
|
|
|
435
489
|
const models = filter ? result.filter(filter) : result;
|
|
436
|
-
const aiModels = models
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
owner
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
// Determine model type based on capabilities
|
|
444
|
-
let modelType = ModelType.Text;
|
|
445
|
-
if (m.id.includes("dall-e") || m.id.includes("gpt-image")) {
|
|
446
|
-
modelType = ModelType.Image;
|
|
447
|
-
}
|
|
490
|
+
const aiModels = models
|
|
491
|
+
.map((m) => {
|
|
492
|
+
const modelCapability = getModelCapabilities(m.id, this.provider);
|
|
493
|
+
let owner = m.owned_by;
|
|
494
|
+
if (owner === 'system') {
|
|
495
|
+
owner = 'openai';
|
|
496
|
+
}
|
|
448
497
|
|
|
498
|
+
// Determine model type based on capabilities
|
|
499
|
+
let modelType = ModelType.Text;
|
|
500
|
+
if (m.id.includes('dall-e') || m.id.includes('gpt-image')) {
|
|
501
|
+
modelType = ModelType.Image;
|
|
502
|
+
}
|
|
449
503
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
504
|
+
return {
|
|
505
|
+
id: m.id,
|
|
506
|
+
name: m.id,
|
|
507
|
+
provider: this.provider,
|
|
508
|
+
owner: owner,
|
|
509
|
+
type: modelType,
|
|
510
|
+
input_modalities: modelModalitiesToArray(modelCapability.input),
|
|
511
|
+
output_modalities: modelModalitiesToArray(modelCapability.output),
|
|
512
|
+
tool_support: modelCapability.tool_support,
|
|
513
|
+
} satisfies AIModel<string>;
|
|
514
|
+
})
|
|
515
|
+
.sort((a, b) => a.id.localeCompare(b.id));
|
|
461
516
|
|
|
462
517
|
return aiModels;
|
|
463
518
|
}
|
|
464
519
|
|
|
520
|
+
async generateEmbeddings(options: EmbeddingsOptions): Promise<EmbeddingsResult> {
|
|
521
|
+
const normalized = normalizeEmbeddingsOptions(options);
|
|
522
|
+
const model = normalized.model ?? OPENAI_DEFAULT_EMBEDDING_MODEL;
|
|
465
523
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
524
|
+
const texts: string[] = normalized.inputs.map((input) => {
|
|
525
|
+
if (input.type !== 'text') {
|
|
526
|
+
throw new Error(
|
|
527
|
+
`Provider 'openai' does not support '${input.type}' embeddings; only 'text' is supported.`,
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
return input.text;
|
|
531
|
+
});
|
|
471
532
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
533
|
+
try {
|
|
534
|
+
const res = await this.service.embeddings.create({
|
|
535
|
+
input: texts,
|
|
536
|
+
model,
|
|
537
|
+
...(normalized.dimensions ? { dimensions: normalized.dimensions } : {}),
|
|
538
|
+
encoding_format: 'float',
|
|
539
|
+
});
|
|
475
540
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
541
|
+
// OpenAI does not guarantee data is returned in the same order as the input,
|
|
542
|
+
// but does return a stable `index` per item. Sort by index to align with inputs.
|
|
543
|
+
const ordered = [...res.data].sort((a, b) => a.index - b.index);
|
|
544
|
+
const items = ordered.map((entry): EmbeddingResultItem => {
|
|
545
|
+
if (!entry.embedding || entry.embedding.length === 0) {
|
|
546
|
+
throw new Error(`OpenAI embedding empty for input index ${entry.index}`);
|
|
547
|
+
}
|
|
548
|
+
return { outputs: [{ values: entry.embedding, modality: 'text' }] };
|
|
549
|
+
});
|
|
480
550
|
|
|
481
|
-
|
|
551
|
+
const usage = res.usage
|
|
552
|
+
? { input_tokens: res.usage.prompt_tokens, input_text_tokens: res.usage.prompt_tokens }
|
|
553
|
+
: undefined;
|
|
482
554
|
|
|
483
|
-
|
|
484
|
-
|
|
555
|
+
return { model, results: items, usage };
|
|
556
|
+
} catch (error) {
|
|
557
|
+
if (LlumiverseError.isLlumiverseError(error)) throw error;
|
|
558
|
+
if (error instanceof Error && !hasNumericStatus(error)) throw error;
|
|
559
|
+
throw this.formatLlumiverseError(error, {
|
|
560
|
+
provider: this.provider,
|
|
561
|
+
model,
|
|
562
|
+
operation: 'execute',
|
|
563
|
+
});
|
|
485
564
|
}
|
|
486
|
-
|
|
487
|
-
return { values: embeddings, model } satisfies EmbeddingsResult;
|
|
488
565
|
}
|
|
489
566
|
|
|
490
|
-
imageModels = [
|
|
567
|
+
imageModels = ['dall-e', 'gpt-image', 'chatgpt-image'];
|
|
491
568
|
|
|
492
569
|
/**
|
|
493
570
|
* Determine if a model is specifically an image generation model (not conversational image model)
|
|
@@ -495,7 +572,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
495
572
|
isImageModel(model: string): boolean {
|
|
496
573
|
// DALL-E models are standalone image generation
|
|
497
574
|
// gpt-image models can generate images in conversations, not standalone
|
|
498
|
-
return this.imageModels.some(imageModel => model.includes(imageModel));
|
|
575
|
+
return this.imageModels.some((imageModel) => model.includes(imageModel));
|
|
499
576
|
}
|
|
500
577
|
|
|
501
578
|
/**
|
|
@@ -508,15 +585,15 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
508
585
|
const model_options = options.model_options as OpenAiDalleOptions | OpenAiGptImageOptions | undefined;
|
|
509
586
|
|
|
510
587
|
// Extract prompt text from ResponseInputItem[]
|
|
511
|
-
let promptText =
|
|
588
|
+
let promptText = '';
|
|
512
589
|
for (const item of prompt) {
|
|
513
590
|
if ('content' in item && typeof item.content === 'string') {
|
|
514
|
-
promptText += item.content
|
|
591
|
+
promptText += `${item.content}\\n`;
|
|
515
592
|
} else if ('content' in item && Array.isArray(item.content)) {
|
|
516
593
|
// Extract text from content array
|
|
517
594
|
for (const part of item.content) {
|
|
518
595
|
if ('type' in part && part.type === 'input_text' && 'text' in part) {
|
|
519
|
-
promptText += part.text
|
|
596
|
+
promptText += `${part.text}\\n`;
|
|
520
597
|
}
|
|
521
598
|
}
|
|
522
599
|
}
|
|
@@ -527,17 +604,17 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
527
604
|
const generateParams: OpenAI.Images.ImageGenerateParamsNonStreaming = {
|
|
528
605
|
model: options.model,
|
|
529
606
|
prompt: promptText,
|
|
530
|
-
size: model_options?.size ||
|
|
607
|
+
size: model_options?.size || '1024x1024',
|
|
531
608
|
};
|
|
532
609
|
|
|
533
610
|
// Add DALL-E specific options
|
|
534
|
-
if (options.model.includes(
|
|
611
|
+
if (options.model.includes('dall-e') || model_options?._option_id === 'openai-dalle') {
|
|
535
612
|
const dalleOptions = model_options as OpenAiDalleOptions | undefined;
|
|
536
613
|
generateParams.n = dalleOptions?.n || 1;
|
|
537
|
-
generateParams.response_format = dalleOptions?.response_format ||
|
|
614
|
+
generateParams.response_format = dalleOptions?.response_format || 'b64_json';
|
|
538
615
|
|
|
539
|
-
if (options.model.includes(
|
|
540
|
-
generateParams.quality = dalleOptions?.image_quality ||
|
|
616
|
+
if (options.model.includes('dall-e-3')) {
|
|
617
|
+
generateParams.quality = dalleOptions?.image_quality || 'standard';
|
|
541
618
|
if (dalleOptions?.style) {
|
|
542
619
|
generateParams.style = dalleOptions.style;
|
|
543
620
|
}
|
|
@@ -567,31 +644,35 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
567
644
|
}
|
|
568
645
|
|
|
569
646
|
results.push({
|
|
570
|
-
type:
|
|
571
|
-
value: imageValue
|
|
647
|
+
type: 'image',
|
|
648
|
+
value: imageValue,
|
|
572
649
|
});
|
|
573
650
|
}
|
|
574
651
|
}
|
|
575
652
|
|
|
576
653
|
return {
|
|
577
|
-
result: results
|
|
654
|
+
result: results,
|
|
578
655
|
};
|
|
579
|
-
|
|
580
|
-
} catch (error: any) {
|
|
656
|
+
} catch (error: unknown) {
|
|
581
657
|
this.logger.error({ error }, `[${this.provider}] Image generation failed`);
|
|
658
|
+
const generationError = error instanceof Error ? error : new Error(String(error));
|
|
659
|
+
const errorCode =
|
|
660
|
+
(error as { code?: unknown })?.code === 'content_policy_violation'
|
|
661
|
+
? 'content_policy_violation'
|
|
662
|
+
: 'validation_error';
|
|
582
663
|
return {
|
|
583
664
|
result: [],
|
|
584
665
|
error: {
|
|
585
|
-
message:
|
|
586
|
-
code:
|
|
587
|
-
}
|
|
666
|
+
message: generationError.message,
|
|
667
|
+
code: errorCode,
|
|
668
|
+
},
|
|
588
669
|
};
|
|
589
670
|
}
|
|
590
671
|
}
|
|
591
672
|
|
|
592
673
|
/**
|
|
593
674
|
* Format OpenAI API errors into LlumiverseError with proper status codes and retryability.
|
|
594
|
-
*
|
|
675
|
+
*
|
|
595
676
|
* OpenAI API errors have a specific structure:
|
|
596
677
|
* - APIError.status: HTTP status code (400, 401, 403, 404, 409, 422, 429, 500+)
|
|
597
678
|
* - APIError.error: Error object with type, message, param, code
|
|
@@ -599,7 +680,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
599
680
|
* - APIError.code: Error code (e.g., 'invalid_api_key', 'rate_limit_exceeded')
|
|
600
681
|
* - APIError.param: Parameter that caused the error (optional)
|
|
601
682
|
* - APIError.type: Error type (optional)
|
|
602
|
-
*
|
|
683
|
+
*
|
|
603
684
|
* Common error types:
|
|
604
685
|
* - BadRequestError (400): Invalid request parameters
|
|
605
686
|
* - AuthenticationError (401): Invalid API key
|
|
@@ -613,20 +694,17 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
613
694
|
* - APIConnectionTimeoutError: Request timeout (no status code)
|
|
614
695
|
* - LengthFinishReasonError: Response truncated due to length
|
|
615
696
|
* - ContentFilterFinishReasonError: Content filtered
|
|
616
|
-
*
|
|
697
|
+
*
|
|
617
698
|
* This implementation works for:
|
|
618
699
|
* - OpenAI API
|
|
619
700
|
* - Azure OpenAI
|
|
620
701
|
* - xAI (uses OpenAI-compatible API)
|
|
621
702
|
* - Azure Foundry (OpenAI-compatible)
|
|
622
703
|
* - Other OpenAI-compatible APIs
|
|
623
|
-
*
|
|
704
|
+
*
|
|
624
705
|
* @see https://platform.openai.com/docs/guides/error-codes
|
|
625
706
|
*/
|
|
626
|
-
public formatLlumiverseError(
|
|
627
|
-
error: unknown,
|
|
628
|
-
context: LlumiverseErrorContext
|
|
629
|
-
): LlumiverseError {
|
|
707
|
+
public formatLlumiverseError(error: unknown, context: LlumiverseErrorContext): LlumiverseError {
|
|
630
708
|
// Check if it's an OpenAI API error
|
|
631
709
|
const isOpenAIError = this.isOpenAIApiError(error);
|
|
632
710
|
|
|
@@ -681,7 +759,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
681
759
|
context,
|
|
682
760
|
error,
|
|
683
761
|
httpStatusCode,
|
|
684
|
-
errorName
|
|
762
|
+
errorName,
|
|
685
763
|
);
|
|
686
764
|
}
|
|
687
765
|
|
|
@@ -690,22 +768,20 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
690
768
|
*/
|
|
691
769
|
private isOpenAIApiError(error: unknown): error is APIError | OpenAIError {
|
|
692
770
|
return (
|
|
693
|
-
error !== null &&
|
|
694
|
-
typeof error === 'object' &&
|
|
695
|
-
(error instanceof APIError || error instanceof OpenAIError)
|
|
771
|
+
error !== null && typeof error === 'object' && (error instanceof APIError || error instanceof OpenAIError)
|
|
696
772
|
);
|
|
697
773
|
}
|
|
698
774
|
|
|
699
775
|
/**
|
|
700
776
|
* Determine if an OpenAI API error is retryable.
|
|
701
|
-
*
|
|
777
|
+
*
|
|
702
778
|
* Retryable errors:
|
|
703
779
|
* - RateLimitError (429): Rate limit exceeded, retry with backoff
|
|
704
780
|
* - InternalServerError (500+): Server-side errors
|
|
705
781
|
* - APIConnectionTimeoutError: Request timeout
|
|
706
782
|
* - Error codes: 'timeout', 'server_error', 'service_unavailable'
|
|
707
783
|
* - Status codes: 408, 429, 502, 503, 504, 529, 5xx
|
|
708
|
-
*
|
|
784
|
+
*
|
|
709
785
|
* Non-retryable errors:
|
|
710
786
|
* - BadRequestError (400): Invalid request parameters
|
|
711
787
|
* - AuthenticationError (401): Invalid API key
|
|
@@ -717,7 +793,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
717
793
|
* - ContentFilterFinishReasonError: Content filtered
|
|
718
794
|
* - Error codes: 'invalid_api_key', 'invalid_request_error', 'model_not_found'
|
|
719
795
|
* - Other 4xx client errors
|
|
720
|
-
*
|
|
796
|
+
*
|
|
721
797
|
* @param error - The error object
|
|
722
798
|
* @param httpStatusCode - The HTTP status code if available
|
|
723
799
|
* @param errorCode - The error code if available
|
|
@@ -728,7 +804,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
728
804
|
error: unknown,
|
|
729
805
|
httpStatusCode: number | undefined,
|
|
730
806
|
errorCode: string | null | undefined,
|
|
731
|
-
errorType: string | undefined
|
|
807
|
+
errorType: string | undefined,
|
|
732
808
|
): boolean | undefined {
|
|
733
809
|
// Check specific OpenAI error types by class
|
|
734
810
|
if (error instanceof RateLimitError) return true;
|
|
@@ -759,7 +835,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
759
835
|
if (errorCode === 'model_not_found') return false;
|
|
760
836
|
if (errorCode === 'insufficient_quota') return false;
|
|
761
837
|
if (errorCode === 'invalid_model') return false;
|
|
762
|
-
if (errorCode.includes('invalid_')) return false;
|
|
838
|
+
if (typeof errorCode === 'string' && errorCode.includes('invalid_')) return false;
|
|
763
839
|
}
|
|
764
840
|
|
|
765
841
|
// Check error type
|
|
@@ -787,10 +863,8 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
|
|
|
787
863
|
// Unknown error type - let consumer decide retry strategy
|
|
788
864
|
return undefined;
|
|
789
865
|
}
|
|
790
|
-
|
|
791
866
|
}
|
|
792
867
|
|
|
793
|
-
|
|
794
868
|
function jobInfo(job: OpenAI.FineTuning.Jobs.FineTuningJob): TrainingJob {
|
|
795
869
|
//validating_files`, `queued`, `running`, `succeeded`, `failed`, or `cancelled`.
|
|
796
870
|
const jobStatus = job.status;
|
|
@@ -800,7 +874,9 @@ function jobInfo(job: OpenAI.FineTuning.Jobs.FineTuningJob): TrainingJob {
|
|
|
800
874
|
status = TrainingJobStatus.succeeded;
|
|
801
875
|
} else if (jobStatus === 'failed') {
|
|
802
876
|
status = TrainingJobStatus.failed;
|
|
803
|
-
details = job.error
|
|
877
|
+
details = job.error
|
|
878
|
+
? `${job.error.code} - ${job.error.message} ${job.error.param ? ` [${job.error.param}]` : ''}`
|
|
879
|
+
: 'error';
|
|
804
880
|
} else if (jobStatus === 'cancelled') {
|
|
805
881
|
status = TrainingJobStatus.cancelled;
|
|
806
882
|
} else {
|
|
@@ -811,20 +887,22 @@ function jobInfo(job: OpenAI.FineTuning.Jobs.FineTuningJob): TrainingJob {
|
|
|
811
887
|
id: job.id,
|
|
812
888
|
model: job.fine_tuned_model || undefined,
|
|
813
889
|
status,
|
|
814
|
-
details
|
|
815
|
-
}
|
|
890
|
+
details,
|
|
891
|
+
};
|
|
816
892
|
}
|
|
817
893
|
|
|
818
|
-
function mapUsage(usage?:
|
|
894
|
+
function mapUsage(usage?: OpenAIUsageWithProviderDetails | null): ExecutionTokenUsage | undefined {
|
|
819
895
|
if (!usage) {
|
|
820
896
|
return undefined;
|
|
821
897
|
}
|
|
898
|
+
const cachedTokens =
|
|
899
|
+
usage.input_tokens_details?.cached_tokens ?? usage.prompt_tokens_details?.cached_tokens ?? usage.cached_tokens;
|
|
822
900
|
return {
|
|
823
901
|
prompt: usage.input_tokens,
|
|
824
902
|
result: usage.output_tokens,
|
|
825
903
|
total: usage.total_tokens,
|
|
826
|
-
prompt_cached:
|
|
827
|
-
prompt_new: usage.input_tokens - (
|
|
904
|
+
prompt_cached: cachedTokens ?? undefined,
|
|
905
|
+
prompt_new: usage.input_tokens - (cachedTokens ?? 0),
|
|
828
906
|
};
|
|
829
907
|
}
|
|
830
908
|
|
|
@@ -833,7 +911,7 @@ function completionResultsToText(completionResults: CompletionResult[] | undefin
|
|
|
833
911
|
return '';
|
|
834
912
|
}
|
|
835
913
|
return completionResults
|
|
836
|
-
.map(r => {
|
|
914
|
+
.map((r) => {
|
|
837
915
|
switch (r.type) {
|
|
838
916
|
case 'text':
|
|
839
917
|
return r.value;
|
|
@@ -842,8 +920,10 @@ function completionResultsToText(completionResults: CompletionResult[] | undefin
|
|
|
842
920
|
case 'image':
|
|
843
921
|
// Skip images in conversation - they're in the result
|
|
844
922
|
return '';
|
|
845
|
-
default:
|
|
846
|
-
|
|
923
|
+
default: {
|
|
924
|
+
const _exhaustive: never = r;
|
|
925
|
+
return String(_exhaustive);
|
|
926
|
+
}
|
|
847
927
|
}
|
|
848
928
|
})
|
|
849
929
|
.join('');
|
|
@@ -856,6 +936,7 @@ function createAssistantMessageFromCompletion(completion: Completion): ResponseI
|
|
|
856
936
|
// Add assistant text message if present
|
|
857
937
|
if (textContent) {
|
|
858
938
|
const assistantMessage: EasyInputMessage = {
|
|
939
|
+
type: 'message',
|
|
859
940
|
role: 'assistant',
|
|
860
941
|
content: textContent,
|
|
861
942
|
};
|
|
@@ -869,9 +950,7 @@ function createAssistantMessageFromCompletion(completion: Completion): ResponseI
|
|
|
869
950
|
type: 'function_call',
|
|
870
951
|
call_id: t.id,
|
|
871
952
|
name: t.tool_name,
|
|
872
|
-
arguments: typeof t.tool_input === 'string'
|
|
873
|
-
? t.tool_input
|
|
874
|
-
: JSON.stringify(t.tool_input ?? {}),
|
|
953
|
+
arguments: typeof t.tool_input === 'string' ? t.tool_input : JSON.stringify(t.tool_input ?? {}),
|
|
875
954
|
};
|
|
876
955
|
result.push(functionCall);
|
|
877
956
|
}
|
|
@@ -880,23 +959,31 @@ function createAssistantMessageFromCompletion(completion: Completion): ResponseI
|
|
|
880
959
|
return result;
|
|
881
960
|
}
|
|
882
961
|
|
|
883
|
-
function mapResponseStream(
|
|
884
|
-
|
|
962
|
+
export function mapResponseStream(
|
|
963
|
+
stream: AsyncIterable<OpenAI.Responses.ResponseStreamEvent>,
|
|
964
|
+
): AsyncIterable<CompletionChunkObject> {
|
|
965
|
+
const toolCallMetadata = new Map<string, { syntheticId: string; callId?: string; name?: string }>();
|
|
885
966
|
|
|
886
967
|
return {
|
|
887
968
|
async *[Symbol.asyncIterator]() {
|
|
969
|
+
let hasTextDeltas = false;
|
|
970
|
+
let refusalText = '';
|
|
888
971
|
for await (const event of stream) {
|
|
889
972
|
if (event.type === 'response.output_item.added' && event.item.type === 'function_call') {
|
|
890
973
|
const syntheticId = `tool_${event.output_index}`;
|
|
891
|
-
const
|
|
892
|
-
|
|
893
|
-
|
|
974
|
+
const callId = event.item.call_id ?? event.item.id;
|
|
975
|
+
const metadata = { syntheticId, callId, name: event.item.name };
|
|
976
|
+
if (event.item.id) {
|
|
977
|
+
toolCallMetadata.set(event.item.id, metadata);
|
|
978
|
+
}
|
|
979
|
+
if (event.item.call_id) {
|
|
980
|
+
toolCallMetadata.set(event.item.call_id, metadata);
|
|
894
981
|
}
|
|
895
|
-
const toolUse: ToolUse & { _actual_id?: string } = {
|
|
982
|
+
const toolUse: ToolUse<unknown> & { _actual_id?: string } = {
|
|
896
983
|
id: syntheticId,
|
|
897
|
-
_actual_id:
|
|
984
|
+
_actual_id: callId,
|
|
898
985
|
tool_name: event.item.name,
|
|
899
|
-
tool_input: ''
|
|
986
|
+
tool_input: '',
|
|
900
987
|
};
|
|
901
988
|
yield {
|
|
902
989
|
result: [],
|
|
@@ -905,11 +992,12 @@ function mapResponseStream(stream: AsyncIterable<OpenAI.Responses.ResponseStream
|
|
|
905
992
|
} else if (event.type === 'response.function_call_arguments.delta') {
|
|
906
993
|
const metadata = toolCallMetadata.get(event.item_id);
|
|
907
994
|
const syntheticId = metadata?.syntheticId ?? `tool_${event.output_index}`;
|
|
908
|
-
const
|
|
995
|
+
const callId = metadata?.callId ?? event.item_id;
|
|
996
|
+
const toolUse: ToolUse<unknown> & { _actual_id?: string } = {
|
|
909
997
|
id: syntheticId,
|
|
910
|
-
_actual_id:
|
|
998
|
+
_actual_id: callId,
|
|
911
999
|
tool_name: metadata?.name ?? '',
|
|
912
|
-
tool_input: event.delta
|
|
1000
|
+
tool_input: event.delta,
|
|
913
1001
|
};
|
|
914
1002
|
yield {
|
|
915
1003
|
result: [],
|
|
@@ -925,16 +1013,36 @@ function mapResponseStream(stream: AsyncIterable<OpenAI.Responses.ResponseStream
|
|
|
925
1013
|
const syntheticId = metadata?.syntheticId ?? `tool_${event.output_index}`;
|
|
926
1014
|
const tool_name = metadata?.name ?? event.name ?? '';
|
|
927
1015
|
if (event.item_id) {
|
|
928
|
-
toolCallMetadata.set(event.item_id, { syntheticId, name: tool_name });
|
|
1016
|
+
toolCallMetadata.set(event.item_id, { syntheticId, callId: metadata?.callId, name: tool_name });
|
|
929
1017
|
}
|
|
930
1018
|
} else if (event.type === 'response.output_text.delta') {
|
|
1019
|
+
hasTextDeltas = true;
|
|
931
1020
|
yield {
|
|
932
1021
|
result: textToCompletionResult(event.delta),
|
|
933
1022
|
} satisfies CompletionChunkObject;
|
|
934
|
-
}
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
1023
|
+
} else if (event.type === 'response.output_text.done') {
|
|
1024
|
+
// Fallback: some models (e.g. gpt-5 with json_schema structured output) buffer
|
|
1025
|
+
// the entire output and never emit delta events — only the done event with the
|
|
1026
|
+
// full text. Emit it here only when no deltas arrived to avoid duplication.
|
|
1027
|
+
if (!hasTextDeltas && event.text) {
|
|
1028
|
+
yield {
|
|
1029
|
+
result: textToCompletionResult(event.text),
|
|
1030
|
+
} satisfies CompletionChunkObject;
|
|
1031
|
+
}
|
|
1032
|
+
} else if (event.type === 'response.refusal.delta') {
|
|
1033
|
+
refusalText += event.delta;
|
|
1034
|
+
} else if (event.type === 'response.refusal.done') {
|
|
1035
|
+
throw new Error(`[OpenAI] Model refused: ${event.refusal || refusalText}`);
|
|
1036
|
+
} else if ((event as { type: string }).type === 'response.error') {
|
|
1037
|
+
const errEvent = event as unknown as { message: string; code?: string | null };
|
|
1038
|
+
throw new Error(
|
|
1039
|
+
`[OpenAI Responses API] ${errEvent.message}${errEvent.code ? ` (${errEvent.code})` : ''}`,
|
|
1040
|
+
);
|
|
1041
|
+
} else if (
|
|
1042
|
+
event.type === 'response.completed' ||
|
|
1043
|
+
event.type === 'response.incomplete' ||
|
|
1044
|
+
event.type === 'response.failed'
|
|
1045
|
+
) {
|
|
938
1046
|
const finalTools = collectTools(event.response.output);
|
|
939
1047
|
yield {
|
|
940
1048
|
result: [],
|
|
@@ -943,12 +1051,12 @@ function mapResponseStream(stream: AsyncIterable<OpenAI.Responses.ResponseStream
|
|
|
943
1051
|
} satisfies CompletionChunkObject;
|
|
944
1052
|
}
|
|
945
1053
|
}
|
|
946
|
-
}
|
|
1054
|
+
},
|
|
947
1055
|
};
|
|
948
1056
|
}
|
|
949
1057
|
|
|
950
1058
|
function insert_image_detail(items: ResponseInputItem[], detail_level: string): ResponseInputItem[] {
|
|
951
|
-
if (detail_level ===
|
|
1059
|
+
if (detail_level === 'auto' || detail_level === 'low' || detail_level === 'high') {
|
|
952
1060
|
for (const item of items) {
|
|
953
1061
|
// Check if it's an EasyInputMessage or Message with content array
|
|
954
1062
|
if ('role' in item && 'content' in item && item.role !== 'assistant') {
|
|
@@ -956,7 +1064,7 @@ function insert_image_detail(items: ResponseInputItem[], detail_level: string):
|
|
|
956
1064
|
if (Array.isArray(content)) {
|
|
957
1065
|
for (const part of content) {
|
|
958
1066
|
if (typeof part === 'object' && part.type === 'input_image') {
|
|
959
|
-
(part as
|
|
1067
|
+
(part as MutableInputImagePart).detail = detail_level;
|
|
960
1068
|
}
|
|
961
1069
|
}
|
|
962
1070
|
}
|
|
@@ -968,19 +1076,19 @@ function insert_image_detail(items: ResponseInputItem[], detail_level: string):
|
|
|
968
1076
|
|
|
969
1077
|
function convertRoles(items: ResponseInputItem[], model: string): ResponseInputItem[] {
|
|
970
1078
|
//New openai models use developer role instead of system
|
|
971
|
-
if (model.includes(
|
|
972
|
-
if (model.includes(
|
|
1079
|
+
if (model.includes('o1') || model.includes('o3')) {
|
|
1080
|
+
if (model.includes('o1-mini') || model.includes('o1-preview')) {
|
|
973
1081
|
//o1-mini and o1-preview support neither system nor developer
|
|
974
1082
|
for (const item of items) {
|
|
975
1083
|
if ('role' in item && (item as EasyInputMessage).role === 'system') {
|
|
976
|
-
(item as
|
|
1084
|
+
(item as MutableRoleItem).role = 'user';
|
|
977
1085
|
}
|
|
978
1086
|
}
|
|
979
1087
|
} else {
|
|
980
1088
|
//Models newer than o1 use developer role
|
|
981
1089
|
for (const item of items) {
|
|
982
1090
|
if ('role' in item && (item as EasyInputMessage).role === 'system') {
|
|
983
|
-
(item as
|
|
1091
|
+
(item as MutableRoleItem).role = 'developer';
|
|
984
1092
|
}
|
|
985
1093
|
}
|
|
986
1094
|
}
|
|
@@ -990,12 +1098,12 @@ function convertRoles(items: ResponseInputItem[], model: string): ResponseInputI
|
|
|
990
1098
|
|
|
991
1099
|
//Structured output support is typically aligned with tool use support
|
|
992
1100
|
//Not true for realtime models, which do not support structured output, but do support tool use.
|
|
993
|
-
function supportsSchema(model: string): boolean {
|
|
994
|
-
const realtimeModel = model.includes(
|
|
1101
|
+
function supportsSchema(model: string, provider: string | Providers): boolean {
|
|
1102
|
+
const realtimeModel = model.includes('realtime');
|
|
995
1103
|
if (realtimeModel) {
|
|
996
1104
|
return false;
|
|
997
1105
|
}
|
|
998
|
-
return supportsToolUse(model,
|
|
1106
|
+
return supportsToolUse(model, provider);
|
|
999
1107
|
}
|
|
1000
1108
|
|
|
1001
1109
|
/**
|
|
@@ -1004,17 +1112,17 @@ function supportsSchema(model: string): boolean {
|
|
|
1004
1112
|
* tools to be defined in the API request.
|
|
1005
1113
|
*/
|
|
1006
1114
|
export function convertOpenAIFunctionItemsToText(items: ResponseInputItem[]): ResponseInputItem[] {
|
|
1007
|
-
const hasFunctionItems = items.some(item => {
|
|
1008
|
-
const type = (item as
|
|
1115
|
+
const hasFunctionItems = items.some((item) => {
|
|
1116
|
+
const type = (item as OpenAIFunctionItem).type;
|
|
1009
1117
|
return type === 'function_call' || type === 'function_call_output';
|
|
1010
1118
|
});
|
|
1011
1119
|
if (!hasFunctionItems) return items;
|
|
1012
1120
|
|
|
1013
|
-
return items.map(item => {
|
|
1014
|
-
const typed = item as
|
|
1121
|
+
return items.map((item) => {
|
|
1122
|
+
const typed = item as OpenAIFunctionItem;
|
|
1015
1123
|
if (typed.type === 'function_call') {
|
|
1016
1124
|
const argsStr = typed.arguments || '';
|
|
1017
|
-
const truncated = argsStr.length > 500 ? argsStr.substring(0, 500)
|
|
1125
|
+
const truncated = argsStr.length > 500 ? `${argsStr.substring(0, 500)}...` : argsStr;
|
|
1018
1126
|
return {
|
|
1019
1127
|
role: 'assistant' as const,
|
|
1020
1128
|
content: `[Tool call: ${typed.name}(${truncated})]`,
|
|
@@ -1022,7 +1130,7 @@ export function convertOpenAIFunctionItemsToText(items: ResponseInputItem[]): Re
|
|
|
1022
1130
|
}
|
|
1023
1131
|
if (typed.type === 'function_call_output') {
|
|
1024
1132
|
const output = typed.output || 'No output';
|
|
1025
|
-
const truncated = output.length > 500 ? output.substring(0, 500)
|
|
1133
|
+
const truncated = output.length > 500 ? `${output.substring(0, 500)}...` : output;
|
|
1026
1134
|
return {
|
|
1027
1135
|
role: 'user' as const,
|
|
1028
1136
|
content: `[Tool result: ${truncated}]`,
|
|
@@ -1036,15 +1144,14 @@ function getToolDefinitions(tools: ToolDefinition[] | undefined | null): OpenAI.
|
|
|
1036
1144
|
return tools ? tools.map(getToolDefinition) : undefined;
|
|
1037
1145
|
}
|
|
1038
1146
|
function getToolDefinition(toolDef: ToolDefinition): OpenAI.Responses.FunctionTool {
|
|
1039
|
-
let parsedSchema: JSONSchema | undefined
|
|
1147
|
+
let parsedSchema: JSONSchema | undefined;
|
|
1040
1148
|
let strictMode = false;
|
|
1041
1149
|
if (toolDef.input_schema) {
|
|
1042
1150
|
try {
|
|
1043
1151
|
//TODO: type assertion here is not safe, does not work with satisfies
|
|
1044
1152
|
parsedSchema = openAISchemaFormat(toolDef.input_schema as JSONSchema);
|
|
1045
1153
|
strictMode = true;
|
|
1046
|
-
}
|
|
1047
|
-
catch (e) {
|
|
1154
|
+
} catch {
|
|
1048
1155
|
//TODO: type assertion here is not safe, does not work with satisfies
|
|
1049
1156
|
parsedSchema = limitedSchemaFormat(toolDef.input_schema as JSONSchema);
|
|
1050
1157
|
strictMode = false;
|
|
@@ -1052,7 +1159,7 @@ function getToolDefinition(toolDef: ToolDefinition): OpenAI.Responses.FunctionTo
|
|
|
1052
1159
|
}
|
|
1053
1160
|
|
|
1054
1161
|
return {
|
|
1055
|
-
type:
|
|
1162
|
+
type: 'function',
|
|
1056
1163
|
name: toolDef.name,
|
|
1057
1164
|
description: toolDef.description,
|
|
1058
1165
|
parameters: parsedSchema ?? null,
|
|
@@ -1064,7 +1171,7 @@ function updateConversation(conversation: unknown, items: ResponseInputItem[]):
|
|
|
1064
1171
|
if (!items) {
|
|
1065
1172
|
// Unwrap array if wrapped, otherwise treat as array
|
|
1066
1173
|
const unwrapped = unwrapConversationArray<ResponseInputItem>(conversation);
|
|
1067
|
-
return unwrapped ?? (conversation as ResponseInputItem[] || []);
|
|
1174
|
+
return unwrapped ?? ((conversation as ResponseInputItem[]) || []);
|
|
1068
1175
|
}
|
|
1069
1176
|
if (!conversation) {
|
|
1070
1177
|
return items;
|
|
@@ -1075,12 +1182,12 @@ function updateConversation(conversation: unknown, items: ResponseInputItem[]):
|
|
|
1075
1182
|
return [...convArray, ...items];
|
|
1076
1183
|
}
|
|
1077
1184
|
|
|
1078
|
-
export function collectTools(output?: OpenAI.Responses.ResponseOutputItem[]): ToolUse[] | undefined {
|
|
1185
|
+
export function collectTools(output?: OpenAI.Responses.ResponseOutputItem[]): ToolUse<unknown>[] | undefined {
|
|
1079
1186
|
if (!output) {
|
|
1080
1187
|
return undefined;
|
|
1081
1188
|
}
|
|
1082
1189
|
|
|
1083
|
-
const tools: ToolUse[] = [];
|
|
1190
|
+
const tools: ToolUse<unknown>[] = [];
|
|
1084
1191
|
for (const item of output) {
|
|
1085
1192
|
if (item.type === 'function_call') {
|
|
1086
1193
|
const id = item.call_id || item.id;
|
|
@@ -1113,8 +1220,8 @@ function extractCompletionResults(output?: OpenAI.Responses.ResponseOutputItem[]
|
|
|
1113
1220
|
for (const part of item.content) {
|
|
1114
1221
|
if (part.type === 'output_text' && part.text) {
|
|
1115
1222
|
results.push({
|
|
1116
|
-
type:
|
|
1117
|
-
value: part.text
|
|
1223
|
+
type: 'text',
|
|
1224
|
+
value: part.text,
|
|
1118
1225
|
});
|
|
1119
1226
|
}
|
|
1120
1227
|
}
|
|
@@ -1122,12 +1229,10 @@ function extractCompletionResults(output?: OpenAI.Responses.ResponseOutputItem[]
|
|
|
1122
1229
|
// GPT-image models return base64 encoded images in result field
|
|
1123
1230
|
const base64Data = item.result;
|
|
1124
1231
|
// Format as data URL for consistency with other image outputs
|
|
1125
|
-
const imageUrl = base64Data.startsWith('data:')
|
|
1126
|
-
? base64Data
|
|
1127
|
-
: `data:image/png;base64,${base64Data}`;
|
|
1232
|
+
const imageUrl = base64Data.startsWith('data:') ? base64Data : `data:image/png;base64,${base64Data}`;
|
|
1128
1233
|
results.push({
|
|
1129
|
-
type:
|
|
1130
|
-
value: imageUrl
|
|
1234
|
+
type: 'image',
|
|
1235
|
+
value: imageUrl,
|
|
1131
1236
|
});
|
|
1132
1237
|
}
|
|
1133
1238
|
}
|
|
@@ -1136,7 +1241,7 @@ function extractCompletionResults(output?: OpenAI.Responses.ResponseOutputItem[]
|
|
|
1136
1241
|
|
|
1137
1242
|
//For strict mode false
|
|
1138
1243
|
function limitedSchemaFormat(schema: JSONSchema): JSONSchema {
|
|
1139
|
-
const formattedSchema = { ...schema };
|
|
1244
|
+
const formattedSchema: JSONSchema = { ...schema };
|
|
1140
1245
|
|
|
1141
1246
|
// Defaults not supported
|
|
1142
1247
|
delete formattedSchema.default;
|
|
@@ -1177,16 +1282,16 @@ function limitedSchemaFormat(schema: JSONSchema): JSONSchema {
|
|
|
1177
1282
|
//For strict mode true
|
|
1178
1283
|
function openAISchemaFormat(schema: JSONSchema, nesting: number = 0): JSONSchema {
|
|
1179
1284
|
if (nesting > 5) {
|
|
1180
|
-
throw new Error(
|
|
1285
|
+
throw new Error('OpenAI schema nesting too deep');
|
|
1181
1286
|
}
|
|
1182
1287
|
|
|
1183
|
-
const formattedSchema = { ...schema };
|
|
1288
|
+
const formattedSchema: JSONSchema = { ...schema };
|
|
1184
1289
|
|
|
1185
1290
|
// Defaults not supported
|
|
1186
1291
|
delete formattedSchema.default;
|
|
1187
1292
|
|
|
1188
1293
|
// Additional properties not supported, required to be set.
|
|
1189
|
-
if (formattedSchema?.type ===
|
|
1294
|
+
if (formattedSchema?.type === 'object') {
|
|
1190
1295
|
formattedSchema.additionalProperties = false;
|
|
1191
1296
|
}
|
|
1192
1297
|
|
|
@@ -1215,18 +1320,24 @@ function openAISchemaFormat(schema: JSONSchema, nesting: number = 0): JSONSchema
|
|
|
1215
1320
|
}
|
|
1216
1321
|
}
|
|
1217
1322
|
}
|
|
1218
|
-
if (
|
|
1323
|
+
if (
|
|
1324
|
+
formattedSchema?.type === 'object' &&
|
|
1325
|
+
(!formattedSchema?.properties || Object.keys(formattedSchema?.properties ?? {}).length === 0)
|
|
1326
|
+
) {
|
|
1219
1327
|
//If no properties are defined, then additionalProperties: true was set or the object would be empty.
|
|
1220
1328
|
//OpenAI does not support this on structured output/ strict mode.
|
|
1221
|
-
throw new Error(
|
|
1329
|
+
throw new Error('OpenAI does not support empty objects or objects with additionalProperties set to true');
|
|
1222
1330
|
}
|
|
1223
1331
|
|
|
1224
|
-
return formattedSchema
|
|
1332
|
+
return formattedSchema;
|
|
1225
1333
|
}
|
|
1226
1334
|
|
|
1227
|
-
function responseFinishReason(
|
|
1335
|
+
function responseFinishReason(
|
|
1336
|
+
response: OpenAI.Responses.Response,
|
|
1337
|
+
tools?: ToolUse<unknown>[] | undefined,
|
|
1338
|
+
): string | undefined {
|
|
1228
1339
|
if (tools && tools.length > 0) {
|
|
1229
|
-
return
|
|
1340
|
+
return 'tool_use';
|
|
1230
1341
|
}
|
|
1231
1342
|
if (response.status === 'incomplete') {
|
|
1232
1343
|
if (response.incomplete_details?.reason === 'max_output_tokens') {
|
|
@@ -1305,7 +1416,31 @@ export function fixOrphanedToolUse(items: ResponseInputItem[]): ResponseInputIte
|
|
|
1305
1416
|
return result;
|
|
1306
1417
|
}
|
|
1307
1418
|
|
|
1308
|
-
|
|
1419
|
+
/**
|
|
1420
|
+
* Drop function_call_output items whose call_id has no matching function_call
|
|
1421
|
+
* item anywhere in the input. Mirror of {@link fixOrphanedToolUse}: that
|
|
1422
|
+
* synthesizes outputs for calls left unanswered (e.g. a cancelled run); this
|
|
1423
|
+
* removes outputs left dangling after their function_call was dropped (e.g. by
|
|
1424
|
+
* conversation compaction/trimming). The OpenAI Responses API requires every
|
|
1425
|
+
* function_call_output to reference a prior function_call's call_id.
|
|
1426
|
+
*/
|
|
1427
|
+
export function fixOrphanedToolResults(items: ResponseInputItem[]): ResponseInputItem[] {
|
|
1428
|
+
if (items.length === 0) return items;
|
|
1429
|
+
const callIds = new Set<string>();
|
|
1430
|
+
for (const item of items) {
|
|
1431
|
+
if ('type' in item && item.type === 'function_call') {
|
|
1432
|
+
callIds.add(item.call_id);
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
return items.filter((item) => {
|
|
1436
|
+
if ('type' in item && item.type === 'function_call_output') {
|
|
1437
|
+
return callIds.has(item.call_id);
|
|
1438
|
+
}
|
|
1439
|
+
return true;
|
|
1440
|
+
});
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
function safeJsonParse(value: unknown): unknown {
|
|
1309
1444
|
if (typeof value !== 'string') {
|
|
1310
1445
|
return value;
|
|
1311
1446
|
}
|