@llumiverse/drivers 1.3.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/{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
|
@@ -1,42 +1,46 @@
|
|
|
1
1
|
import {
|
|
2
|
-
type AIModel,
|
|
2
|
+
type AIModel,
|
|
3
|
+
type Completion,
|
|
4
|
+
type CompletionChunkObject,
|
|
5
|
+
type ExecutionOptions,
|
|
6
|
+
type LlumiverseError,
|
|
3
7
|
type LlumiverseErrorContext,
|
|
4
8
|
ModelType,
|
|
5
9
|
type PromptSegment,
|
|
6
10
|
type VertexAIClaudeOptions,
|
|
7
|
-
} from
|
|
8
|
-
import type { ClaudePrompt } from
|
|
11
|
+
} from '@llumiverse/core';
|
|
12
|
+
import type { ClaudePrompt } from '../../shared/claude-messages.js';
|
|
9
13
|
import {
|
|
10
14
|
executeClaudeCompletion,
|
|
11
15
|
formatAnthropicLlumiverseError,
|
|
12
16
|
formatClaudePrompt,
|
|
13
17
|
isClaudeErrorRetryable,
|
|
14
18
|
streamClaudeCompletion,
|
|
15
|
-
} from
|
|
19
|
+
} from '../../shared/claude-messages.js';
|
|
16
20
|
|
|
17
|
-
|
|
18
|
-
import type {
|
|
19
|
-
import type { ModelDefinition } from "../models.js";
|
|
21
|
+
import type { VertexAIDriver } from '../index.js';
|
|
22
|
+
import type { ModelDefinition } from '../models.js';
|
|
20
23
|
|
|
21
24
|
export const ANTHROPIC_REGIONS: Record<string, string> = {
|
|
22
|
-
us:
|
|
23
|
-
europe:
|
|
24
|
-
global:
|
|
25
|
-
}
|
|
25
|
+
us: 'us-east5',
|
|
26
|
+
europe: 'europe-west1',
|
|
27
|
+
global: 'global',
|
|
28
|
+
};
|
|
26
29
|
|
|
27
|
-
export const NON_GLOBAL_ANTHROPIC_MODELS = [
|
|
28
|
-
"claude-3-5",
|
|
29
|
-
"claude-3",
|
|
30
|
-
];
|
|
30
|
+
export const NON_GLOBAL_ANTHROPIC_MODELS = ['claude-3-5', 'claude-3'];
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Parse a VertexAI model path (e.g. "locations/us-east5/claude-3-5-sonnet") into
|
|
34
34
|
* its region and model name components.
|
|
35
35
|
*/
|
|
36
|
-
function resolveVertexAIModelPath(options: ExecutionOptions): {
|
|
37
|
-
|
|
36
|
+
function resolveVertexAIModelPath(options: ExecutionOptions): {
|
|
37
|
+
modelName: string;
|
|
38
|
+
region: string | undefined;
|
|
39
|
+
options: ExecutionOptions;
|
|
40
|
+
} {
|
|
41
|
+
const splits = options.model.split('/');
|
|
38
42
|
let region: string | undefined;
|
|
39
|
-
if (splits[0] ===
|
|
43
|
+
if (splits[0] === 'locations' && splits.length >= 2) {
|
|
40
44
|
region = splits[1];
|
|
41
45
|
}
|
|
42
46
|
const modelName = splits[splits.length - 1];
|
|
@@ -44,8 +48,7 @@ function resolveVertexAIModelPath(options: ExecutionOptions): { modelName: strin
|
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
export class ClaudeModelDefinition implements ModelDefinition<ClaudePrompt> {
|
|
47
|
-
|
|
48
|
-
model: AIModel
|
|
51
|
+
model: AIModel;
|
|
49
52
|
|
|
50
53
|
constructor(modelId: string) {
|
|
51
54
|
this.model = {
|
|
@@ -57,30 +60,46 @@ export class ClaudeModelDefinition implements ModelDefinition<ClaudePrompt> {
|
|
|
57
60
|
} satisfies AIModel;
|
|
58
61
|
}
|
|
59
62
|
|
|
60
|
-
async createPrompt(
|
|
61
|
-
|
|
63
|
+
async createPrompt(
|
|
64
|
+
_driver: VertexAIDriver,
|
|
65
|
+
segments: PromptSegment[],
|
|
66
|
+
options: ExecutionOptions,
|
|
67
|
+
): Promise<ClaudePrompt> {
|
|
68
|
+
return formatClaudePrompt(segments, options, _driver.logger);
|
|
62
69
|
}
|
|
63
70
|
|
|
64
|
-
async requestTextCompletion(
|
|
71
|
+
async requestTextCompletion(
|
|
72
|
+
driver: VertexAIDriver,
|
|
73
|
+
prompt: ClaudePrompt,
|
|
74
|
+
options: ExecutionOptions,
|
|
75
|
+
): Promise<Completion> {
|
|
65
76
|
const { region, options: resolvedOptions } = resolveVertexAIModelPath(options);
|
|
66
|
-
const client = await driver.getAnthropicClient(region);
|
|
77
|
+
const client = await driver.getAnthropicClient(region, resolvedOptions.httpTimeout);
|
|
67
78
|
const model_options = resolvedOptions.model_options as VertexAIClaudeOptions | undefined;
|
|
68
|
-
if (
|
|
69
|
-
model_options?._option_id !==
|
|
70
|
-
model_options?._option_id !==
|
|
71
|
-
|
|
79
|
+
if (
|
|
80
|
+
model_options?._option_id !== undefined &&
|
|
81
|
+
model_options?._option_id !== 'vertexai-claude' &&
|
|
82
|
+
model_options?._option_id !== 'text-fallback'
|
|
83
|
+
) {
|
|
84
|
+
driver.logger.debug({ options: resolvedOptions.model_options }, 'Unexpected option id');
|
|
72
85
|
}
|
|
73
86
|
return executeClaudeCompletion(client, prompt, resolvedOptions);
|
|
74
87
|
}
|
|
75
88
|
|
|
76
|
-
async requestTextCompletionStream(
|
|
89
|
+
async requestTextCompletionStream(
|
|
90
|
+
driver: VertexAIDriver,
|
|
91
|
+
prompt: ClaudePrompt,
|
|
92
|
+
options: ExecutionOptions,
|
|
93
|
+
): Promise<AsyncIterable<CompletionChunkObject>> {
|
|
77
94
|
const { region, options: resolvedOptions } = resolveVertexAIModelPath(options);
|
|
78
|
-
const client = await driver.getAnthropicClient(region);
|
|
95
|
+
const client = await driver.getAnthropicClient(region, resolvedOptions.httpTimeout);
|
|
79
96
|
const model_options = resolvedOptions.model_options as VertexAIClaudeOptions | undefined;
|
|
80
|
-
if (
|
|
81
|
-
model_options?._option_id !==
|
|
82
|
-
model_options?._option_id !==
|
|
83
|
-
|
|
97
|
+
if (
|
|
98
|
+
model_options?._option_id !== undefined &&
|
|
99
|
+
model_options?._option_id !== 'vertexai-claude' &&
|
|
100
|
+
model_options?._option_id !== 'text-fallback'
|
|
101
|
+
) {
|
|
102
|
+
driver.logger.debug({ options: resolvedOptions.model_options }, 'Unexpected option id');
|
|
84
103
|
}
|
|
85
104
|
return streamClaudeCompletion(client, prompt, resolvedOptions);
|
|
86
105
|
}
|
|
@@ -88,16 +107,12 @@ export class ClaudeModelDefinition implements ModelDefinition<ClaudePrompt> {
|
|
|
88
107
|
isClaudeErrorRetryable(
|
|
89
108
|
error: unknown,
|
|
90
109
|
httpStatusCode: number | undefined,
|
|
91
|
-
errorType: string | undefined
|
|
110
|
+
errorType: string | undefined,
|
|
92
111
|
): boolean | undefined {
|
|
93
112
|
return isClaudeErrorRetryable(error, httpStatusCode, errorType);
|
|
94
113
|
}
|
|
95
114
|
|
|
96
|
-
formatLlumiverseError(
|
|
97
|
-
_driver: VertexAIDriver,
|
|
98
|
-
error: unknown,
|
|
99
|
-
context: LlumiverseErrorContext
|
|
100
|
-
): LlumiverseError {
|
|
115
|
+
formatLlumiverseError(_driver: VertexAIDriver, error: unknown, context: LlumiverseErrorContext): LlumiverseError {
|
|
101
116
|
return formatAnthropicLlumiverseError(error, context);
|
|
102
117
|
}
|
|
103
118
|
}
|
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
* Fix: use a local `payloadContents` variable so the caller's conversation is never mutated.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import { ExecutionOptions } from '@llumiverse/core';
|
|
17
16
|
import { FinishReason } from '@google/genai';
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
17
|
+
import { type DataSource, type ExecutionOptions, PromptRole, type PromptSegment } from '@llumiverse/core';
|
|
18
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
19
|
+
import type { GenerateContentPrompt, VertexAIDriver } from '../index.js';
|
|
20
20
|
import { convertGeminiFunctionPartsToText, GeminiModelDefinition } from './gemini.js';
|
|
21
21
|
|
|
22
22
|
// ---------------------------------------------------------------------------
|
|
@@ -35,8 +35,8 @@ describe('convertGeminiFunctionPartsToText', () => {
|
|
|
35
35
|
parts: [{ functionResponse: { name: 'plan', response: { output: 'done' } } }],
|
|
36
36
|
},
|
|
37
37
|
];
|
|
38
|
-
const originalItemRefs = original.map(c => c);
|
|
39
|
-
const originalPartRefs = original.map(c => c.parts[0]);
|
|
38
|
+
const originalItemRefs = original.map((c) => c);
|
|
39
|
+
const originalPartRefs = original.map((c) => c.parts[0]);
|
|
40
40
|
|
|
41
41
|
const result = convertGeminiFunctionPartsToText(original);
|
|
42
42
|
|
|
@@ -64,7 +64,7 @@ describe('convertGeminiFunctionPartsToText', () => {
|
|
|
64
64
|
|
|
65
65
|
const result = convertGeminiFunctionPartsToText(contents);
|
|
66
66
|
|
|
67
|
-
expect(result[0].parts
|
|
67
|
+
expect(result[0].parts?.[0]).toEqual({
|
|
68
68
|
text: '[Tool call: get_weather({"location":"Paris"})]',
|
|
69
69
|
});
|
|
70
70
|
});
|
|
@@ -79,7 +79,7 @@ describe('convertGeminiFunctionPartsToText', () => {
|
|
|
79
79
|
|
|
80
80
|
const result = convertGeminiFunctionPartsToText(contents);
|
|
81
81
|
|
|
82
|
-
expect(result[0].parts
|
|
82
|
+
expect(result[0].parts?.[0]).toEqual({
|
|
83
83
|
text: '[Tool result for get_weather: {"temperature":"15°C"}]',
|
|
84
84
|
});
|
|
85
85
|
});
|
|
@@ -90,7 +90,7 @@ describe('convertGeminiFunctionPartsToText', () => {
|
|
|
90
90
|
|
|
91
91
|
const result = convertGeminiFunctionPartsToText(contents);
|
|
92
92
|
|
|
93
|
-
expect(result[0].parts
|
|
93
|
+
expect(result[0].parts?.[0]).toBe(textPart);
|
|
94
94
|
});
|
|
95
95
|
});
|
|
96
96
|
|
|
@@ -105,7 +105,10 @@ function makeContentsWithFunctionParts() {
|
|
|
105
105
|
];
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
function makeDriver(overrides: {
|
|
108
|
+
function makeDriver(overrides: {
|
|
109
|
+
generateContent?: () => Promise<unknown>;
|
|
110
|
+
generateContentStream?: () => Promise<AsyncIterable<unknown>>;
|
|
111
|
+
}) {
|
|
109
112
|
return {
|
|
110
113
|
logger: { warn: () => {}, info: () => {}, error: () => {} },
|
|
111
114
|
getGoogleGenAIClient: () => ({
|
|
@@ -119,30 +122,64 @@ function makeDriver(overrides: { generateContent?: () => Promise<any>; generateC
|
|
|
119
122
|
|
|
120
123
|
const mockNonStreamingResponse = {
|
|
121
124
|
usageMetadata: { promptTokenCount: 10, candidatesTokenCount: 5, totalTokenCount: 15 },
|
|
122
|
-
candidates: [
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
125
|
+
candidates: [
|
|
126
|
+
{
|
|
127
|
+
finishReason: FinishReason.STOP,
|
|
128
|
+
content: { role: 'model', parts: [{ text: 'Summary.' }] },
|
|
129
|
+
safetyRatings: [],
|
|
130
|
+
},
|
|
131
|
+
],
|
|
127
132
|
};
|
|
128
133
|
|
|
129
134
|
const mockStreamingChunk = {
|
|
130
135
|
usageMetadata: { promptTokenCount: 10, candidatesTokenCount: 5, totalTokenCount: 15 },
|
|
131
|
-
candidates: [
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
+
candidates: [
|
|
137
|
+
{
|
|
138
|
+
finishReason: FinishReason.STOP,
|
|
139
|
+
content: { role: 'model', parts: [{ text: 'Summary.' }] },
|
|
140
|
+
safetyRatings: [],
|
|
141
|
+
},
|
|
142
|
+
],
|
|
136
143
|
};
|
|
137
144
|
|
|
138
145
|
describe('GeminiModelDefinition - no conversation mutation', () => {
|
|
146
|
+
it('createPrompt uses DataSource URIs directly for Gemini file data', async () => {
|
|
147
|
+
const modelDef = new GeminiModelDefinition('gemini-2.0-flash');
|
|
148
|
+
const file: DataSource = {
|
|
149
|
+
name: 'doc.pdf',
|
|
150
|
+
mime_type: 'application/pdf',
|
|
151
|
+
getURI: vi.fn().mockResolvedValue('gs://test-bucket/doc.pdf'),
|
|
152
|
+
getURL: vi.fn().mockResolvedValue('https://signed.example/doc.pdf'),
|
|
153
|
+
getStream: vi.fn().mockResolvedValue(new ReadableStream()),
|
|
154
|
+
};
|
|
155
|
+
const segments: PromptSegment[] = [
|
|
156
|
+
{
|
|
157
|
+
role: PromptRole.user,
|
|
158
|
+
files: [file],
|
|
159
|
+
} as PromptSegment,
|
|
160
|
+
];
|
|
161
|
+
const options: ExecutionOptions = { model: 'publishers/google/models/gemini-2.0-flash' };
|
|
162
|
+
|
|
163
|
+
const prompt = await modelDef.createPrompt({} as VertexAIDriver, segments, options);
|
|
164
|
+
|
|
165
|
+
expect(file.getURI).toHaveBeenCalledTimes(1);
|
|
166
|
+
expect(file.getURL).not.toHaveBeenCalled();
|
|
167
|
+
expect(file.getStream).not.toHaveBeenCalled();
|
|
168
|
+
expect(prompt.contents[0].parts?.[0]).toEqual({
|
|
169
|
+
fileData: {
|
|
170
|
+
fileUri: 'gs://test-bucket/doc.pdf',
|
|
171
|
+
mimeType: 'application/pdf',
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
139
176
|
it('requestTextCompletion: does not mutate prompt.contents when tools=[] and conversation has function parts', async () => {
|
|
140
177
|
const modelDef = new GeminiModelDefinition('gemini-2.0-flash');
|
|
141
178
|
const originalContents = makeContentsWithFunctionParts();
|
|
142
179
|
const contentsSnapshot = JSON.stringify(originalContents);
|
|
143
180
|
|
|
144
181
|
const driver = makeDriver({ generateContent: async () => mockNonStreamingResponse });
|
|
145
|
-
const prompt = { contents: originalContents, system: undefined } as
|
|
182
|
+
const prompt = { contents: originalContents, system: undefined } as unknown as GenerateContentPrompt;
|
|
146
183
|
const options: ExecutionOptions = { model: 'publishers/google/models/gemini-2.0-flash', tools: [] };
|
|
147
184
|
|
|
148
185
|
await modelDef.requestTextCompletion(driver, prompt, options);
|
|
@@ -158,14 +195,19 @@ describe('GeminiModelDefinition - no conversation mutation', () => {
|
|
|
158
195
|
const contentsSnapshot = JSON.stringify(originalContents);
|
|
159
196
|
|
|
160
197
|
const driver = makeDriver({
|
|
161
|
-
generateContentStream: async () =>
|
|
198
|
+
generateContentStream: async () =>
|
|
199
|
+
(async function* () {
|
|
200
|
+
yield mockStreamingChunk;
|
|
201
|
+
})(),
|
|
162
202
|
});
|
|
163
|
-
const prompt = { contents: originalContents, system: undefined } as
|
|
203
|
+
const prompt = { contents: originalContents, system: undefined } as unknown as GenerateContentPrompt;
|
|
164
204
|
const options: ExecutionOptions = { model: 'publishers/google/models/gemini-2.0-flash', tools: [] };
|
|
165
205
|
|
|
166
206
|
const stream = await modelDef.requestTextCompletionStream(driver, prompt, options);
|
|
167
207
|
// Drain the stream to trigger all processing
|
|
168
|
-
for await (const _chunk of stream) {
|
|
208
|
+
for await (const _chunk of stream) {
|
|
209
|
+
/* noop */
|
|
210
|
+
}
|
|
169
211
|
|
|
170
212
|
expect(JSON.stringify(originalContents)).toBe(contentsSnapshot);
|
|
171
213
|
expect(originalContents[0].parts[0]).toHaveProperty('functionCall');
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { LlumiverseError } from '@llumiverse/core';
|
|
2
2
|
import { beforeEach, describe, expect, it } from 'vitest';
|
|
3
|
+
import { exposePrivate, getProp } from '../../../test/__helpers__/test-utils.js';
|
|
3
4
|
import { VertexAIDriver } from '../index.js';
|
|
4
5
|
import { GeminiModelDefinition } from './gemini.js';
|
|
5
6
|
|
|
7
|
+
type GeminiModelInternals = {
|
|
8
|
+
isGeminiErrorRetryable: (httpStatusCode: number, message?: string) => boolean | undefined;
|
|
9
|
+
};
|
|
10
|
+
|
|
6
11
|
describe('GeminiModelDefinition Error Handling', () => {
|
|
7
12
|
let driver: VertexAIDriver;
|
|
8
13
|
let modelDef: GeminiModelDefinition;
|
|
@@ -88,6 +93,33 @@ describe('GeminiModelDefinition Error Handling', () => {
|
|
|
88
93
|
expect(error.name).toBe('NOT_FOUND');
|
|
89
94
|
});
|
|
90
95
|
|
|
96
|
+
it('should mark a client-closed/aborted request (499) as retryable despite the 4xx status', () => {
|
|
97
|
+
const error = modelDef.formatLlumiverseError(
|
|
98
|
+
driver,
|
|
99
|
+
{ status: 499, message: 'This operation was aborted' },
|
|
100
|
+
{ provider: 'vertexai', model: 'gemini-2.0-flash', operation: 'execute' },
|
|
101
|
+
);
|
|
102
|
+
expect(error.retryable).toBe(true);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should mark a client-closed/cancelled request (499) as retryable despite the 4xx status', () => {
|
|
106
|
+
const error = modelDef.formatLlumiverseError(
|
|
107
|
+
driver,
|
|
108
|
+
{ status: 499, message: 'Client Closed Request: The operation was cancelled.' },
|
|
109
|
+
{ provider: 'vertexai', model: 'gemini-2.0-flash', operation: 'execute' },
|
|
110
|
+
);
|
|
111
|
+
expect(error.retryable).toBe(true);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should mark DEADLINE_EXCEEDED as retryable even under a 4xx status', () => {
|
|
115
|
+
const error = modelDef.formatLlumiverseError(
|
|
116
|
+
driver,
|
|
117
|
+
{ status: 400, message: 'DEADLINE_EXCEEDED: Deadline expired before operation could complete' },
|
|
118
|
+
{ provider: 'vertexai', model: 'gemini-2.0-flash', operation: 'execute' },
|
|
119
|
+
);
|
|
120
|
+
expect(error.retryable).toBe(true);
|
|
121
|
+
});
|
|
122
|
+
|
|
91
123
|
it('should handle RESOURCE_EXHAUSTED error (429) as retryable', () => {
|
|
92
124
|
const googleError = {
|
|
93
125
|
status: 429,
|
|
@@ -202,7 +234,7 @@ describe('GeminiModelDefinition Error Handling', () => {
|
|
|
202
234
|
});
|
|
203
235
|
|
|
204
236
|
expect(error.originalError).toBe(googleError);
|
|
205
|
-
expect((error.originalError
|
|
237
|
+
expect(getProp<number>(error.originalError, 'status')).toBe(429);
|
|
206
238
|
});
|
|
207
239
|
|
|
208
240
|
it('should throw for non-Google API errors', () => {
|
|
@@ -247,6 +279,29 @@ describe('GeminiModelDefinition Error Handling', () => {
|
|
|
247
279
|
expect(error.name).toBe('ValidationError');
|
|
248
280
|
});
|
|
249
281
|
|
|
282
|
+
it('should mark URL_REJECTED-REJECTED_CLIENT_THROTTLED 400s as retryable', () => {
|
|
283
|
+
// Reproduces a real Vertex AI failure: passing an audio file by URL
|
|
284
|
+
// (sys:AnalyzeAudio) sometimes 400s with INVALID_ARGUMENT whose
|
|
285
|
+
// message contains URL_REJECTED-REJECTED_CLIENT_THROTTLED — that's
|
|
286
|
+
// really a transient throttle on Google's URL fetcher.
|
|
287
|
+
const googleError = {
|
|
288
|
+
status: 400,
|
|
289
|
+
message:
|
|
290
|
+
'Cannot fetch content from the provided URL. Please ensure the URL is valid ' +
|
|
291
|
+
'and accessible by Vertex AI. Vertex AI respects robots.txt rules, so confirm ' +
|
|
292
|
+
'the URL is allowed to be crawled. Status: URL_REJECTED-REJECTED_CLIENT_THROTTLED',
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
const error = modelDef.formatLlumiverseError(driver, googleError, {
|
|
296
|
+
provider: 'vertexai',
|
|
297
|
+
model: 'gemini-2.5-flash',
|
|
298
|
+
operation: 'execute',
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
expect(error.code).toBe(400);
|
|
302
|
+
expect(error.retryable).toBe(true);
|
|
303
|
+
});
|
|
304
|
+
|
|
250
305
|
it('should handle errors without extractable name', () => {
|
|
251
306
|
const googleError = {
|
|
252
307
|
status: 500,
|
|
@@ -263,14 +318,44 @@ describe('GeminiModelDefinition Error Handling', () => {
|
|
|
263
318
|
expect(error.name).toBe('LlumiverseError');
|
|
264
319
|
expect(error.code).toBe(500);
|
|
265
320
|
});
|
|
321
|
+
|
|
322
|
+
it('should treat invalid_grant credential issuer failures as non-retryable', () => {
|
|
323
|
+
const authError = new Error("Error code invalid_grant: Error connecting to the given credential's issuer.");
|
|
324
|
+
|
|
325
|
+
const error = driver.formatLlumiverseError(authError, {
|
|
326
|
+
provider: 'vertexai',
|
|
327
|
+
model: 'gemini-2.5-flash',
|
|
328
|
+
operation: 'execute',
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
expect(error.retryable).toBe(false);
|
|
332
|
+
expect(error.code).toBeUndefined();
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it('should preserve invalid_grant from Google API errors', () => {
|
|
336
|
+
const googleError = {
|
|
337
|
+
status: 400,
|
|
338
|
+
message: "Error code invalid_grant: Error connecting to the given credential's issuer.",
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
const error = modelDef.formatLlumiverseError(driver, googleError, {
|
|
342
|
+
provider: 'vertexai',
|
|
343
|
+
model: 'locations/global/publishers/google/models/gemini-2.5-flash',
|
|
344
|
+
operation: 'execute',
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
expect(error.retryable).toBe(false);
|
|
348
|
+
expect(error.code).toBe(400);
|
|
349
|
+
expect(error.name).toBe('invalid_grant');
|
|
350
|
+
});
|
|
266
351
|
});
|
|
267
352
|
|
|
268
353
|
describe('isGeminiErrorRetryable', () => {
|
|
269
354
|
it('should classify retryable status codes correctly', () => {
|
|
270
|
-
const retryableStatusCodes = [408, 429, 500, 502, 503, 504];
|
|
355
|
+
const retryableStatusCodes = [408, 429, 499, 500, 502, 503, 504];
|
|
271
356
|
|
|
272
357
|
retryableStatusCodes.forEach((statusCode) => {
|
|
273
|
-
const result = (modelDef
|
|
358
|
+
const result = exposePrivate<GeminiModelInternals>(modelDef).isGeminiErrorRetryable(statusCode);
|
|
274
359
|
expect(result, `Status code ${statusCode} should be retryable`).toBe(true);
|
|
275
360
|
});
|
|
276
361
|
});
|
|
@@ -279,21 +364,52 @@ describe('GeminiModelDefinition Error Handling', () => {
|
|
|
279
364
|
const nonRetryableStatusCodes = [400, 401, 403, 404, 409];
|
|
280
365
|
|
|
281
366
|
nonRetryableStatusCodes.forEach((statusCode) => {
|
|
282
|
-
const result = (modelDef
|
|
367
|
+
const result = exposePrivate<GeminiModelInternals>(modelDef).isGeminiErrorRetryable(statusCode);
|
|
283
368
|
expect(result, `Status code ${statusCode} should not be retryable`).toBe(false);
|
|
284
369
|
});
|
|
285
370
|
});
|
|
286
371
|
|
|
287
372
|
it('should classify other 5xx errors as retryable', () => {
|
|
288
|
-
expect((modelDef
|
|
289
|
-
expect((modelDef
|
|
290
|
-
expect((modelDef
|
|
373
|
+
expect(exposePrivate<GeminiModelInternals>(modelDef).isGeminiErrorRetryable(501)).toBe(true);
|
|
374
|
+
expect(exposePrivate<GeminiModelInternals>(modelDef).isGeminiErrorRetryable(505)).toBe(true);
|
|
375
|
+
expect(exposePrivate<GeminiModelInternals>(modelDef).isGeminiErrorRetryable(599)).toBe(true);
|
|
291
376
|
});
|
|
292
377
|
|
|
293
378
|
it('should classify other 4xx errors as non-retryable', () => {
|
|
294
|
-
expect((modelDef
|
|
295
|
-
expect((modelDef
|
|
296
|
-
|
|
379
|
+
expect(exposePrivate<GeminiModelInternals>(modelDef).isGeminiErrorRetryable(402)).toBe(false);
|
|
380
|
+
expect(exposePrivate<GeminiModelInternals>(modelDef).isGeminiErrorRetryable(405)).toBe(false);
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
it('should treat 400 URL_REJECTED-REJECTED_CLIENT_THROTTLED as retryable', () => {
|
|
384
|
+
// Vertex AI surfaces inline-URL-fetcher throttling as 400 INVALID_ARGUMENT.
|
|
385
|
+
// The status is misleading — it's a transient Google-side throttle.
|
|
386
|
+
const message =
|
|
387
|
+
'Cannot fetch content from the provided URL. ' + 'Status: URL_REJECTED-REJECTED_CLIENT_THROTTLED';
|
|
388
|
+
expect(exposePrivate<GeminiModelInternals>(modelDef).isGeminiErrorRetryable(400, message)).toBe(true);
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
it('should treat 400 URL_REJECTED-REJECTED_RATE_LIMITED as retryable', () => {
|
|
392
|
+
const message =
|
|
393
|
+
'Cannot fetch content from the provided URL. ' + 'Status: URL_REJECTED-REJECTED_RATE_LIMITED';
|
|
394
|
+
expect(exposePrivate<GeminiModelInternals>(modelDef).isGeminiErrorRetryable(400, message)).toBe(true);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it('should still treat plain 400 INVALID_ARGUMENT as non-retryable', () => {
|
|
398
|
+
expect(
|
|
399
|
+
exposePrivate<GeminiModelInternals>(modelDef).isGeminiErrorRetryable(
|
|
400
|
+
400,
|
|
401
|
+
'INVALID_ARGUMENT: bad input',
|
|
402
|
+
),
|
|
403
|
+
).toBe(false);
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
it('should treat invalid_grant as non-retryable before status-based retry rules', () => {
|
|
407
|
+
expect(
|
|
408
|
+
exposePrivate<GeminiModelInternals>(modelDef).isGeminiErrorRetryable(
|
|
409
|
+
503,
|
|
410
|
+
"Error code invalid_grant: Error connecting to the given credential's issuer.",
|
|
411
|
+
),
|
|
412
|
+
).toBe(false);
|
|
297
413
|
});
|
|
298
414
|
});
|
|
299
415
|
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for fixOrphanedToolResults (Gemini).
|
|
3
|
+
*
|
|
4
|
+
* Gemini pairs a `functionResponse` part to its `functionCall` part by name. A
|
|
5
|
+
* functionResponse left dangling after its functionCall was dropped — e.g. by
|
|
6
|
+
* conversation compaction trimming the model tool-call turn, or an unmergeable
|
|
7
|
+
* parallel batch — causes the Gemini/Vertex API to reject the request. This is
|
|
8
|
+
* the same class of bug fixed in the Claude, Bedrock, and OpenAI drivers.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { Content } from '@google/genai';
|
|
12
|
+
import type { ExecutionOptions } from '@llumiverse/core';
|
|
13
|
+
import { describe, expect, test } from 'vitest';
|
|
14
|
+
import { fixOrphanedToolResults, getGeminiPayload } from './gemini.js';
|
|
15
|
+
|
|
16
|
+
const OPTIONS_WITH_TOOLS = {
|
|
17
|
+
model: 'gemini-2.5-flash',
|
|
18
|
+
tools: [
|
|
19
|
+
{ name: 'a', description: '', input_schema: { type: 'object', properties: {} } },
|
|
20
|
+
{ name: 'b', description: '', input_schema: { type: 'object', properties: {} } },
|
|
21
|
+
],
|
|
22
|
+
} as unknown as ExecutionOptions;
|
|
23
|
+
|
|
24
|
+
function functionResponseNames(contents: Content[]): string[] {
|
|
25
|
+
return contents.flatMap((content) =>
|
|
26
|
+
(content.parts ?? []).flatMap((part) => (part.functionResponse?.name ? [part.functionResponse.name] : [])),
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe('fixOrphanedToolResults - Gemini', () => {
|
|
31
|
+
test('returns empty array for empty input', () => {
|
|
32
|
+
expect(fixOrphanedToolResults([])).toEqual([]);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('keeps a functionResponse that has a matching functionCall in the previous model content', () => {
|
|
36
|
+
const contents: Content[] = [
|
|
37
|
+
{ role: 'model', parts: [{ functionCall: { name: 'search', args: {} } }] },
|
|
38
|
+
{ role: 'user', parts: [{ functionResponse: { name: 'search', response: { ok: true } } }] },
|
|
39
|
+
];
|
|
40
|
+
expect(fixOrphanedToolResults(contents)).toEqual(contents);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('keeps all responses of a parallel batch when both calls are present', () => {
|
|
44
|
+
const contents: Content[] = [
|
|
45
|
+
{
|
|
46
|
+
role: 'model',
|
|
47
|
+
parts: [{ functionCall: { name: 'a', args: {} } }, { functionCall: { name: 'b', args: {} } }],
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
role: 'user',
|
|
51
|
+
parts: [
|
|
52
|
+
{ functionResponse: { name: 'a', response: {} } },
|
|
53
|
+
{ functionResponse: { name: 'b', response: {} } },
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
];
|
|
57
|
+
expect(fixOrphanedToolResults(contents)).toEqual(contents);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('drops a functionResponse whose functionCall is absent from the previous content', () => {
|
|
61
|
+
const contents: Content[] = [
|
|
62
|
+
{ role: 'model', parts: [{ functionCall: { name: 'a', args: {} } }] },
|
|
63
|
+
{
|
|
64
|
+
role: 'user',
|
|
65
|
+
parts: [
|
|
66
|
+
{ functionResponse: { name: 'a', response: {} } },
|
|
67
|
+
{ functionResponse: { name: 'gone', response: {} } },
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
const result = fixOrphanedToolResults(contents);
|
|
73
|
+
expect(result[1].parts).toHaveLength(1);
|
|
74
|
+
expect(result[1].parts?.[0].functionResponse?.name).toBe('a');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('drops a content that becomes empty after removing orphaned responses', () => {
|
|
78
|
+
const contents: Content[] = [
|
|
79
|
+
{ role: 'user', parts: [{ text: '[summary of prior work]' }] },
|
|
80
|
+
{ role: 'user', parts: [{ functionResponse: { name: 'gone', response: {} } }] },
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
const result = fixOrphanedToolResults(contents);
|
|
84
|
+
expect(result).toHaveLength(1);
|
|
85
|
+
expect(result[0].parts?.[0].text).toBe('[summary of prior work]');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('preserves non-functionResponse parts while dropping the orphan', () => {
|
|
89
|
+
const contents: Content[] = [
|
|
90
|
+
{ role: 'model', parts: [{ text: 'thinking' }] },
|
|
91
|
+
{
|
|
92
|
+
role: 'user',
|
|
93
|
+
parts: [{ functionResponse: { name: 'gone', response: {} } }, { text: 'continue please' }],
|
|
94
|
+
},
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
const result = fixOrphanedToolResults(contents);
|
|
98
|
+
expect(result[1].parts).toHaveLength(1);
|
|
99
|
+
expect(result[1].parts?.[0].text).toBe('continue please');
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('getGeminiPayload - orphaned tool results', () => {
|
|
104
|
+
test('retains split parallel functionResponses by merging consecutive user contents before cleanup', () => {
|
|
105
|
+
const contents: Content[] = [
|
|
106
|
+
{
|
|
107
|
+
role: 'model',
|
|
108
|
+
parts: [{ functionCall: { name: 'a', args: {} } }, { functionCall: { name: 'b', args: {} } }],
|
|
109
|
+
},
|
|
110
|
+
{ role: 'user', parts: [{ functionResponse: { name: 'a', response: { ok: true } } }] },
|
|
111
|
+
{ role: 'user', parts: [{ functionResponse: { name: 'b', response: { ok: true } } }] },
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
const payload = getGeminiPayload(OPTIONS_WITH_TOOLS, { contents });
|
|
115
|
+
|
|
116
|
+
expect(functionResponseNames(payload.contents as Content[])).toEqual(['a', 'b']);
|
|
117
|
+
});
|
|
118
|
+
});
|