@llumiverse/drivers 1.2.0 → 1.4.0-dev.20260629.090753Z
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/{types/adobe → adobe}/firefly.d.ts +2 -1
- package/lib/adobe/firefly.d.ts.map +1 -0
- package/lib/adobe/firefly.js +120 -0
- package/lib/adobe/firefly.js.map +1 -0
- package/lib/anthropic/index.d.ts +23 -0
- package/lib/anthropic/index.d.ts.map +1 -0
- package/lib/anthropic/index.js +68 -0
- package/lib/anthropic/index.js.map +1 -0
- package/lib/{types/azure → azure}/azure_foundry.d.ts +9 -7
- package/lib/azure/azure_foundry.d.ts.map +1 -0
- package/lib/{esm/azure → azure}/azure_foundry.js +138 -124
- package/lib/azure/azure_foundry.js.map +1 -0
- package/lib/{types/bedrock → bedrock}/converse.d.ts +2 -2
- package/lib/bedrock/converse.d.ts.map +1 -0
- package/lib/{esm/bedrock → bedrock}/converse.js +86 -79
- package/lib/bedrock/converse.js.map +1 -0
- package/lib/bedrock/embeddings.d.ts +4 -0
- package/lib/bedrock/embeddings.d.ts.map +1 -0
- package/lib/bedrock/embeddings.js +359 -0
- package/lib/bedrock/embeddings.js.map +1 -0
- package/lib/{types/bedrock → bedrock}/index.d.ts +33 -9
- package/lib/bedrock/index.d.ts.map +1 -0
- package/lib/{esm/bedrock → bedrock}/index.js +599 -324
- package/lib/bedrock/index.js.map +1 -0
- package/lib/{types/bedrock → bedrock}/nova-image-payload.d.ts +5 -5
- package/lib/bedrock/nova-image-payload.d.ts.map +1 -0
- package/lib/{esm/bedrock → bedrock}/nova-image-payload.js +31 -36
- package/lib/bedrock/nova-image-payload.js.map +1 -0
- package/lib/{types/bedrock → bedrock}/payloads.d.ts +1 -1
- package/lib/bedrock/payloads.d.ts.map +1 -0
- package/lib/{cjs/bedrock → bedrock}/payloads.js.map +1 -1
- package/lib/{types/bedrock → bedrock}/s3.d.ts +3 -2
- package/lib/bedrock/s3.d.ts.map +1 -0
- package/lib/{esm/bedrock → bedrock}/s3.js +27 -23
- package/lib/bedrock/s3.js.map +1 -0
- package/lib/{types/bedrock → bedrock}/twelvelabs.d.ts +8 -9
- package/lib/bedrock/twelvelabs.d.ts.map +1 -0
- package/lib/{esm/bedrock → bedrock}/twelvelabs.js +17 -21
- package/lib/bedrock/twelvelabs.js.map +1 -0
- package/lib/{types/groq → groq}/index.d.ts +6 -4
- package/lib/groq/index.d.ts.map +1 -0
- package/lib/{esm/groq → groq}/index.js +72 -39
- package/lib/groq/index.js.map +1 -0
- package/lib/{types/huggingface_ie.d.ts → huggingface_ie.d.ts} +4 -3
- package/lib/huggingface_ie.d.ts.map +1 -0
- package/lib/{esm/huggingface_ie.js → huggingface_ie.js} +32 -31
- package/lib/huggingface_ie.js.map +1 -0
- package/lib/index.d.ts +16 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +16 -0
- package/lib/index.js.map +1 -0
- package/lib/{types/mistral → mistral}/index.d.ts +6 -5
- package/lib/mistral/index.d.ts.map +1 -0
- package/lib/{esm/mistral → mistral}/index.js +66 -38
- package/lib/mistral/index.js.map +1 -0
- package/lib/{types/mistral → mistral}/types.d.ts +1 -0
- package/lib/mistral/types.d.ts.map +1 -0
- package/lib/mistral/types.js.map +1 -0
- package/lib/{types/openai → openai}/azure_openai.d.ts +5 -4
- package/lib/openai/azure_openai.d.ts.map +1 -0
- package/lib/{esm/openai → openai}/azure_openai.js +17 -14
- package/lib/openai/azure_openai.js.map +1 -0
- package/lib/openai/drivers.d.ts +4 -0
- package/lib/openai/drivers.d.ts.map +1 -0
- package/lib/openai/drivers.js +9 -0
- package/lib/openai/drivers.js.map +1 -0
- package/lib/{types/openai → openai}/index.d.ts +17 -5
- package/lib/openai/index.d.ts.map +1 -0
- package/lib/{esm/openai → openai}/index.js +254 -148
- package/lib/openai/index.js.map +1 -0
- package/lib/{types/openai → openai}/openai.d.ts +3 -3
- package/lib/openai/openai.d.ts.map +1 -0
- package/lib/openai/openai.js +15 -0
- package/lib/openai/openai.js.map +1 -0
- package/lib/{types/openai → openai}/openai_compatible.d.ts +3 -3
- package/lib/openai/openai_compatible.d.ts.map +1 -0
- package/lib/{esm/openai → openai}/openai_compatible.js +14 -11
- package/lib/openai/openai_compatible.js.map +1 -0
- package/lib/{types/openai → openai}/openai_format.d.ts +3 -2
- package/lib/openai/openai_format.d.ts.map +1 -0
- package/lib/{esm/openai → openai}/openai_format.js +66 -24
- package/lib/openai/openai_format.js.map +1 -0
- package/lib/{types/replicate.d.ts → replicate.d.ts} +3 -2
- package/lib/replicate.d.ts.map +1 -0
- package/lib/{esm/replicate.js → replicate.js} +59 -49
- package/lib/replicate.js.map +1 -0
- package/lib/shared/claude-messages.d.ts +99 -0
- package/lib/shared/claude-messages.d.ts.map +1 -0
- package/lib/shared/claude-messages.js +869 -0
- package/lib/shared/claude-messages.js.map +1 -0
- package/lib/{types/shared → shared}/claude-thinking.d.ts +1 -1
- package/lib/shared/claude-thinking.d.ts.map +1 -0
- package/lib/{esm/shared → shared}/claude-thinking.js +5 -7
- package/lib/shared/claude-thinking.js.map +1 -0
- package/lib/shared/debug-prompt.d.ts +5 -0
- package/lib/shared/debug-prompt.d.ts.map +1 -0
- package/lib/shared/debug-prompt.js +25 -0
- package/lib/shared/debug-prompt.js.map +1 -0
- package/lib/{types/test-driver → test-driver}/TestErrorCompletionStream.d.ts +1 -1
- package/lib/test-driver/TestErrorCompletionStream.d.ts.map +1 -0
- package/lib/test-driver/TestErrorCompletionStream.js +16 -0
- package/lib/test-driver/TestErrorCompletionStream.js.map +1 -0
- package/lib/{types/test-driver → test-driver}/TestValidationErrorCompletionStream.d.ts +1 -1
- package/lib/test-driver/TestValidationErrorCompletionStream.d.ts.map +1 -0
- package/lib/{esm/test-driver → test-driver}/TestValidationErrorCompletionStream.js +4 -4
- package/lib/test-driver/TestValidationErrorCompletionStream.js.map +1 -0
- package/lib/{types/test-driver → test-driver}/index.d.ts +3 -3
- package/lib/test-driver/index.d.ts.map +1 -0
- package/lib/{esm/test-driver → test-driver}/index.js +20 -20
- package/lib/test-driver/index.js.map +1 -0
- package/lib/{types/test-driver → test-driver}/utils.d.ts +1 -1
- package/lib/test-driver/utils.d.ts.map +1 -0
- package/lib/{esm/test-driver → test-driver}/utils.js +4 -4
- package/lib/test-driver/utils.js.map +1 -0
- package/lib/togetherai/index.d.ts +15 -0
- package/lib/togetherai/index.d.ts.map +1 -0
- package/lib/togetherai/index.js +54 -0
- package/lib/togetherai/index.js.map +1 -0
- package/lib/{types/togetherai → togetherai}/interfaces.d.ts +1 -1
- package/lib/togetherai/interfaces.d.ts.map +1 -0
- package/lib/{cjs/watsonx → togetherai}/interfaces.js.map +1 -1
- package/lib/vertexai/debug.d.ts +2 -0
- package/lib/vertexai/debug.d.ts.map +1 -0
- package/lib/{esm/vertexai → vertexai}/debug.js +1 -1
- package/lib/vertexai/debug.js.map +1 -0
- package/lib/vertexai/embeddings/embed-legacy-multimodal.d.ts +9 -0
- package/lib/vertexai/embeddings/embed-legacy-multimodal.d.ts.map +1 -0
- package/lib/vertexai/embeddings/embed-legacy-multimodal.js +141 -0
- package/lib/vertexai/embeddings/embed-legacy-multimodal.js.map +1 -0
- package/lib/vertexai/embeddings/embed.d.ts +13 -0
- package/lib/vertexai/embeddings/embed.d.ts.map +1 -0
- package/lib/vertexai/embeddings/embed.js +162 -0
- package/lib/vertexai/embeddings/embed.js.map +1 -0
- package/lib/vertexai/embeddings/source-utils.d.ts +9 -0
- package/lib/vertexai/embeddings/source-utils.d.ts.map +1 -0
- package/lib/vertexai/embeddings/source-utils.js +18 -0
- package/lib/vertexai/embeddings/source-utils.js.map +1 -0
- package/lib/{types/vertexai → vertexai}/index.d.ts +31 -19
- package/lib/vertexai/index.d.ts.map +1 -0
- package/lib/{esm/vertexai → vertexai}/index.js +221 -159
- package/lib/vertexai/index.js.map +1 -0
- package/lib/vertexai/models/claude.d.ts +16 -0
- package/lib/vertexai/models/claude.d.ts.map +1 -0
- package/lib/vertexai/models/claude.js +65 -0
- package/lib/vertexai/models/claude.js.map +1 -0
- package/lib/{types/vertexai → vertexai}/models/gemini.d.ts +29 -5
- package/lib/vertexai/models/gemini.d.ts.map +1 -0
- package/lib/{esm/vertexai → vertexai}/models/gemini.js +255 -127
- package/lib/vertexai/models/gemini.js.map +1 -0
- package/lib/{types/vertexai → vertexai}/models/imagen.d.ts +12 -11
- package/lib/vertexai/models/imagen.d.ts.map +1 -0
- package/lib/{esm/vertexai → vertexai}/models/imagen.js +74 -47
- package/lib/vertexai/models/imagen.js.map +1 -0
- package/lib/{types/vertexai → vertexai}/models/llama.d.ts +4 -4
- package/lib/vertexai/models/llama.d.ts.map +1 -0
- package/lib/{esm/vertexai → vertexai}/models/llama.js +37 -33
- package/lib/vertexai/models/llama.js.map +1 -0
- package/lib/vertexai/models.d.ts +21 -0
- package/lib/vertexai/models.d.ts.map +1 -0
- package/lib/{esm/vertexai → vertexai}/models.js +12 -9
- package/lib/vertexai/models.js.map +1 -0
- package/lib/{types/watsonx → watsonx}/index.d.ts +4 -3
- package/lib/watsonx/index.d.ts.map +1 -0
- package/lib/{esm/watsonx → watsonx}/index.js +69 -41
- package/lib/watsonx/index.js.map +1 -0
- package/lib/watsonx/interfaces.d.ts.map +1 -0
- package/lib/{esm/watsonx → watsonx}/interfaces.js.map +1 -1
- package/lib/{types/xai → xai}/index.d.ts +4 -4
- package/lib/xai/index.d.ts.map +1 -0
- package/lib/{esm/xai → xai}/index.js +22 -18
- package/lib/xai/index.js.map +1 -0
- package/package.json +88 -36
- package/src/adobe/firefly.ts +60 -43
- package/src/anthropic/index.ts +121 -0
- package/src/azure/azure_foundry.ts +210 -165
- package/src/bedrock/converse.ts +124 -101
- package/src/bedrock/embeddings.test.ts +508 -0
- package/src/bedrock/embeddings.ts +517 -0
- package/src/bedrock/error-handling.test.ts +62 -14
- package/src/bedrock/index.ts +832 -466
- package/src/bedrock/nova-image-payload.ts +98 -91
- package/src/bedrock/payloads.ts +9 -9
- package/src/bedrock/s3.test.ts +39 -0
- package/src/bedrock/s3.ts +57 -38
- package/src/bedrock/streaming-tool-use.test.ts +86 -69
- package/src/bedrock/twelvelabs.ts +35 -32
- package/src/groq/index.ts +143 -60
- package/src/http-timeout-wiring.test.ts +263 -0
- package/src/huggingface_ie.ts +45 -61
- package/src/index.ts +15 -15
- package/src/mistral/index.ts +97 -48
- package/src/mistral/types.ts +13 -12
- package/src/openai/azure_openai.ts +35 -27
- package/src/openai/drivers.ts +9 -0
- package/src/openai/error-handling.test.ts +308 -113
- package/src/openai/index.ts +389 -254
- package/src/openai/openai.ts +6 -13
- package/src/openai/openai_compatible.ts +35 -25
- package/src/openai/openai_format.ts +90 -40
- package/src/replicate.ts +108 -86
- package/src/shared/claude-messages.test.ts +37 -0
- package/src/shared/claude-messages.ts +1085 -0
- package/src/shared/claude-thinking.ts +6 -12
- package/src/shared/debug-prompt.ts +28 -0
- package/src/test-driver/TestErrorCompletionStream.ts +9 -10
- package/src/test-driver/TestValidationErrorCompletionStream.ts +9 -10
- package/src/test-driver/index.ts +43 -27
- package/src/test-driver/utils.ts +8 -8
- package/src/togetherai/index.test.ts +32 -0
- package/src/togetherai/index.ts +64 -128
- package/src/togetherai/interfaces.ts +5 -5
- package/src/vertexai/debug.ts +3 -3
- package/src/vertexai/embeddings/embed-legacy-multimodal.ts +211 -0
- package/src/vertexai/embeddings/embed.ts +206 -0
- package/src/vertexai/embeddings/embeddings.test.ts +497 -0
- package/src/vertexai/embeddings/source-utils.test.ts +54 -0
- package/src/vertexai/embeddings/source-utils.ts +29 -0
- package/src/vertexai/index.ts +339 -253
- package/src/vertexai/models/claude-error-handling.test.ts +86 -39
- package/src/vertexai/models/claude-streaming-spacing.test.ts +82 -75
- package/src/vertexai/models/claude.ts +84 -1041
- package/src/vertexai/models/gemini-conversation-mutation.test.ts +65 -23
- package/src/vertexai/models/gemini-error-handling.test.ts +126 -10
- package/src/vertexai/models/gemini-orphaned-tool-result.test.ts +118 -0
- package/src/vertexai/models/gemini.ts +372 -182
- package/src/vertexai/models/imagen.ts +164 -100
- package/src/vertexai/models/llama.ts +70 -48
- package/src/vertexai/models.ts +36 -16
- package/src/watsonx/index.ts +101 -65
- package/src/watsonx/interfaces.ts +10 -15
- package/src/xai/index.ts +30 -30
- package/lib/cjs/adobe/firefly.js +0 -120
- package/lib/cjs/adobe/firefly.js.map +0 -1
- package/lib/cjs/azure/azure_foundry.js +0 -432
- package/lib/cjs/azure/azure_foundry.js.map +0 -1
- package/lib/cjs/bedrock/converse.js +0 -359
- package/lib/cjs/bedrock/converse.js.map +0 -1
- package/lib/cjs/bedrock/index.js +0 -1508
- package/lib/cjs/bedrock/index.js.map +0 -1
- package/lib/cjs/bedrock/nova-image-payload.js +0 -207
- package/lib/cjs/bedrock/nova-image-payload.js.map +0 -1
- package/lib/cjs/bedrock/payloads.js +0 -3
- package/lib/cjs/bedrock/s3.js +0 -107
- package/lib/cjs/bedrock/s3.js.map +0 -1
- package/lib/cjs/bedrock/twelvelabs.js +0 -87
- package/lib/cjs/bedrock/twelvelabs.js.map +0 -1
- package/lib/cjs/groq/index.js +0 -328
- package/lib/cjs/groq/index.js.map +0 -1
- package/lib/cjs/huggingface_ie.js +0 -201
- package/lib/cjs/huggingface_ie.js.map +0 -1
- package/lib/cjs/index.js +0 -31
- package/lib/cjs/index.js.map +0 -1
- package/lib/cjs/mistral/index.js +0 -176
- package/lib/cjs/mistral/index.js.map +0 -1
- package/lib/cjs/mistral/types.js +0 -83
- package/lib/cjs/mistral/types.js.map +0 -1
- package/lib/cjs/openai/azure_openai.js +0 -72
- package/lib/cjs/openai/azure_openai.js.map +0 -1
- package/lib/cjs/openai/index.js +0 -1129
- package/lib/cjs/openai/index.js.map +0 -1
- package/lib/cjs/openai/openai.js +0 -21
- package/lib/cjs/openai/openai.js.map +0 -1
- package/lib/cjs/openai/openai_compatible.js +0 -63
- package/lib/cjs/openai/openai_compatible.js.map +0 -1
- package/lib/cjs/openai/openai_format.js +0 -131
- package/lib/cjs/openai/openai_format.js.map +0 -1
- package/lib/cjs/package.json +0 -3
- package/lib/cjs/replicate.js +0 -275
- package/lib/cjs/replicate.js.map +0 -1
- package/lib/cjs/shared/claude-thinking.js +0 -60
- package/lib/cjs/shared/claude-thinking.js.map +0 -1
- package/lib/cjs/test-driver/TestErrorCompletionStream.js +0 -20
- package/lib/cjs/test-driver/TestErrorCompletionStream.js.map +0 -1
- package/lib/cjs/test-driver/TestValidationErrorCompletionStream.js +0 -24
- package/lib/cjs/test-driver/TestValidationErrorCompletionStream.js.map +0 -1
- package/lib/cjs/test-driver/index.js +0 -109
- package/lib/cjs/test-driver/index.js.map +0 -1
- package/lib/cjs/test-driver/utils.js +0 -30
- package/lib/cjs/test-driver/utils.js.map +0 -1
- package/lib/cjs/togetherai/index.js +0 -126
- package/lib/cjs/togetherai/index.js.map +0 -1
- package/lib/cjs/togetherai/interfaces.js +0 -3
- package/lib/cjs/togetherai/interfaces.js.map +0 -1
- package/lib/cjs/vertexai/debug.js +0 -12
- package/lib/cjs/vertexai/debug.js.map +0 -1
- package/lib/cjs/vertexai/embeddings/embeddings-image.js +0 -27
- package/lib/cjs/vertexai/embeddings/embeddings-image.js.map +0 -1
- package/lib/cjs/vertexai/embeddings/embeddings-text.js +0 -23
- package/lib/cjs/vertexai/embeddings/embeddings-text.js.map +0 -1
- package/lib/cjs/vertexai/index.js +0 -644
- package/lib/cjs/vertexai/index.js.map +0 -1
- package/lib/cjs/vertexai/models/claude.js +0 -917
- package/lib/cjs/vertexai/models/claude.js.map +0 -1
- package/lib/cjs/vertexai/models/gemini.js +0 -858
- package/lib/cjs/vertexai/models/gemini.js.map +0 -1
- package/lib/cjs/vertexai/models/imagen.js +0 -303
- package/lib/cjs/vertexai/models/imagen.js.map +0 -1
- package/lib/cjs/vertexai/models/llama.js +0 -183
- package/lib/cjs/vertexai/models/llama.js.map +0 -1
- package/lib/cjs/vertexai/models.js +0 -35
- package/lib/cjs/vertexai/models.js.map +0 -1
- package/lib/cjs/watsonx/index.js +0 -161
- package/lib/cjs/watsonx/index.js.map +0 -1
- package/lib/cjs/watsonx/interfaces.js +0 -3
- package/lib/cjs/xai/index.js +0 -65
- package/lib/cjs/xai/index.js.map +0 -1
- package/lib/esm/adobe/firefly.js +0 -116
- package/lib/esm/adobe/firefly.js.map +0 -1
- package/lib/esm/azure/azure_foundry.js.map +0 -1
- package/lib/esm/bedrock/converse.js.map +0 -1
- package/lib/esm/bedrock/index.js.map +0 -1
- package/lib/esm/bedrock/nova-image-payload.js.map +0 -1
- package/lib/esm/bedrock/payloads.js.map +0 -1
- package/lib/esm/bedrock/s3.js.map +0 -1
- package/lib/esm/bedrock/twelvelabs.js.map +0 -1
- package/lib/esm/groq/index.js.map +0 -1
- package/lib/esm/huggingface_ie.js.map +0 -1
- package/lib/esm/index.js +0 -15
- package/lib/esm/index.js.map +0 -1
- package/lib/esm/mistral/index.js.map +0 -1
- package/lib/esm/mistral/types.js.map +0 -1
- package/lib/esm/openai/azure_openai.js.map +0 -1
- package/lib/esm/openai/index.js.map +0 -1
- package/lib/esm/openai/openai.js +0 -14
- package/lib/esm/openai/openai.js.map +0 -1
- package/lib/esm/openai/openai_compatible.js.map +0 -1
- package/lib/esm/openai/openai_format.js.map +0 -1
- package/lib/esm/replicate.js.map +0 -1
- package/lib/esm/shared/claude-thinking.js.map +0 -1
- package/lib/esm/test-driver/TestErrorCompletionStream.js +0 -16
- package/lib/esm/test-driver/TestErrorCompletionStream.js.map +0 -1
- package/lib/esm/test-driver/TestValidationErrorCompletionStream.js.map +0 -1
- package/lib/esm/test-driver/index.js.map +0 -1
- package/lib/esm/test-driver/utils.js.map +0 -1
- package/lib/esm/togetherai/index.js +0 -122
- package/lib/esm/togetherai/index.js.map +0 -1
- package/lib/esm/togetherai/interfaces.js.map +0 -1
- package/lib/esm/vertexai/debug.js.map +0 -1
- package/lib/esm/vertexai/embeddings/embeddings-image.js +0 -24
- package/lib/esm/vertexai/embeddings/embeddings-image.js.map +0 -1
- package/lib/esm/vertexai/embeddings/embeddings-text.js +0 -20
- package/lib/esm/vertexai/embeddings/embeddings-text.js.map +0 -1
- package/lib/esm/vertexai/index.js.map +0 -1
- package/lib/esm/vertexai/models/claude.js +0 -906
- package/lib/esm/vertexai/models/claude.js.map +0 -1
- package/lib/esm/vertexai/models/gemini.js.map +0 -1
- package/lib/esm/vertexai/models/imagen.js.map +0 -1
- package/lib/esm/vertexai/models/llama.js.map +0 -1
- package/lib/esm/vertexai/models.js.map +0 -1
- package/lib/esm/watsonx/index.js.map +0 -1
- package/lib/esm/xai/index.js.map +0 -1
- package/lib/types/adobe/firefly.d.ts.map +0 -1
- package/lib/types/azure/azure_foundry.d.ts.map +0 -1
- package/lib/types/bedrock/converse.d.ts.map +0 -1
- package/lib/types/bedrock/index.d.ts.map +0 -1
- package/lib/types/bedrock/nova-image-payload.d.ts.map +0 -1
- package/lib/types/bedrock/payloads.d.ts.map +0 -1
- package/lib/types/bedrock/s3.d.ts.map +0 -1
- package/lib/types/bedrock/twelvelabs.d.ts.map +0 -1
- package/lib/types/groq/index.d.ts.map +0 -1
- package/lib/types/huggingface_ie.d.ts.map +0 -1
- package/lib/types/index.d.ts +0 -15
- package/lib/types/index.d.ts.map +0 -1
- package/lib/types/mistral/index.d.ts.map +0 -1
- package/lib/types/mistral/types.d.ts.map +0 -1
- package/lib/types/openai/azure_openai.d.ts.map +0 -1
- package/lib/types/openai/index.d.ts.map +0 -1
- package/lib/types/openai/openai.d.ts.map +0 -1
- package/lib/types/openai/openai_compatible.d.ts.map +0 -1
- package/lib/types/openai/openai_format.d.ts.map +0 -1
- package/lib/types/replicate.d.ts.map +0 -1
- package/lib/types/shared/claude-thinking.d.ts.map +0 -1
- package/lib/types/test-driver/TestErrorCompletionStream.d.ts.map +0 -1
- package/lib/types/test-driver/TestValidationErrorCompletionStream.d.ts.map +0 -1
- package/lib/types/test-driver/index.d.ts.map +0 -1
- package/lib/types/test-driver/utils.d.ts.map +0 -1
- package/lib/types/togetherai/index.d.ts +0 -23
- package/lib/types/togetherai/index.d.ts.map +0 -1
- package/lib/types/togetherai/interfaces.d.ts.map +0 -1
- package/lib/types/vertexai/debug.d.ts +0 -2
- package/lib/types/vertexai/debug.d.ts.map +0 -1
- package/lib/types/vertexai/embeddings/embeddings-image.d.ts +0 -11
- package/lib/types/vertexai/embeddings/embeddings-image.d.ts.map +0 -1
- package/lib/types/vertexai/embeddings/embeddings-text.d.ts +0 -10
- package/lib/types/vertexai/embeddings/embeddings-text.d.ts.map +0 -1
- package/lib/types/vertexai/index.d.ts.map +0 -1
- package/lib/types/vertexai/models/claude.d.ts +0 -119
- package/lib/types/vertexai/models/claude.d.ts.map +0 -1
- package/lib/types/vertexai/models/gemini.d.ts.map +0 -1
- package/lib/types/vertexai/models/imagen.d.ts.map +0 -1
- package/lib/types/vertexai/models/llama.d.ts.map +0 -1
- package/lib/types/vertexai/models.d.ts +0 -20
- package/lib/types/vertexai/models.d.ts.map +0 -1
- package/lib/types/watsonx/index.d.ts.map +0 -1
- package/lib/types/watsonx/interfaces.d.ts.map +0 -1
- package/lib/types/xai/index.d.ts.map +0 -1
- package/src/vertexai/embeddings/embeddings-image.ts +0 -52
- package/src/vertexai/embeddings/embeddings-text.ts +0 -54
- /package/lib/{esm/bedrock → bedrock}/payloads.js +0 -0
- /package/lib/{esm/mistral → mistral}/types.js +0 -0
- /package/lib/{esm/togetherai → togetherai}/interfaces.js +0 -0
- /package/lib/{types/watsonx → watsonx}/interfaces.d.ts +0 -0
- /package/lib/{esm/watsonx → watsonx}/interfaces.js +0 -0
|
@@ -1,95 +1,150 @@
|
|
|
1
|
-
import type { ApiError } from
|
|
1
|
+
import type { ApiError } from '@google/genai';
|
|
2
2
|
import {
|
|
3
|
-
type Content,
|
|
3
|
+
type Content,
|
|
4
|
+
FinishReason,
|
|
5
|
+
FunctionCallingConfigMode,
|
|
6
|
+
type FunctionDeclaration,
|
|
7
|
+
type GenerateContentConfig,
|
|
8
|
+
type GenerateContentParameters,
|
|
4
9
|
type GenerateContentResponseUsageMetadata,
|
|
5
|
-
HarmBlockThreshold,
|
|
10
|
+
HarmBlockThreshold,
|
|
11
|
+
HarmCategory,
|
|
12
|
+
Modality,
|
|
13
|
+
type Part,
|
|
6
14
|
ProminentPeople,
|
|
7
|
-
type SafetySetting,
|
|
15
|
+
type SafetySetting,
|
|
16
|
+
type ThinkingConfig,
|
|
8
17
|
ThinkingLevel,
|
|
9
|
-
type Tool
|
|
10
|
-
} from
|
|
18
|
+
type Tool,
|
|
19
|
+
} from '@google/genai';
|
|
11
20
|
import {
|
|
12
|
-
type AIModel,
|
|
21
|
+
type AIModel,
|
|
22
|
+
type Completion,
|
|
23
|
+
type CompletionChunkObject,
|
|
24
|
+
type CompletionResult,
|
|
25
|
+
type ExecutionOptions,
|
|
13
26
|
type ExecutionTokenUsage,
|
|
14
27
|
getConversationMeta,
|
|
15
28
|
getGeminiModelVersion,
|
|
16
29
|
incrementConversationTurn,
|
|
17
30
|
isGeminiModelVersionGte,
|
|
18
|
-
type JSONObject,
|
|
19
|
-
|
|
31
|
+
type JSONObject,
|
|
32
|
+
LlumiverseError,
|
|
33
|
+
type LlumiverseErrorContext,
|
|
34
|
+
ModelType,
|
|
35
|
+
type PromptOptions,
|
|
36
|
+
PromptRole,
|
|
37
|
+
type PromptSegment,
|
|
38
|
+
readStreamAsBase64,
|
|
39
|
+
type StatelessExecutionOptions,
|
|
20
40
|
stripBase64ImagesFromConversation,
|
|
21
41
|
stripHeartbeatsFromConversation,
|
|
22
|
-
type ToolDefinition,
|
|
42
|
+
type ToolDefinition,
|
|
43
|
+
type ToolUse,
|
|
23
44
|
truncateLargeTextInConversation,
|
|
24
45
|
unwrapConversationArray,
|
|
25
|
-
type VertexAIGeminiOptions
|
|
26
|
-
} from
|
|
27
|
-
import { asyncMap } from
|
|
28
|
-
import
|
|
29
|
-
import type {
|
|
46
|
+
type VertexAIGeminiOptions,
|
|
47
|
+
} from '@llumiverse/core';
|
|
48
|
+
import { asyncMap } from '@llumiverse/core/async';
|
|
49
|
+
import { truncateBinaryForDebug } from '../../shared/debug-prompt.js';
|
|
50
|
+
import type { GenerateContentPrompt, VertexAIDriver } from '../index.js';
|
|
51
|
+
import type { ModelDefinition } from '../models.js';
|
|
52
|
+
|
|
53
|
+
type GoogleApiErrorLike = Pick<ApiError, 'status' | 'message'>;
|
|
30
54
|
|
|
31
55
|
function supportsStructuredOutput(options: PromptOptions): boolean {
|
|
32
56
|
// Gemini 1.0 Ultra does not support JSON output, 1.0 Pro does.
|
|
33
|
-
return !!options.result_schema && !options.model.includes(
|
|
57
|
+
return !!options.result_schema && !options.model.includes('ultra');
|
|
34
58
|
}
|
|
35
59
|
|
|
36
60
|
const geminiSafetySettings: SafetySetting[] = [
|
|
37
61
|
{
|
|
38
62
|
category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
|
|
39
|
-
threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH
|
|
63
|
+
threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH,
|
|
40
64
|
},
|
|
41
65
|
{
|
|
42
66
|
category: HarmCategory.HARM_CATEGORY_HARASSMENT,
|
|
43
|
-
threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH
|
|
67
|
+
threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH,
|
|
44
68
|
},
|
|
45
69
|
{
|
|
46
70
|
category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT,
|
|
47
|
-
threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH
|
|
71
|
+
threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH,
|
|
48
72
|
},
|
|
49
73
|
{
|
|
50
74
|
category: HarmCategory.HARM_CATEGORY_HATE_SPEECH,
|
|
51
|
-
threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH
|
|
75
|
+
threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH,
|
|
52
76
|
},
|
|
53
77
|
{
|
|
54
78
|
category: HarmCategory.HARM_CATEGORY_UNSPECIFIED,
|
|
55
|
-
threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH
|
|
79
|
+
threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH,
|
|
56
80
|
},
|
|
57
81
|
{
|
|
58
82
|
category: HarmCategory.HARM_CATEGORY_CIVIC_INTEGRITY,
|
|
59
|
-
threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH
|
|
60
|
-
}
|
|
83
|
+
threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH,
|
|
84
|
+
},
|
|
61
85
|
];
|
|
62
86
|
|
|
87
|
+
function formatGeminiContentForDebug(content: Content): Content {
|
|
88
|
+
return {
|
|
89
|
+
...content,
|
|
90
|
+
parts: content.parts?.map((part) => {
|
|
91
|
+
if (!part.inlineData?.data) {
|
|
92
|
+
return part;
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
...part,
|
|
96
|
+
inlineData: {
|
|
97
|
+
...part.inlineData,
|
|
98
|
+
data: truncateBinaryForDebug(part.inlineData.data),
|
|
99
|
+
},
|
|
100
|
+
} satisfies Part;
|
|
101
|
+
}),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function formatGeminiDebugPrompt(prompt: GenerateContentPrompt): GenerateContentPrompt {
|
|
106
|
+
return {
|
|
107
|
+
...prompt,
|
|
108
|
+
contents: prompt.contents.map(formatGeminiContentForDebug),
|
|
109
|
+
system: prompt.system ? formatGeminiContentForDebug(prompt.system) : undefined,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
63
113
|
// We do the mapping here rather than in common to avoid bringing the SDK into the common package.
|
|
64
|
-
function getProminentPeopleOption(
|
|
114
|
+
function getProminentPeopleOption(
|
|
115
|
+
prominentPeople?: 'PROMINENT_PEOPLE_UNSPECIFIED' | 'ALLOW_PROMINENT_PEOPLE' | 'BLOCK_PROMINENT_PEOPLE',
|
|
116
|
+
) {
|
|
65
117
|
switch (prominentPeople) {
|
|
66
|
-
case
|
|
118
|
+
case 'ALLOW_PROMINENT_PEOPLE':
|
|
67
119
|
return ProminentPeople.ALLOW_PROMINENT_PEOPLE;
|
|
68
|
-
case
|
|
120
|
+
case 'BLOCK_PROMINENT_PEOPLE':
|
|
69
121
|
return ProminentPeople.BLOCK_PROMINENT_PEOPLE;
|
|
70
|
-
case
|
|
122
|
+
case 'PROMINENT_PEOPLE_UNSPECIFIED':
|
|
71
123
|
return ProminentPeople.PROMINENT_PEOPLE_UNSPECIFIED;
|
|
72
124
|
default:
|
|
73
125
|
return undefined;
|
|
74
126
|
}
|
|
75
127
|
}
|
|
76
128
|
|
|
77
|
-
function getGeminiPayload(options: ExecutionOptions, prompt: GenerateContentPrompt): GenerateContentParameters {
|
|
129
|
+
export function getGeminiPayload(options: ExecutionOptions, prompt: GenerateContentPrompt): GenerateContentParameters {
|
|
78
130
|
const model_options = options.model_options as VertexAIGeminiOptions | undefined;
|
|
79
131
|
const tools = getToolDefinitions(options.tools);
|
|
80
132
|
|
|
81
133
|
// When no tools are provided but conversation contains functionCall/functionResponse parts
|
|
82
134
|
// (e.g. checkpoint summary calls), convert them to text to avoid API errors.
|
|
83
135
|
// Use a local variable to avoid mutating the caller's conversation object.
|
|
84
|
-
let payloadContents = prompt.contents;
|
|
136
|
+
let payloadContents = mergeConsecutiveRole(prompt.contents);
|
|
85
137
|
if (!tools && payloadContents) {
|
|
86
|
-
const hasToolParts = payloadContents.some(c =>
|
|
87
|
-
c.parts?.some(p => p.functionCall || p.functionResponse)
|
|
88
|
-
);
|
|
138
|
+
const hasToolParts = payloadContents.some((c) => c.parts?.some((p) => p.functionCall || p.functionResponse));
|
|
89
139
|
if (hasToolParts) {
|
|
90
140
|
payloadContents = convertGeminiFunctionPartsToText(payloadContents);
|
|
91
141
|
}
|
|
92
142
|
}
|
|
143
|
+
// Drop functionResponse parts whose functionCall was lost (e.g. compaction),
|
|
144
|
+
// which would otherwise trip Gemini's functionCall/functionResponse pairing.
|
|
145
|
+
if (payloadContents) {
|
|
146
|
+
payloadContents = fixOrphanedToolResults(payloadContents);
|
|
147
|
+
}
|
|
93
148
|
|
|
94
149
|
const useStructuredOutput = supportsStructuredOutput(options) && !tools;
|
|
95
150
|
|
|
@@ -112,21 +167,23 @@ function getGeminiPayload(options: ExecutionOptions, prompt: GenerateContentProm
|
|
|
112
167
|
prominentPeople: getProminentPeopleOption(model_options?.prominent_people),
|
|
113
168
|
outputMimeType: model_options?.output_mime_type,
|
|
114
169
|
outputCompressionQuality: model_options?.output_compression_quality,
|
|
115
|
-
}
|
|
116
|
-
}
|
|
170
|
+
},
|
|
171
|
+
};
|
|
117
172
|
|
|
118
173
|
const config: GenerateContentConfig = {
|
|
119
174
|
systemInstruction: prompt.system,
|
|
120
175
|
safetySettings: geminiSafetySettings,
|
|
121
176
|
tools: tools ? [tools] : undefined,
|
|
122
|
-
toolConfig: tools
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
177
|
+
toolConfig: tools
|
|
178
|
+
? {
|
|
179
|
+
functionCallingConfig: {
|
|
180
|
+
mode: FunctionCallingConfigMode.AUTO,
|
|
181
|
+
},
|
|
182
|
+
}
|
|
183
|
+
: undefined,
|
|
127
184
|
candidateCount: 1,
|
|
128
185
|
//JSON/Structured output
|
|
129
|
-
responseMimeType: useStructuredOutput ?
|
|
186
|
+
responseMimeType: useStructuredOutput ? 'application/json' : undefined,
|
|
130
187
|
responseJsonSchema: useStructuredOutput ? options.result_schema : undefined,
|
|
131
188
|
//Model options
|
|
132
189
|
temperature: model_options?.temperature,
|
|
@@ -139,12 +196,12 @@ function getGeminiPayload(options: ExecutionOptions, prompt: GenerateContentProm
|
|
|
139
196
|
seed: model_options?.seed,
|
|
140
197
|
thinkingConfig: geminiThinkingConfig(options),
|
|
141
198
|
labels: options.labels,
|
|
142
|
-
}
|
|
199
|
+
};
|
|
143
200
|
|
|
144
201
|
return {
|
|
145
202
|
model: options.model,
|
|
146
203
|
contents: payloadContents,
|
|
147
|
-
config: options.model.toLowerCase().includes(
|
|
204
|
+
config: options.model.toLowerCase().includes('image') ? configNanoBanana : config,
|
|
148
205
|
};
|
|
149
206
|
}
|
|
150
207
|
|
|
@@ -159,16 +216,16 @@ function extractCompletionResults(content: Content): CompletionResult[] {
|
|
|
159
216
|
for (const part of parts) {
|
|
160
217
|
if (part.text) {
|
|
161
218
|
results.push({
|
|
162
|
-
type:
|
|
163
|
-
value: part.text
|
|
219
|
+
type: 'text',
|
|
220
|
+
value: part.text,
|
|
164
221
|
});
|
|
165
222
|
} else if (part.inlineData) {
|
|
166
|
-
const base64ImageBytes: string = part.inlineData.data ??
|
|
167
|
-
const mimeType = part.inlineData.mimeType ??
|
|
223
|
+
const base64ImageBytes: string = part.inlineData.data ?? '';
|
|
224
|
+
const mimeType = part.inlineData.mimeType ?? 'image/png';
|
|
168
225
|
const imageUrl = `data:${mimeType};base64,${base64ImageBytes}`;
|
|
169
226
|
results.push({
|
|
170
|
-
type:
|
|
171
|
-
value: imageUrl
|
|
227
|
+
type: 'image',
|
|
228
|
+
value: imageUrl,
|
|
172
229
|
});
|
|
173
230
|
}
|
|
174
231
|
}
|
|
@@ -200,8 +257,8 @@ function collectToolUseParts(content: Content): ToolUse[] | undefined {
|
|
|
200
257
|
export function mergeConsecutiveRole(contents: Content[] | undefined): Content[] {
|
|
201
258
|
if (!contents || contents.length === 0) return [];
|
|
202
259
|
|
|
203
|
-
const needsMerging = contents.some(
|
|
204
|
-
i < contents.length - 1 && content.role === contents[i + 1].role
|
|
260
|
+
const needsMerging = contents.some(
|
|
261
|
+
(content, i) => i < contents.length - 1 && content.role === contents[i + 1].role,
|
|
205
262
|
);
|
|
206
263
|
// If no merging needed, return original array
|
|
207
264
|
if (!needsMerging) {
|
|
@@ -226,19 +283,61 @@ export function mergeConsecutiveRole(contents: Content[] | undefined): Content[]
|
|
|
226
283
|
return result;
|
|
227
284
|
}
|
|
228
285
|
|
|
286
|
+
/**
|
|
287
|
+
* Drop functionResponse parts whose name has no matching functionCall in the
|
|
288
|
+
* immediately-preceding `model` content. Gemini pairs a functionResponse to its
|
|
289
|
+
* functionCall by name; a response left dangling after its call was dropped
|
|
290
|
+
* (e.g. by conversation compaction/trimming, or an unmergeable parallel batch)
|
|
291
|
+
* causes the API to reject the request. Mirrors the same guard added to the
|
|
292
|
+
* Claude, Bedrock, and OpenAI drivers.
|
|
293
|
+
*
|
|
294
|
+
* Run after mergeConsecutiveRole so parallel responses that were split across
|
|
295
|
+
* user contents are first recombined under the model turn that issued the calls,
|
|
296
|
+
* and are therefore not mistaken for orphans.
|
|
297
|
+
*/
|
|
298
|
+
export function fixOrphanedToolResults(contents: Content[]): Content[] {
|
|
299
|
+
if (contents.length === 0) return contents;
|
|
300
|
+
const result: Content[] = [];
|
|
301
|
+
for (let i = 0; i < contents.length; i++) {
|
|
302
|
+
const content = contents[i];
|
|
303
|
+
if (content.role !== 'user' || !content.parts) {
|
|
304
|
+
result.push(content);
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
const hasFunctionResponse = content.parts.some((part) => part.functionResponse);
|
|
308
|
+
if (!hasFunctionResponse) {
|
|
309
|
+
result.push(content);
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
const prev = contents[i - 1];
|
|
313
|
+
const allowedNames = new Set<string>();
|
|
314
|
+
if (prev && prev.role === 'model' && prev.parts) {
|
|
315
|
+
for (const part of prev.parts) {
|
|
316
|
+
if (part.functionCall?.name) allowedNames.add(part.functionCall.name);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
const filtered = content.parts.filter((part) =>
|
|
320
|
+
part.functionResponse ? allowedNames.has(part.functionResponse.name ?? '') : true,
|
|
321
|
+
);
|
|
322
|
+
// Drop the content if every part was an orphaned functionResponse.
|
|
323
|
+
if (filtered.length === 0) continue;
|
|
324
|
+
result.push(filtered.length === content.parts.length ? content : { ...content, parts: filtered });
|
|
325
|
+
}
|
|
326
|
+
return result;
|
|
327
|
+
}
|
|
328
|
+
|
|
229
329
|
const supportedFinishReasons: FinishReason[] = [
|
|
230
330
|
FinishReason.MAX_TOKENS,
|
|
231
331
|
FinishReason.STOP,
|
|
232
332
|
FinishReason.FINISH_REASON_UNSPECIFIED,
|
|
233
|
-
]
|
|
333
|
+
];
|
|
234
334
|
|
|
235
335
|
// Finish reasons that indicate tool call issues but should be recovered gracefully
|
|
236
336
|
// instead of throwing an error. The tool_use is still extracted and returned
|
|
237
337
|
// so the workflow can generate a proper toolError response.
|
|
238
338
|
const recoverableToolCallReasons = [
|
|
239
339
|
'UNEXPECTED_TOOL_CALL', // Model called an undeclared tool
|
|
240
|
-
]
|
|
241
|
-
|
|
340
|
+
];
|
|
242
341
|
|
|
243
342
|
function geminiThinkingBudget(option: StatelessExecutionOptions) {
|
|
244
343
|
const model_options = option.model_options as VertexAIGeminiOptions | undefined;
|
|
@@ -252,7 +351,7 @@ function geminiThinkingBudget(option: StatelessExecutionOptions) {
|
|
|
252
351
|
// Set minimum thinking level by default.
|
|
253
352
|
// Docs: https://ai.google.dev/gemini-api/docs/thinking#set-budget
|
|
254
353
|
if (getGeminiModelVersion(option.model) === '2.5') {
|
|
255
|
-
if (option.model.includes(
|
|
354
|
+
if (option.model.includes('pro')) {
|
|
256
355
|
return 128;
|
|
257
356
|
}
|
|
258
357
|
return 0;
|
|
@@ -260,37 +359,40 @@ function geminiThinkingBudget(option: StatelessExecutionOptions) {
|
|
|
260
359
|
return undefined;
|
|
261
360
|
}
|
|
262
361
|
|
|
263
|
-
function geminiThinkingLevelForEffort(
|
|
264
|
-
|
|
362
|
+
function geminiThinkingLevelForEffort(
|
|
363
|
+
model: string,
|
|
364
|
+
effort: VertexAIGeminiOptions['effort'],
|
|
365
|
+
): ThinkingLevel | undefined {
|
|
366
|
+
if (model.includes('gemini-3-pro-image')) {
|
|
265
367
|
return ThinkingLevel.HIGH;
|
|
266
368
|
}
|
|
267
|
-
if (model.includes(
|
|
268
|
-
return effort ===
|
|
369
|
+
if (model.includes('gemini-3.1-flash-image')) {
|
|
370
|
+
return effort === 'low' ? ThinkingLevel.MINIMAL : ThinkingLevel.HIGH;
|
|
269
371
|
}
|
|
270
372
|
switch (effort) {
|
|
271
|
-
case
|
|
373
|
+
case 'low':
|
|
272
374
|
return ThinkingLevel.LOW;
|
|
273
|
-
case
|
|
375
|
+
case 'medium':
|
|
274
376
|
return ThinkingLevel.MEDIUM;
|
|
275
|
-
case
|
|
377
|
+
case 'high':
|
|
276
378
|
return ThinkingLevel.HIGH;
|
|
277
379
|
default:
|
|
278
380
|
return undefined;
|
|
279
381
|
}
|
|
280
382
|
}
|
|
281
383
|
|
|
282
|
-
function geminiBudgetForEffort(model: string, effort: NonNullable<VertexAIGeminiOptions[
|
|
283
|
-
const isFlashLite = model.includes(
|
|
284
|
-
const isFlash = model.includes(
|
|
285
|
-
const isPro = model.includes(
|
|
384
|
+
function geminiBudgetForEffort(model: string, effort: NonNullable<VertexAIGeminiOptions['effort']>): number {
|
|
385
|
+
const isFlashLite = model.includes('flash-lite');
|
|
386
|
+
const isFlash = model.includes('flash') && !isFlashLite;
|
|
387
|
+
const isPro = model.includes('pro');
|
|
286
388
|
|
|
287
|
-
if (effort ===
|
|
389
|
+
if (effort === 'low') {
|
|
288
390
|
if (isPro) return 128;
|
|
289
391
|
if (isFlashLite) return 512;
|
|
290
392
|
if (isFlash) return 1;
|
|
291
393
|
return 1024;
|
|
292
394
|
}
|
|
293
|
-
if (effort ===
|
|
395
|
+
if (effort === 'medium') {
|
|
294
396
|
return 8192;
|
|
295
397
|
}
|
|
296
398
|
if (isPro) return 32768;
|
|
@@ -329,21 +431,20 @@ function geminiThinkingConfig(option: StatelessExecutionOptions): ThinkingConfig
|
|
|
329
431
|
if (isGeminiModelVersionGte(option.model, '3.0')) {
|
|
330
432
|
return {
|
|
331
433
|
includeThoughts: include_thoughts,
|
|
332
|
-
thinkingLevel: option.model.includes(
|
|
434
|
+
thinkingLevel: option.model.includes('gemini-3-pro-image') ? ThinkingLevel.HIGH : ThinkingLevel.LOW,
|
|
333
435
|
};
|
|
334
436
|
}
|
|
335
437
|
if (isGeminiModelVersionGte(option.model, '2.5')) {
|
|
336
438
|
const thinking_budget_tokens = geminiThinkingBudget(option) ?? 0;
|
|
337
439
|
return {
|
|
338
440
|
includeThoughts: include_thoughts,
|
|
339
|
-
thinkingBudget: thinking_budget_tokens
|
|
441
|
+
thinkingBudget: thinking_budget_tokens,
|
|
340
442
|
};
|
|
341
443
|
}
|
|
342
444
|
}
|
|
343
445
|
|
|
344
446
|
export class GeminiModelDefinition implements ModelDefinition<GenerateContentPrompt> {
|
|
345
|
-
|
|
346
|
-
model: AIModel
|
|
447
|
+
model: AIModel;
|
|
347
448
|
|
|
348
449
|
constructor(modelId: string) {
|
|
349
450
|
this.model = {
|
|
@@ -351,18 +452,22 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
351
452
|
name: modelId,
|
|
352
453
|
provider: 'vertexai',
|
|
353
454
|
type: ModelType.Text,
|
|
354
|
-
can_stream: true
|
|
455
|
+
can_stream: true,
|
|
355
456
|
} satisfies AIModel;
|
|
356
457
|
}
|
|
357
458
|
|
|
358
|
-
async createPrompt(
|
|
359
|
-
|
|
459
|
+
async createPrompt(
|
|
460
|
+
_driver: VertexAIDriver,
|
|
461
|
+
segments: PromptSegment[],
|
|
462
|
+
options: ExecutionOptions,
|
|
463
|
+
): Promise<GenerateContentPrompt> {
|
|
464
|
+
const splits = options.model.split('/');
|
|
360
465
|
const modelName = splits[splits.length - 1];
|
|
361
466
|
options = { ...options, model: modelName };
|
|
362
467
|
|
|
363
468
|
const schema = options.result_schema;
|
|
364
469
|
let contents: Content[] = [];
|
|
365
|
-
let system: Content | undefined = { role:
|
|
470
|
+
let system: Content | undefined = { role: 'user', parts: [] }; // Single content block for system messages
|
|
366
471
|
|
|
367
472
|
const safety: Content[] = [];
|
|
368
473
|
|
|
@@ -371,17 +476,19 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
371
476
|
if (msg.role === PromptRole.system) {
|
|
372
477
|
// Text only for system messages
|
|
373
478
|
if (msg.files && msg.files.length > 0) {
|
|
374
|
-
throw new Error(
|
|
479
|
+
throw new Error(
|
|
480
|
+
'Gemini does not support files/images etc. in system messages. Only text content is allowed.',
|
|
481
|
+
);
|
|
375
482
|
}
|
|
376
483
|
|
|
377
484
|
if (msg.content) {
|
|
378
485
|
system.parts?.push({
|
|
379
|
-
text: msg.content
|
|
486
|
+
text: msg.content,
|
|
380
487
|
});
|
|
381
488
|
}
|
|
382
489
|
} else if (msg.role === PromptRole.tool) {
|
|
383
490
|
if (!msg.tool_use_id) {
|
|
384
|
-
throw new Error(
|
|
491
|
+
throw new Error('Tool response missing tool_use_id');
|
|
385
492
|
}
|
|
386
493
|
// Build functionResponse part with optional thought_signature for Gemini thinking models
|
|
387
494
|
const functionResponsePart: Part = {
|
|
@@ -394,9 +501,10 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
394
501
|
};
|
|
395
502
|
contents.push({
|
|
396
503
|
role: 'user',
|
|
397
|
-
parts: [functionResponsePart]
|
|
504
|
+
parts: [functionResponsePart],
|
|
398
505
|
});
|
|
399
|
-
} else {
|
|
506
|
+
} else {
|
|
507
|
+
// PromptRole.user, PromptRole.assistant, PromptRole.safety
|
|
400
508
|
const parts: Part[] = [];
|
|
401
509
|
// Text content handling
|
|
402
510
|
if (msg.content) {
|
|
@@ -408,15 +516,16 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
408
516
|
// File content handling
|
|
409
517
|
if (msg.files) {
|
|
410
518
|
for (const f of msg.files) {
|
|
411
|
-
const
|
|
412
|
-
const
|
|
519
|
+
const fileUri = await f.getURI();
|
|
520
|
+
const isGsUri =
|
|
521
|
+
fileUri.startsWith('gs://') || fileUri.startsWith('https://storage.googleapis.com/');
|
|
413
522
|
|
|
414
|
-
if (
|
|
523
|
+
if (isGsUri) {
|
|
415
524
|
parts.push({
|
|
416
525
|
fileData: {
|
|
417
|
-
fileUri
|
|
418
|
-
mimeType: f.mime_type
|
|
419
|
-
}
|
|
526
|
+
fileUri,
|
|
527
|
+
mimeType: f.mime_type,
|
|
528
|
+
},
|
|
420
529
|
});
|
|
421
530
|
} else {
|
|
422
531
|
// Inline data handling
|
|
@@ -425,8 +534,8 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
425
534
|
parts.push({
|
|
426
535
|
inlineData: {
|
|
427
536
|
data,
|
|
428
|
-
mimeType: f.mime_type
|
|
429
|
-
}
|
|
537
|
+
mimeType: f.mime_type,
|
|
538
|
+
},
|
|
430
539
|
});
|
|
431
540
|
}
|
|
432
541
|
}
|
|
@@ -453,15 +562,17 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
453
562
|
if (supportsStructuredOutput(options) && !options.tools) {
|
|
454
563
|
// Gemini structured output is unnecessarily sparse. Adding encouragement to fill the fields.
|
|
455
564
|
// Putting JSON in prompt is not recommended by Google, when using structured output.
|
|
456
|
-
system.parts?.push({ text:
|
|
565
|
+
system.parts?.push({ text: 'Fill all appropriate fields in the JSON output.' });
|
|
457
566
|
} else {
|
|
458
567
|
// Fallback to putting the schema in the system instructions, if not using structured output.
|
|
459
568
|
if (options.tools) {
|
|
460
569
|
system.parts?.push({
|
|
461
|
-
text:
|
|
570
|
+
text: `When not calling tools, the output must be a JSON object using the following JSON Schema:\n${JSON.stringify(schema)}`,
|
|
462
571
|
});
|
|
463
572
|
} else {
|
|
464
|
-
system.parts?.push({
|
|
573
|
+
system.parts?.push({
|
|
574
|
+
text: `The output must be a JSON object using the following JSON Schema:\n${JSON.stringify(schema)}`,
|
|
575
|
+
});
|
|
465
576
|
}
|
|
466
577
|
}
|
|
467
578
|
}
|
|
@@ -482,8 +593,11 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
482
593
|
return { contents, system };
|
|
483
594
|
}
|
|
484
595
|
|
|
485
|
-
usageMetadataToTokenUsage(
|
|
486
|
-
|
|
596
|
+
usageMetadataToTokenUsage(
|
|
597
|
+
driver: VertexAIDriver,
|
|
598
|
+
usageMetadata: GenerateContentResponseUsageMetadata | undefined,
|
|
599
|
+
): ExecutionTokenUsage {
|
|
600
|
+
if (!usageMetadata?.totalTokenCount) {
|
|
487
601
|
return {};
|
|
488
602
|
}
|
|
489
603
|
const tokenUsage: ExecutionTokenUsage = {
|
|
@@ -494,16 +608,20 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
494
608
|
};
|
|
495
609
|
|
|
496
610
|
//Output/Response side
|
|
497
|
-
tokenUsage.result =
|
|
498
|
-
|
|
499
|
-
|
|
611
|
+
tokenUsage.result =
|
|
612
|
+
(usageMetadata.candidatesTokenCount ?? 0) +
|
|
613
|
+
(usageMetadata.thoughtsTokenCount ?? 0) +
|
|
614
|
+
(usageMetadata.toolUsePromptTokenCount ?? 0);
|
|
500
615
|
|
|
501
616
|
if ((tokenUsage.total ?? 0) !== (tokenUsage.prompt ?? 0) + tokenUsage.result) {
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
617
|
+
// Token-accounting mismatch: warn-level diagnostic (the call still
|
|
618
|
+
// returns the best-effort tokenUsage). Use the driver's structured
|
|
619
|
+
// logger so we don't promote stderr writes to ERROR in serverless
|
|
620
|
+
// log aggregators — see the recoverable-tool-call sites below.
|
|
621
|
+
driver.logger.warn(
|
|
622
|
+
{ total: tokenUsage.total, prompt: tokenUsage.prompt, result: tokenUsage.result },
|
|
623
|
+
'[VertexAI] Gemini token usage mismatch: total does not equal prompt + result',
|
|
624
|
+
);
|
|
507
625
|
}
|
|
508
626
|
|
|
509
627
|
if (!tokenUsage.result) {
|
|
@@ -513,10 +631,14 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
513
631
|
return tokenUsage;
|
|
514
632
|
}
|
|
515
633
|
|
|
516
|
-
async requestTextCompletion(
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
634
|
+
async requestTextCompletion(
|
|
635
|
+
driver: VertexAIDriver,
|
|
636
|
+
prompt: GenerateContentPrompt,
|
|
637
|
+
options: ExecutionOptions,
|
|
638
|
+
): Promise<Completion> {
|
|
639
|
+
const splits = options.model.split('/');
|
|
640
|
+
let region: string | undefined;
|
|
641
|
+
if (splits[0] === 'locations' && splits.length >= 2) {
|
|
520
642
|
region = splits[1];
|
|
521
643
|
}
|
|
522
644
|
const modelName = splits[splits.length - 1];
|
|
@@ -535,45 +657,61 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
535
657
|
prompt.contents = conversation;
|
|
536
658
|
|
|
537
659
|
// TODO: Remove hack, use global endpoint manually if needed.
|
|
538
|
-
if (options.model.includes(
|
|
539
|
-
region =
|
|
660
|
+
if (options.model.includes('gemini-2.5-flash-image')) {
|
|
661
|
+
region = 'global'; // Gemini Flash Image only available in global region, this is for nano-banana model
|
|
540
662
|
}
|
|
541
663
|
|
|
542
664
|
const model_options = options.model_options as VertexAIGeminiOptions | undefined;
|
|
543
|
-
const client = driver.getGoogleGenAIClient(region, model_options?.flex ?? false);
|
|
665
|
+
const client = driver.getGoogleGenAIClient(region, model_options?.flex ?? false, options.httpTimeout);
|
|
544
666
|
|
|
545
667
|
const payload = getGeminiPayload(options, prompt);
|
|
546
668
|
const response = await client.models.generateContent(payload);
|
|
547
669
|
|
|
548
|
-
const token_usage: ExecutionTokenUsage = this.usageMetadataToTokenUsage(response.usageMetadata);
|
|
670
|
+
const token_usage: ExecutionTokenUsage = this.usageMetadataToTokenUsage(driver, response.usageMetadata);
|
|
549
671
|
|
|
550
672
|
let tool_use: ToolUse[] | undefined;
|
|
551
|
-
let finish_reason: string | undefined, result:
|
|
552
|
-
const candidate = response.candidates
|
|
673
|
+
let finish_reason: string | undefined, result: CompletionResult[] | undefined;
|
|
674
|
+
const candidate = response.candidates?.[0];
|
|
553
675
|
if (candidate) {
|
|
554
676
|
switch (candidate.finishReason) {
|
|
555
|
-
case FinishReason.MAX_TOKENS:
|
|
556
|
-
|
|
557
|
-
|
|
677
|
+
case FinishReason.MAX_TOKENS:
|
|
678
|
+
finish_reason = 'length';
|
|
679
|
+
break;
|
|
680
|
+
case FinishReason.STOP:
|
|
681
|
+
finish_reason = 'stop';
|
|
682
|
+
break;
|
|
683
|
+
default:
|
|
684
|
+
finish_reason = candidate.finishReason;
|
|
558
685
|
}
|
|
559
686
|
const content = candidate.content;
|
|
560
687
|
|
|
561
688
|
// Check for unsupported finish reasons, but allow recoverable tool call issues
|
|
562
689
|
const isRecoverableToolCall = recoverableToolCallReasons.includes(candidate.finishReason as string);
|
|
563
|
-
if (
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
690
|
+
if (
|
|
691
|
+
candidate.finishReason &&
|
|
692
|
+
!supportedFinishReasons.includes(candidate.finishReason) &&
|
|
693
|
+
!isRecoverableToolCall
|
|
694
|
+
) {
|
|
695
|
+
throw new Error(
|
|
696
|
+
`Unsupported finish reason: ${candidate.finishReason}, ` +
|
|
697
|
+
`finish message: ${candidate.finishMessage}, ` +
|
|
698
|
+
`content: ${JSON.stringify(content, null, 2)}, safety: ${JSON.stringify(candidate.safetyRatings, null, 2)}`,
|
|
699
|
+
);
|
|
567
700
|
}
|
|
568
701
|
|
|
569
702
|
if (content) {
|
|
570
703
|
tool_use = collectToolUseParts(content);
|
|
571
704
|
|
|
572
705
|
// For recoverable tool call issues, log warning but continue processing
|
|
573
|
-
// The workflow will handle the invalid tool call gracefully
|
|
706
|
+
// The workflow will handle the invalid tool call gracefully.
|
|
707
|
+
// Route through the driver's structured logger instead of `console.warn`
|
|
708
|
+
// so downstream runtimes (e.g. Cloud Run) don't promote stderr writes
|
|
709
|
+
// to ERROR severity for what is, by definition, a recoverable event.
|
|
574
710
|
if (isRecoverableToolCall && tool_use && tool_use.length > 0) {
|
|
575
|
-
|
|
576
|
-
`
|
|
711
|
+
driver.logger.warn(
|
|
712
|
+
`[Gemini] Recoverable tool call issue (${candidate.finishReason}): ` +
|
|
713
|
+
`Model tried to call undeclared tool(s): ${tool_use.map((t) => t.tool_name).join(', ')}`,
|
|
714
|
+
);
|
|
577
715
|
}
|
|
578
716
|
|
|
579
717
|
result = extractCompletionResults(content);
|
|
@@ -581,10 +719,8 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
581
719
|
}
|
|
582
720
|
}
|
|
583
721
|
|
|
584
|
-
|
|
585
|
-
|
|
586
722
|
if (tool_use) {
|
|
587
|
-
finish_reason =
|
|
723
|
+
finish_reason = 'tool_use';
|
|
588
724
|
}
|
|
589
725
|
|
|
590
726
|
// Increment turn counter for deferred stripping
|
|
@@ -595,7 +731,7 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
595
731
|
const stripOptions = {
|
|
596
732
|
keepForTurns: options.stripImagesAfterTurns ?? Infinity,
|
|
597
733
|
currentTurn,
|
|
598
|
-
textMaxTokens: options.stripTextMaxTokens
|
|
734
|
+
textMaxTokens: options.stripTextMaxTokens,
|
|
599
735
|
};
|
|
600
736
|
let processedConversation = stripBase64ImagesFromConversation(conversation, stripOptions);
|
|
601
737
|
|
|
@@ -612,19 +748,23 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
612
748
|
const finalConversation = storeSystemInConversation(processedConversation, prompt.system);
|
|
613
749
|
|
|
614
750
|
return {
|
|
615
|
-
result: result && result.length > 0 ? result : [{ type:
|
|
751
|
+
result: result && result.length > 0 ? result : [{ type: 'text' as const, value: '' }],
|
|
616
752
|
token_usage: token_usage,
|
|
617
753
|
finish_reason: finish_reason,
|
|
618
754
|
original_response: options.include_original_response ? response : undefined,
|
|
619
755
|
conversation: finalConversation,
|
|
620
|
-
tool_use
|
|
756
|
+
tool_use,
|
|
621
757
|
} satisfies Completion;
|
|
622
758
|
}
|
|
623
759
|
|
|
624
|
-
async requestTextCompletionStream(
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
760
|
+
async requestTextCompletionStream(
|
|
761
|
+
driver: VertexAIDriver,
|
|
762
|
+
prompt: GenerateContentPrompt,
|
|
763
|
+
options: ExecutionOptions,
|
|
764
|
+
): Promise<AsyncIterable<CompletionChunkObject>> {
|
|
765
|
+
const splits = options.model.split('/');
|
|
766
|
+
let region: string | undefined;
|
|
767
|
+
if (splits[0] === 'locations' && splits.length >= 2) {
|
|
628
768
|
region = splits[1];
|
|
629
769
|
}
|
|
630
770
|
const modelName = splits[splits.length - 1];
|
|
@@ -643,44 +783,60 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
643
783
|
const conversation = updateConversation(options.conversation, prompt.contents);
|
|
644
784
|
prompt.contents = conversation;
|
|
645
785
|
|
|
646
|
-
if (options.model.includes(
|
|
647
|
-
region =
|
|
786
|
+
if (options.model.includes('gemini-2.5-flash-image')) {
|
|
787
|
+
region = 'global'; // Gemini Flash Image only available in global region, this is for nano-banana model
|
|
648
788
|
}
|
|
649
789
|
|
|
650
790
|
const model_options = options.model_options as VertexAIGeminiOptions | undefined;
|
|
651
|
-
const client = driver.getGoogleGenAIClient(region, model_options?.flex ?? false);
|
|
791
|
+
const client = driver.getGoogleGenAIClient(region, model_options?.flex ?? false, options.httpTimeout);
|
|
652
792
|
|
|
653
793
|
const payload = getGeminiPayload(options, prompt);
|
|
654
794
|
const response = await client.models.generateContentStream(payload);
|
|
655
795
|
|
|
656
796
|
const stream = asyncMap(response, async (item) => {
|
|
657
|
-
const token_usage: ExecutionTokenUsage = this.usageMetadataToTokenUsage(item.usageMetadata);
|
|
797
|
+
const token_usage: ExecutionTokenUsage = this.usageMetadataToTokenUsage(driver, item.usageMetadata);
|
|
658
798
|
if (item.candidates && item.candidates.length > 0) {
|
|
659
799
|
for (const candidate of item.candidates) {
|
|
660
800
|
let tool_use: ToolUse[] | undefined;
|
|
661
801
|
let finish_reason: string | undefined;
|
|
662
802
|
switch (candidate.finishReason) {
|
|
663
|
-
case FinishReason.MAX_TOKENS:
|
|
664
|
-
|
|
665
|
-
|
|
803
|
+
case FinishReason.MAX_TOKENS:
|
|
804
|
+
finish_reason = 'length';
|
|
805
|
+
break;
|
|
806
|
+
case FinishReason.STOP:
|
|
807
|
+
finish_reason = 'stop';
|
|
808
|
+
break;
|
|
809
|
+
default:
|
|
810
|
+
finish_reason = candidate.finishReason;
|
|
666
811
|
}
|
|
667
812
|
// Check for unsupported finish reasons, but allow recoverable tool call issues
|
|
668
813
|
const isRecoverableToolCall = recoverableToolCallReasons.includes(candidate.finishReason as string);
|
|
669
|
-
if (
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
814
|
+
if (
|
|
815
|
+
candidate.finishReason &&
|
|
816
|
+
!supportedFinishReasons.includes(candidate.finishReason) &&
|
|
817
|
+
!isRecoverableToolCall
|
|
818
|
+
) {
|
|
819
|
+
throw new Error(
|
|
820
|
+
`Unsupported finish reason: ${candidate.finishReason}, ` +
|
|
821
|
+
`finish message: ${candidate.finishMessage}, ` +
|
|
822
|
+
`content: ${JSON.stringify(candidate.content, null, 2)}, safety: ${JSON.stringify(candidate.safetyRatings, null, 2)}`,
|
|
823
|
+
);
|
|
673
824
|
}
|
|
674
825
|
if (candidate.content?.role === 'model') {
|
|
675
826
|
// Collect all parts in order (text and images)
|
|
676
827
|
const combinedResults = extractCompletionResults(candidate.content);
|
|
677
828
|
tool_use = collectToolUseParts(candidate.content);
|
|
678
829
|
if (tool_use) {
|
|
679
|
-
finish_reason =
|
|
680
|
-
// Log warning for recoverable tool call issues
|
|
830
|
+
finish_reason = 'tool_use';
|
|
831
|
+
// Log warning for recoverable tool call issues — see the
|
|
832
|
+
// matching site in `requestTextCompletion` above for why
|
|
833
|
+
// we route through the driver's logger instead of
|
|
834
|
+
// `console.warn`.
|
|
681
835
|
if (isRecoverableToolCall) {
|
|
682
|
-
|
|
683
|
-
`
|
|
836
|
+
driver.logger.warn(
|
|
837
|
+
`[Gemini] Recoverable tool call issue (${candidate.finishReason}): ` +
|
|
838
|
+
`Model tried to call undeclared tool(s): ${tool_use.map((t) => t.tool_name).join(', ')}`,
|
|
839
|
+
);
|
|
684
840
|
}
|
|
685
841
|
}
|
|
686
842
|
return {
|
|
@@ -694,8 +850,10 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
694
850
|
}
|
|
695
851
|
//No normal output, returning block reason if it exists.
|
|
696
852
|
return {
|
|
697
|
-
result: item.promptFeedback?.blockReasonMessage
|
|
698
|
-
|
|
853
|
+
result: item.promptFeedback?.blockReasonMessage
|
|
854
|
+
? [{ type: 'text' as const, value: item.promptFeedback.blockReasonMessage }]
|
|
855
|
+
: [],
|
|
856
|
+
finish_reason: item.promptFeedback?.blockReason ?? '',
|
|
699
857
|
token_usage: token_usage,
|
|
700
858
|
};
|
|
701
859
|
});
|
|
@@ -705,11 +863,11 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
705
863
|
|
|
706
864
|
/**
|
|
707
865
|
* Format Google API errors into LlumiverseError with proper status codes and retryability.
|
|
708
|
-
*
|
|
866
|
+
*
|
|
709
867
|
* Google API errors follow AIP-193 standard:
|
|
710
868
|
* - ApiError.status: HTTP status code
|
|
711
869
|
* - ApiError.message: Error message
|
|
712
|
-
*
|
|
870
|
+
*
|
|
713
871
|
* Common error codes:
|
|
714
872
|
* - 400 (INVALID_ARGUMENT): Invalid request parameters
|
|
715
873
|
* - 401 (UNAUTHENTICATED): Authentication required
|
|
@@ -719,15 +877,11 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
719
877
|
* - 500 (INTERNAL): Internal server error
|
|
720
878
|
* - 503 (UNAVAILABLE): Service temporarily unavailable
|
|
721
879
|
* - 504 (DEADLINE_EXCEEDED): Request timeout
|
|
722
|
-
*
|
|
880
|
+
*
|
|
723
881
|
* @see https://google.aip.dev/193
|
|
724
882
|
* @see https://docs.cloud.google.com/vertex-ai/generative-ai/docs/model-reference/api-errors
|
|
725
883
|
*/
|
|
726
|
-
formatLlumiverseError(
|
|
727
|
-
_driver: VertexAIDriver,
|
|
728
|
-
error: unknown,
|
|
729
|
-
context: LlumiverseErrorContext
|
|
730
|
-
): LlumiverseError {
|
|
884
|
+
formatLlumiverseError(_driver: VertexAIDriver, error: unknown, context: LlumiverseErrorContext): LlumiverseError {
|
|
731
885
|
// Check if it's a Google API error with status code
|
|
732
886
|
const isApiError = this.isGoogleApiError(error);
|
|
733
887
|
|
|
@@ -737,11 +891,11 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
737
891
|
throw error;
|
|
738
892
|
}
|
|
739
893
|
|
|
740
|
-
const apiError = error
|
|
894
|
+
const apiError = error;
|
|
741
895
|
const httpStatusCode = apiError.status;
|
|
742
896
|
|
|
743
897
|
// Extract error message
|
|
744
|
-
const message = apiError.message
|
|
898
|
+
const message = apiError.message;
|
|
745
899
|
|
|
746
900
|
// Build user-facing message with status code
|
|
747
901
|
let userMessage = message;
|
|
@@ -752,7 +906,7 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
752
906
|
}
|
|
753
907
|
|
|
754
908
|
// Determine retryability based on Google error codes
|
|
755
|
-
const retryable = this.isGeminiErrorRetryable(httpStatusCode);
|
|
909
|
+
const retryable = this.isGeminiErrorRetryable(httpStatusCode, message);
|
|
756
910
|
|
|
757
911
|
// Extract error name/type from message if present
|
|
758
912
|
const errorName = this.extractErrorName(message);
|
|
@@ -763,34 +917,36 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
763
917
|
context,
|
|
764
918
|
error,
|
|
765
919
|
httpStatusCode,
|
|
766
|
-
errorName
|
|
920
|
+
errorName,
|
|
767
921
|
);
|
|
768
922
|
}
|
|
769
923
|
|
|
770
924
|
/**
|
|
771
925
|
* Type guard to check if error is a Google API error.
|
|
772
926
|
*/
|
|
773
|
-
private isGoogleApiError(error: unknown): error is
|
|
927
|
+
private isGoogleApiError(error: unknown): error is GoogleApiErrorLike {
|
|
774
928
|
return (
|
|
775
929
|
error !== null &&
|
|
776
930
|
typeof error === 'object' &&
|
|
777
931
|
'status' in error &&
|
|
778
|
-
typeof (error as
|
|
779
|
-
'message' in error
|
|
932
|
+
typeof (error as { status?: unknown }).status === 'number' &&
|
|
933
|
+
'message' in error &&
|
|
934
|
+
typeof (error as { message?: unknown }).message === 'string'
|
|
780
935
|
);
|
|
781
936
|
}
|
|
782
937
|
|
|
783
938
|
/**
|
|
784
939
|
* Determine if a Google API error is retryable based on HTTP status code.
|
|
785
|
-
*
|
|
940
|
+
*
|
|
786
941
|
* Retryable errors (per Google AIP-194):
|
|
787
942
|
* - 408 (REQUEST_TIMEOUT): Request timeout
|
|
943
|
+
* - 499 (CANCELLED / Client Closed Request): Transport cancellation
|
|
788
944
|
* - 429 (RESOURCE_EXHAUSTED): Rate limit exceeded, quota exhausted
|
|
789
945
|
* - 500 (INTERNAL): Internal server error
|
|
790
946
|
* - 502 (BAD_GATEWAY): Bad gateway
|
|
791
947
|
* - 503 (UNAVAILABLE): Service temporarily unavailable
|
|
792
948
|
* - 504 (DEADLINE_EXCEEDED): Gateway timeout
|
|
793
|
-
*
|
|
949
|
+
*
|
|
794
950
|
* Non-retryable errors:
|
|
795
951
|
* - 400 (INVALID_ARGUMENT): Invalid request parameters
|
|
796
952
|
* - 401 (UNAUTHENTICATED): Authentication required
|
|
@@ -798,19 +954,48 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
798
954
|
* - 404 (NOT_FOUND): Resource not found
|
|
799
955
|
* - 409 (CONFLICT): Resource conflict
|
|
800
956
|
* - Other 4xx client errors
|
|
801
|
-
*
|
|
957
|
+
*
|
|
958
|
+
* Exception: certain 400s from Vertex AI's inline URL fetcher (used when
|
|
959
|
+
* passing a file by URL to multimodal models) surface as INVALID_ARGUMENT
|
|
960
|
+
* but are actually transient throttling/rate-limit signals on the
|
|
961
|
+
* fetcher, not a bad request. Detect those by message substring and
|
|
962
|
+
* treat them as retryable.
|
|
963
|
+
*
|
|
802
964
|
* @param httpStatusCode - The HTTP status code from the API error
|
|
965
|
+
* @param message - The error message (used to detect transient 400 sub-cases)
|
|
803
966
|
* @returns True if retryable, false if not retryable, undefined if unknown
|
|
804
967
|
*/
|
|
805
|
-
private isGeminiErrorRetryable(httpStatusCode: number): boolean | undefined {
|
|
968
|
+
private isGeminiErrorRetryable(httpStatusCode: number, message?: string): boolean | undefined {
|
|
969
|
+
if (message && this.isNonRetryableAuthError(message)) return false;
|
|
970
|
+
|
|
806
971
|
// Retryable status codes
|
|
807
972
|
if (httpStatusCode === 408) return true; // Request timeout
|
|
808
973
|
if (httpStatusCode === 429) return true; // Rate limit/quota
|
|
974
|
+
if (httpStatusCode === 499) return true; // Client closed / operation cancelled
|
|
809
975
|
if (httpStatusCode === 502) return true; // Bad gateway
|
|
810
976
|
if (httpStatusCode === 503) return true; // Service unavailable
|
|
811
977
|
if (httpStatusCode === 504) return true; // Gateway timeout
|
|
812
978
|
if (httpStatusCode >= 500 && httpStatusCode < 600) return true; // Other 5xx server errors
|
|
813
979
|
|
|
980
|
+
// Vertex AI URL fetcher transient throttling, surfaced as 400 INVALID_ARGUMENT
|
|
981
|
+
// but really a Google-side rate limit on the inline-content fetcher.
|
|
982
|
+
if (httpStatusCode === 400 && message) {
|
|
983
|
+
if (
|
|
984
|
+
message.includes('URL_REJECTED-REJECTED_CLIENT_THROTTLED') ||
|
|
985
|
+
message.includes('URL_REJECTED-REJECTED_RATE_LIMITED')
|
|
986
|
+
) {
|
|
987
|
+
return true;
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
// A transport-level abort/cancel (request-timeout / dropped connection, sometimes reported
|
|
992
|
+
// as 499 client-closed) or a deadline-exceeded is transient and should be retried,
|
|
993
|
+
// even though it carries a 4xx status. Honor it before the 4xx -> non-retryable rule.
|
|
994
|
+
if (message) {
|
|
995
|
+
const lower = message.toLowerCase();
|
|
996
|
+
if (lower.includes('aborted') || lower.includes('cancelled') || lower.includes('deadline')) return true;
|
|
997
|
+
}
|
|
998
|
+
|
|
814
999
|
// Non-retryable 4xx client errors
|
|
815
1000
|
if (httpStatusCode >= 400 && httpStatusCode < 500) return false;
|
|
816
1001
|
|
|
@@ -818,6 +1003,11 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
818
1003
|
return undefined;
|
|
819
1004
|
}
|
|
820
1005
|
|
|
1006
|
+
private isNonRetryableAuthError(message: string): boolean {
|
|
1007
|
+
const lowerMessage = message.toLowerCase();
|
|
1008
|
+
return lowerMessage.includes('invalid_grant') || lowerMessage.includes("credential's issuer");
|
|
1009
|
+
}
|
|
1010
|
+
|
|
821
1011
|
/**
|
|
822
1012
|
* Extract error type name from error message.
|
|
823
1013
|
* Google errors often include the error type in the message.
|
|
@@ -826,9 +1016,10 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
826
1016
|
private extractErrorName(message: string): string | undefined {
|
|
827
1017
|
// Common Google error patterns
|
|
828
1018
|
const patterns = [
|
|
829
|
-
/^([
|
|
1019
|
+
/^Error code ([a-zA-Z0-9_-]+):/, // "Error code invalid_grant: message"
|
|
1020
|
+
/^([A-Z_]+):/, // "ERROR_NAME: message"
|
|
830
1021
|
/\[([A-Z_]+)\]/, // "[ERROR_NAME] message"
|
|
831
|
-
/^(\w+Error):/,
|
|
1022
|
+
/^(\w+Error):/, // "ErrorTypeError: message"
|
|
832
1023
|
];
|
|
833
1024
|
|
|
834
1025
|
for (const pattern of patterns) {
|
|
@@ -840,31 +1031,30 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
840
1031
|
|
|
841
1032
|
return undefined;
|
|
842
1033
|
}
|
|
843
|
-
|
|
844
1034
|
}
|
|
845
1035
|
|
|
846
|
-
|
|
847
1036
|
/**
|
|
848
1037
|
* Converts functionCall and functionResponse parts to text parts in Gemini Content[].
|
|
849
1038
|
* Preserves tool call information while removing structured parts that require
|
|
850
1039
|
* tools/toolConfig to be defined in the API request.
|
|
851
1040
|
*/
|
|
852
1041
|
export function convertGeminiFunctionPartsToText(contents: Content[]): Content[] {
|
|
853
|
-
return contents.map(content => {
|
|
1042
|
+
return contents.map((content) => {
|
|
854
1043
|
if (!content.parts) return content;
|
|
855
|
-
const hasFunctionParts = content.parts.some(p => p.functionCall || p.functionResponse);
|
|
1044
|
+
const hasFunctionParts = content.parts.some((p) => p.functionCall || p.functionResponse);
|
|
856
1045
|
if (!hasFunctionParts) return content;
|
|
857
1046
|
|
|
858
|
-
const newParts = content.parts.map(part => {
|
|
1047
|
+
const newParts = content.parts.map((part) => {
|
|
859
1048
|
if (part.functionCall) {
|
|
860
1049
|
const argsStr = part.functionCall.args ? JSON.stringify(part.functionCall.args) : '';
|
|
861
|
-
const truncated = argsStr.length > 500 ? argsStr.substring(0, 500)
|
|
1050
|
+
const truncated = argsStr.length > 500 ? `${argsStr.substring(0, 500)}...` : argsStr;
|
|
862
1051
|
return { text: `[Tool call: ${part.functionCall.name}(${truncated})]` };
|
|
863
1052
|
}
|
|
864
1053
|
if (part.functionResponse) {
|
|
865
1054
|
const respStr = part.functionResponse.response
|
|
866
|
-
? JSON.stringify(part.functionResponse.response)
|
|
867
|
-
|
|
1055
|
+
? JSON.stringify(part.functionResponse.response)
|
|
1056
|
+
: 'No response';
|
|
1057
|
+
const truncated = respStr.length > 500 ? `${respStr.substring(0, 500)}...` : respStr;
|
|
868
1058
|
return { text: `[Tool result for ${part.functionResponse.name}: ${truncated}]` };
|
|
869
1059
|
}
|
|
870
1060
|
return part;
|
|
@@ -881,7 +1071,7 @@ function getToolDefinitions(tools: ToolDefinition[] | undefined | null): Tool |
|
|
|
881
1071
|
// For multiple tools, we have multiple functions in one tool.
|
|
882
1072
|
return {
|
|
883
1073
|
functionDeclarations: tools.map(getToolFunction),
|
|
884
|
-
}
|
|
1074
|
+
};
|
|
885
1075
|
}
|
|
886
1076
|
|
|
887
1077
|
function getToolFunction(tool: ToolDefinition): FunctionDeclaration {
|
|
@@ -904,7 +1094,7 @@ function getToolFunction(tool: ToolDefinition): FunctionDeclaration {
|
|
|
904
1094
|
function updateConversation(conversation: unknown, prompt: Content[]): Content[] {
|
|
905
1095
|
// Unwrap array if wrapped, otherwise treat as array
|
|
906
1096
|
const unwrapped = unwrapConversationArray<Content>(conversation);
|
|
907
|
-
const convArray = unwrapped ?? (conversation as Content[] || []);
|
|
1097
|
+
const convArray = unwrapped ?? ((conversation as Content[]) || []);
|
|
908
1098
|
return convArray.concat(prompt);
|
|
909
1099
|
}
|
|
910
1100
|
|
|
@@ -933,7 +1123,7 @@ function extractSystemFromConversation(conversation: unknown): Content | undefin
|
|
|
933
1123
|
function storeSystemInConversation(conversation: unknown, system: Content | undefined): unknown {
|
|
934
1124
|
if (!system) return conversation;
|
|
935
1125
|
if (typeof conversation === 'object' && conversation !== null) {
|
|
936
|
-
return { ...conversation as object, [SYSTEM_KEY]: system };
|
|
1126
|
+
return { ...(conversation as object), [SYSTEM_KEY]: system };
|
|
937
1127
|
}
|
|
938
1128
|
return conversation;
|
|
939
1129
|
}
|
|
@@ -951,10 +1141,10 @@ function storeSystemInConversation(conversation: unknown, system: Content | unde
|
|
|
951
1141
|
*/
|
|
952
1142
|
function formatFunctionResponse(response: string): JSONObject {
|
|
953
1143
|
response = response.trim();
|
|
954
|
-
if (response.startsWith(
|
|
1144
|
+
if (response.startsWith('{') && response.endsWith('}')) {
|
|
955
1145
|
try {
|
|
956
1146
|
return JSON.parse(response);
|
|
957
|
-
} catch
|
|
1147
|
+
} catch {
|
|
958
1148
|
return { output: response };
|
|
959
1149
|
}
|
|
960
1150
|
} else {
|