@llumiverse/drivers 1.3.0 → 1.4.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/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/{types/anthropic → anthropic}/index.d.ts +4 -2
- package/lib/anthropic/index.d.ts.map +1 -0
- package/lib/{esm/anthropic → anthropic}/index.js +19 -8
- 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 +16 -5
- package/lib/openai/index.d.ts.map +1 -0
- package/lib/{esm/openai → openai}/index.js +242 -141
- 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/{types/shared → shared}/claude-messages.d.ts +25 -1
- package/lib/shared/claude-messages.d.ts.map +1 -0
- package/lib/{esm/shared → shared}/claude-messages.js +212 -59
- 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/{types/vertexai → vertexai}/models/claude.d.ts +4 -4
- package/lib/vertexai/models/claude.d.ts.map +1 -0
- package/lib/{esm/vertexai → vertexai}/models/claude.js +17 -20
- package/lib/vertexai/models/claude.js.map +1 -0
- package/lib/{types/vertexai → vertexai}/models/gemini.d.ts +28 -4
- package/lib/vertexai/models/gemini.d.ts.map +1 -0
- package/lib/{esm/vertexai → vertexai}/models/gemini.js +238 -116
- 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 +87 -36
- package/src/adobe/firefly.ts +60 -43
- package/src/anthropic/index.ts +37 -20
- 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 -16
- 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 +378 -248
- 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 +303 -97
- 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 +331 -244
- package/src/vertexai/models/claude-error-handling.test.ts +83 -36
- package/src/vertexai/models/claude-streaming-spacing.test.ts +82 -75
- package/src/vertexai/models/claude.ts +55 -40
- 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 +349 -172
- 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/anthropic/index.js +0 -64
- package/lib/cjs/anthropic/index.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 -32
- 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 -1135
- 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-messages.js +0 -737
- package/lib/cjs/shared/claude-messages.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 -72
- package/lib/cjs/vertexai/models/claude.js.map +0 -1
- package/lib/cjs/vertexai/models/gemini.js +0 -864
- 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/anthropic/index.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 -16
- 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-messages.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.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/anthropic/index.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 -16
- 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-messages.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.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,11 +959,15 @@ function createAssistantMessageFromCompletion(completion: Completion): ResponseI
|
|
|
880
959
|
return result;
|
|
881
960
|
}
|
|
882
961
|
|
|
883
|
-
export 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}`;
|
|
@@ -896,11 +979,11 @@ export function mapResponseStream(stream: AsyncIterable<OpenAI.Responses.Respons
|
|
|
896
979
|
if (event.item.call_id) {
|
|
897
980
|
toolCallMetadata.set(event.item.call_id, metadata);
|
|
898
981
|
}
|
|
899
|
-
const toolUse: ToolUse & { _actual_id?: string } = {
|
|
982
|
+
const toolUse: ToolUse<unknown> & { _actual_id?: string } = {
|
|
900
983
|
id: syntheticId,
|
|
901
984
|
_actual_id: callId,
|
|
902
985
|
tool_name: event.item.name,
|
|
903
|
-
tool_input: ''
|
|
986
|
+
tool_input: '',
|
|
904
987
|
};
|
|
905
988
|
yield {
|
|
906
989
|
result: [],
|
|
@@ -910,11 +993,11 @@ export function mapResponseStream(stream: AsyncIterable<OpenAI.Responses.Respons
|
|
|
910
993
|
const metadata = toolCallMetadata.get(event.item_id);
|
|
911
994
|
const syntheticId = metadata?.syntheticId ?? `tool_${event.output_index}`;
|
|
912
995
|
const callId = metadata?.callId ?? event.item_id;
|
|
913
|
-
const toolUse: ToolUse & { _actual_id?: string } = {
|
|
996
|
+
const toolUse: ToolUse<unknown> & { _actual_id?: string } = {
|
|
914
997
|
id: syntheticId,
|
|
915
998
|
_actual_id: callId,
|
|
916
999
|
tool_name: metadata?.name ?? '',
|
|
917
|
-
tool_input: event.delta
|
|
1000
|
+
tool_input: event.delta,
|
|
918
1001
|
};
|
|
919
1002
|
yield {
|
|
920
1003
|
result: [],
|
|
@@ -933,13 +1016,33 @@ export function mapResponseStream(stream: AsyncIterable<OpenAI.Responses.Respons
|
|
|
933
1016
|
toolCallMetadata.set(event.item_id, { syntheticId, callId: metadata?.callId, name: tool_name });
|
|
934
1017
|
}
|
|
935
1018
|
} else if (event.type === 'response.output_text.delta') {
|
|
1019
|
+
hasTextDeltas = true;
|
|
936
1020
|
yield {
|
|
937
1021
|
result: textToCompletionResult(event.delta),
|
|
938
1022
|
} satisfies CompletionChunkObject;
|
|
939
|
-
}
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
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
|
+
) {
|
|
943
1046
|
const finalTools = collectTools(event.response.output);
|
|
944
1047
|
yield {
|
|
945
1048
|
result: [],
|
|
@@ -948,12 +1051,12 @@ export function mapResponseStream(stream: AsyncIterable<OpenAI.Responses.Respons
|
|
|
948
1051
|
} satisfies CompletionChunkObject;
|
|
949
1052
|
}
|
|
950
1053
|
}
|
|
951
|
-
}
|
|
1054
|
+
},
|
|
952
1055
|
};
|
|
953
1056
|
}
|
|
954
1057
|
|
|
955
1058
|
function insert_image_detail(items: ResponseInputItem[], detail_level: string): ResponseInputItem[] {
|
|
956
|
-
if (detail_level ===
|
|
1059
|
+
if (detail_level === 'auto' || detail_level === 'low' || detail_level === 'high') {
|
|
957
1060
|
for (const item of items) {
|
|
958
1061
|
// Check if it's an EasyInputMessage or Message with content array
|
|
959
1062
|
if ('role' in item && 'content' in item && item.role !== 'assistant') {
|
|
@@ -961,7 +1064,7 @@ function insert_image_detail(items: ResponseInputItem[], detail_level: string):
|
|
|
961
1064
|
if (Array.isArray(content)) {
|
|
962
1065
|
for (const part of content) {
|
|
963
1066
|
if (typeof part === 'object' && part.type === 'input_image') {
|
|
964
|
-
(part as
|
|
1067
|
+
(part as MutableInputImagePart).detail = detail_level;
|
|
965
1068
|
}
|
|
966
1069
|
}
|
|
967
1070
|
}
|
|
@@ -973,19 +1076,19 @@ function insert_image_detail(items: ResponseInputItem[], detail_level: string):
|
|
|
973
1076
|
|
|
974
1077
|
function convertRoles(items: ResponseInputItem[], model: string): ResponseInputItem[] {
|
|
975
1078
|
//New openai models use developer role instead of system
|
|
976
|
-
if (model.includes(
|
|
977
|
-
if (model.includes(
|
|
1079
|
+
if (model.includes('o1') || model.includes('o3')) {
|
|
1080
|
+
if (model.includes('o1-mini') || model.includes('o1-preview')) {
|
|
978
1081
|
//o1-mini and o1-preview support neither system nor developer
|
|
979
1082
|
for (const item of items) {
|
|
980
1083
|
if ('role' in item && (item as EasyInputMessage).role === 'system') {
|
|
981
|
-
(item as
|
|
1084
|
+
(item as MutableRoleItem).role = 'user';
|
|
982
1085
|
}
|
|
983
1086
|
}
|
|
984
1087
|
} else {
|
|
985
1088
|
//Models newer than o1 use developer role
|
|
986
1089
|
for (const item of items) {
|
|
987
1090
|
if ('role' in item && (item as EasyInputMessage).role === 'system') {
|
|
988
|
-
(item as
|
|
1091
|
+
(item as MutableRoleItem).role = 'developer';
|
|
989
1092
|
}
|
|
990
1093
|
}
|
|
991
1094
|
}
|
|
@@ -995,12 +1098,12 @@ function convertRoles(items: ResponseInputItem[], model: string): ResponseInputI
|
|
|
995
1098
|
|
|
996
1099
|
//Structured output support is typically aligned with tool use support
|
|
997
1100
|
//Not true for realtime models, which do not support structured output, but do support tool use.
|
|
998
|
-
function supportsSchema(model: string): boolean {
|
|
999
|
-
const realtimeModel = model.includes(
|
|
1101
|
+
function supportsSchema(model: string, provider: string | Providers): boolean {
|
|
1102
|
+
const realtimeModel = model.includes('realtime');
|
|
1000
1103
|
if (realtimeModel) {
|
|
1001
1104
|
return false;
|
|
1002
1105
|
}
|
|
1003
|
-
return supportsToolUse(model,
|
|
1106
|
+
return supportsToolUse(model, provider);
|
|
1004
1107
|
}
|
|
1005
1108
|
|
|
1006
1109
|
/**
|
|
@@ -1009,17 +1112,17 @@ function supportsSchema(model: string): boolean {
|
|
|
1009
1112
|
* tools to be defined in the API request.
|
|
1010
1113
|
*/
|
|
1011
1114
|
export function convertOpenAIFunctionItemsToText(items: ResponseInputItem[]): ResponseInputItem[] {
|
|
1012
|
-
const hasFunctionItems = items.some(item => {
|
|
1013
|
-
const type = (item as
|
|
1115
|
+
const hasFunctionItems = items.some((item) => {
|
|
1116
|
+
const type = (item as OpenAIFunctionItem).type;
|
|
1014
1117
|
return type === 'function_call' || type === 'function_call_output';
|
|
1015
1118
|
});
|
|
1016
1119
|
if (!hasFunctionItems) return items;
|
|
1017
1120
|
|
|
1018
|
-
return items.map(item => {
|
|
1019
|
-
const typed = item as
|
|
1121
|
+
return items.map((item) => {
|
|
1122
|
+
const typed = item as OpenAIFunctionItem;
|
|
1020
1123
|
if (typed.type === 'function_call') {
|
|
1021
1124
|
const argsStr = typed.arguments || '';
|
|
1022
|
-
const truncated = argsStr.length > 500 ? argsStr.substring(0, 500)
|
|
1125
|
+
const truncated = argsStr.length > 500 ? `${argsStr.substring(0, 500)}...` : argsStr;
|
|
1023
1126
|
return {
|
|
1024
1127
|
role: 'assistant' as const,
|
|
1025
1128
|
content: `[Tool call: ${typed.name}(${truncated})]`,
|
|
@@ -1027,7 +1130,7 @@ export function convertOpenAIFunctionItemsToText(items: ResponseInputItem[]): Re
|
|
|
1027
1130
|
}
|
|
1028
1131
|
if (typed.type === 'function_call_output') {
|
|
1029
1132
|
const output = typed.output || 'No output';
|
|
1030
|
-
const truncated = output.length > 500 ? output.substring(0, 500)
|
|
1133
|
+
const truncated = output.length > 500 ? `${output.substring(0, 500)}...` : output;
|
|
1031
1134
|
return {
|
|
1032
1135
|
role: 'user' as const,
|
|
1033
1136
|
content: `[Tool result: ${truncated}]`,
|
|
@@ -1041,15 +1144,14 @@ function getToolDefinitions(tools: ToolDefinition[] | undefined | null): OpenAI.
|
|
|
1041
1144
|
return tools ? tools.map(getToolDefinition) : undefined;
|
|
1042
1145
|
}
|
|
1043
1146
|
function getToolDefinition(toolDef: ToolDefinition): OpenAI.Responses.FunctionTool {
|
|
1044
|
-
let parsedSchema: JSONSchema | undefined
|
|
1147
|
+
let parsedSchema: JSONSchema | undefined;
|
|
1045
1148
|
let strictMode = false;
|
|
1046
1149
|
if (toolDef.input_schema) {
|
|
1047
1150
|
try {
|
|
1048
1151
|
//TODO: type assertion here is not safe, does not work with satisfies
|
|
1049
1152
|
parsedSchema = openAISchemaFormat(toolDef.input_schema as JSONSchema);
|
|
1050
1153
|
strictMode = true;
|
|
1051
|
-
}
|
|
1052
|
-
catch (e) {
|
|
1154
|
+
} catch {
|
|
1053
1155
|
//TODO: type assertion here is not safe, does not work with satisfies
|
|
1054
1156
|
parsedSchema = limitedSchemaFormat(toolDef.input_schema as JSONSchema);
|
|
1055
1157
|
strictMode = false;
|
|
@@ -1057,7 +1159,7 @@ function getToolDefinition(toolDef: ToolDefinition): OpenAI.Responses.FunctionTo
|
|
|
1057
1159
|
}
|
|
1058
1160
|
|
|
1059
1161
|
return {
|
|
1060
|
-
type:
|
|
1162
|
+
type: 'function',
|
|
1061
1163
|
name: toolDef.name,
|
|
1062
1164
|
description: toolDef.description,
|
|
1063
1165
|
parameters: parsedSchema ?? null,
|
|
@@ -1069,7 +1171,7 @@ function updateConversation(conversation: unknown, items: ResponseInputItem[]):
|
|
|
1069
1171
|
if (!items) {
|
|
1070
1172
|
// Unwrap array if wrapped, otherwise treat as array
|
|
1071
1173
|
const unwrapped = unwrapConversationArray<ResponseInputItem>(conversation);
|
|
1072
|
-
return unwrapped ?? (conversation as ResponseInputItem[] || []);
|
|
1174
|
+
return unwrapped ?? ((conversation as ResponseInputItem[]) || []);
|
|
1073
1175
|
}
|
|
1074
1176
|
if (!conversation) {
|
|
1075
1177
|
return items;
|
|
@@ -1080,12 +1182,12 @@ function updateConversation(conversation: unknown, items: ResponseInputItem[]):
|
|
|
1080
1182
|
return [...convArray, ...items];
|
|
1081
1183
|
}
|
|
1082
1184
|
|
|
1083
|
-
export function collectTools(output?: OpenAI.Responses.ResponseOutputItem[]): ToolUse[] | undefined {
|
|
1185
|
+
export function collectTools(output?: OpenAI.Responses.ResponseOutputItem[]): ToolUse<unknown>[] | undefined {
|
|
1084
1186
|
if (!output) {
|
|
1085
1187
|
return undefined;
|
|
1086
1188
|
}
|
|
1087
1189
|
|
|
1088
|
-
const tools: ToolUse[] = [];
|
|
1190
|
+
const tools: ToolUse<unknown>[] = [];
|
|
1089
1191
|
for (const item of output) {
|
|
1090
1192
|
if (item.type === 'function_call') {
|
|
1091
1193
|
const id = item.call_id || item.id;
|
|
@@ -1118,8 +1220,8 @@ function extractCompletionResults(output?: OpenAI.Responses.ResponseOutputItem[]
|
|
|
1118
1220
|
for (const part of item.content) {
|
|
1119
1221
|
if (part.type === 'output_text' && part.text) {
|
|
1120
1222
|
results.push({
|
|
1121
|
-
type:
|
|
1122
|
-
value: part.text
|
|
1223
|
+
type: 'text',
|
|
1224
|
+
value: part.text,
|
|
1123
1225
|
});
|
|
1124
1226
|
}
|
|
1125
1227
|
}
|
|
@@ -1127,12 +1229,10 @@ function extractCompletionResults(output?: OpenAI.Responses.ResponseOutputItem[]
|
|
|
1127
1229
|
// GPT-image models return base64 encoded images in result field
|
|
1128
1230
|
const base64Data = item.result;
|
|
1129
1231
|
// Format as data URL for consistency with other image outputs
|
|
1130
|
-
const imageUrl = base64Data.startsWith('data:')
|
|
1131
|
-
? base64Data
|
|
1132
|
-
: `data:image/png;base64,${base64Data}`;
|
|
1232
|
+
const imageUrl = base64Data.startsWith('data:') ? base64Data : `data:image/png;base64,${base64Data}`;
|
|
1133
1233
|
results.push({
|
|
1134
|
-
type:
|
|
1135
|
-
value: imageUrl
|
|
1234
|
+
type: 'image',
|
|
1235
|
+
value: imageUrl,
|
|
1136
1236
|
});
|
|
1137
1237
|
}
|
|
1138
1238
|
}
|
|
@@ -1141,7 +1241,7 @@ function extractCompletionResults(output?: OpenAI.Responses.ResponseOutputItem[]
|
|
|
1141
1241
|
|
|
1142
1242
|
//For strict mode false
|
|
1143
1243
|
function limitedSchemaFormat(schema: JSONSchema): JSONSchema {
|
|
1144
|
-
const formattedSchema = { ...schema };
|
|
1244
|
+
const formattedSchema: JSONSchema = { ...schema };
|
|
1145
1245
|
|
|
1146
1246
|
// Defaults not supported
|
|
1147
1247
|
delete formattedSchema.default;
|
|
@@ -1182,16 +1282,16 @@ function limitedSchemaFormat(schema: JSONSchema): JSONSchema {
|
|
|
1182
1282
|
//For strict mode true
|
|
1183
1283
|
function openAISchemaFormat(schema: JSONSchema, nesting: number = 0): JSONSchema {
|
|
1184
1284
|
if (nesting > 5) {
|
|
1185
|
-
throw new Error(
|
|
1285
|
+
throw new Error('OpenAI schema nesting too deep');
|
|
1186
1286
|
}
|
|
1187
1287
|
|
|
1188
|
-
const formattedSchema = { ...schema };
|
|
1288
|
+
const formattedSchema: JSONSchema = { ...schema };
|
|
1189
1289
|
|
|
1190
1290
|
// Defaults not supported
|
|
1191
1291
|
delete formattedSchema.default;
|
|
1192
1292
|
|
|
1193
1293
|
// Additional properties not supported, required to be set.
|
|
1194
|
-
if (formattedSchema?.type ===
|
|
1294
|
+
if (formattedSchema?.type === 'object') {
|
|
1195
1295
|
formattedSchema.additionalProperties = false;
|
|
1196
1296
|
}
|
|
1197
1297
|
|
|
@@ -1220,18 +1320,24 @@ function openAISchemaFormat(schema: JSONSchema, nesting: number = 0): JSONSchema
|
|
|
1220
1320
|
}
|
|
1221
1321
|
}
|
|
1222
1322
|
}
|
|
1223
|
-
if (
|
|
1323
|
+
if (
|
|
1324
|
+
formattedSchema?.type === 'object' &&
|
|
1325
|
+
(!formattedSchema?.properties || Object.keys(formattedSchema?.properties ?? {}).length === 0)
|
|
1326
|
+
) {
|
|
1224
1327
|
//If no properties are defined, then additionalProperties: true was set or the object would be empty.
|
|
1225
1328
|
//OpenAI does not support this on structured output/ strict mode.
|
|
1226
|
-
throw new Error(
|
|
1329
|
+
throw new Error('OpenAI does not support empty objects or objects with additionalProperties set to true');
|
|
1227
1330
|
}
|
|
1228
1331
|
|
|
1229
|
-
return formattedSchema
|
|
1332
|
+
return formattedSchema;
|
|
1230
1333
|
}
|
|
1231
1334
|
|
|
1232
|
-
function responseFinishReason(
|
|
1335
|
+
function responseFinishReason(
|
|
1336
|
+
response: OpenAI.Responses.Response,
|
|
1337
|
+
tools?: ToolUse<unknown>[] | undefined,
|
|
1338
|
+
): string | undefined {
|
|
1233
1339
|
if (tools && tools.length > 0) {
|
|
1234
|
-
return
|
|
1340
|
+
return 'tool_use';
|
|
1235
1341
|
}
|
|
1236
1342
|
if (response.status === 'incomplete') {
|
|
1237
1343
|
if (response.incomplete_details?.reason === 'max_output_tokens') {
|
|
@@ -1310,7 +1416,31 @@ export function fixOrphanedToolUse(items: ResponseInputItem[]): ResponseInputIte
|
|
|
1310
1416
|
return result;
|
|
1311
1417
|
}
|
|
1312
1418
|
|
|
1313
|
-
|
|
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 {
|
|
1314
1444
|
if (typeof value !== 'string') {
|
|
1315
1445
|
return value;
|
|
1316
1446
|
}
|