@llumiverse/drivers 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/{types/adobe → adobe}/firefly.d.ts +2 -1
- package/lib/adobe/firefly.d.ts.map +1 -0
- package/lib/adobe/firefly.js +120 -0
- package/lib/adobe/firefly.js.map +1 -0
- package/lib/{types/anthropic → anthropic}/index.d.ts +4 -2
- package/lib/anthropic/index.d.ts.map +1 -0
- package/lib/{esm/anthropic → anthropic}/index.js +19 -8
- package/lib/anthropic/index.js.map +1 -0
- package/lib/{types/azure → azure}/azure_foundry.d.ts +9 -7
- package/lib/azure/azure_foundry.d.ts.map +1 -0
- package/lib/{esm/azure → azure}/azure_foundry.js +138 -124
- package/lib/azure/azure_foundry.js.map +1 -0
- package/lib/{types/bedrock → bedrock}/converse.d.ts +2 -2
- package/lib/bedrock/converse.d.ts.map +1 -0
- package/lib/{esm/bedrock → bedrock}/converse.js +86 -79
- package/lib/bedrock/converse.js.map +1 -0
- package/lib/bedrock/embeddings.d.ts +4 -0
- package/lib/bedrock/embeddings.d.ts.map +1 -0
- package/lib/bedrock/embeddings.js +359 -0
- package/lib/bedrock/embeddings.js.map +1 -0
- package/lib/{types/bedrock → bedrock}/index.d.ts +33 -9
- package/lib/bedrock/index.d.ts.map +1 -0
- package/lib/{esm/bedrock → bedrock}/index.js +599 -324
- package/lib/bedrock/index.js.map +1 -0
- package/lib/{types/bedrock → bedrock}/nova-image-payload.d.ts +5 -5
- package/lib/bedrock/nova-image-payload.d.ts.map +1 -0
- package/lib/{esm/bedrock → bedrock}/nova-image-payload.js +31 -36
- package/lib/bedrock/nova-image-payload.js.map +1 -0
- package/lib/{types/bedrock → bedrock}/payloads.d.ts +1 -1
- package/lib/bedrock/payloads.d.ts.map +1 -0
- package/lib/{cjs/bedrock → bedrock}/payloads.js.map +1 -1
- package/lib/{types/bedrock → bedrock}/s3.d.ts +3 -2
- package/lib/bedrock/s3.d.ts.map +1 -0
- package/lib/{esm/bedrock → bedrock}/s3.js +27 -23
- package/lib/bedrock/s3.js.map +1 -0
- package/lib/{types/bedrock → bedrock}/twelvelabs.d.ts +8 -9
- package/lib/bedrock/twelvelabs.d.ts.map +1 -0
- package/lib/{esm/bedrock → bedrock}/twelvelabs.js +17 -21
- package/lib/bedrock/twelvelabs.js.map +1 -0
- package/lib/{types/groq → groq}/index.d.ts +6 -4
- package/lib/groq/index.d.ts.map +1 -0
- package/lib/{esm/groq → groq}/index.js +72 -39
- package/lib/groq/index.js.map +1 -0
- package/lib/{types/huggingface_ie.d.ts → huggingface_ie.d.ts} +4 -3
- package/lib/huggingface_ie.d.ts.map +1 -0
- package/lib/{esm/huggingface_ie.js → huggingface_ie.js} +32 -31
- package/lib/huggingface_ie.js.map +1 -0
- package/lib/index.d.ts +16 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +16 -0
- package/lib/index.js.map +1 -0
- package/lib/{types/mistral → mistral}/index.d.ts +6 -5
- package/lib/mistral/index.d.ts.map +1 -0
- package/lib/{esm/mistral → mistral}/index.js +66 -38
- package/lib/mistral/index.js.map +1 -0
- package/lib/{types/mistral → mistral}/types.d.ts +1 -0
- package/lib/mistral/types.d.ts.map +1 -0
- package/lib/mistral/types.js.map +1 -0
- package/lib/{types/openai → openai}/azure_openai.d.ts +5 -4
- package/lib/openai/azure_openai.d.ts.map +1 -0
- package/lib/{esm/openai → openai}/azure_openai.js +17 -14
- package/lib/openai/azure_openai.js.map +1 -0
- package/lib/openai/drivers.d.ts +4 -0
- package/lib/openai/drivers.d.ts.map +1 -0
- package/lib/openai/drivers.js +9 -0
- package/lib/openai/drivers.js.map +1 -0
- package/lib/{types/openai → openai}/index.d.ts +16 -5
- package/lib/openai/index.d.ts.map +1 -0
- package/lib/{esm/openai → openai}/index.js +242 -141
- package/lib/openai/index.js.map +1 -0
- package/lib/{types/openai → openai}/openai.d.ts +3 -3
- package/lib/openai/openai.d.ts.map +1 -0
- package/lib/openai/openai.js +15 -0
- package/lib/openai/openai.js.map +1 -0
- package/lib/{types/openai → openai}/openai_compatible.d.ts +3 -3
- package/lib/openai/openai_compatible.d.ts.map +1 -0
- package/lib/{esm/openai → openai}/openai_compatible.js +14 -11
- package/lib/openai/openai_compatible.js.map +1 -0
- package/lib/{types/openai → openai}/openai_format.d.ts +3 -2
- package/lib/openai/openai_format.d.ts.map +1 -0
- package/lib/{esm/openai → openai}/openai_format.js +66 -24
- package/lib/openai/openai_format.js.map +1 -0
- package/lib/{types/replicate.d.ts → replicate.d.ts} +3 -2
- package/lib/replicate.d.ts.map +1 -0
- package/lib/{esm/replicate.js → replicate.js} +59 -49
- package/lib/replicate.js.map +1 -0
- package/lib/{types/shared → shared}/claude-messages.d.ts +25 -1
- package/lib/shared/claude-messages.d.ts.map +1 -0
- package/lib/{esm/shared → shared}/claude-messages.js +212 -59
- package/lib/shared/claude-messages.js.map +1 -0
- package/lib/{types/shared → shared}/claude-thinking.d.ts +1 -1
- package/lib/shared/claude-thinking.d.ts.map +1 -0
- package/lib/{esm/shared → shared}/claude-thinking.js +5 -7
- package/lib/shared/claude-thinking.js.map +1 -0
- package/lib/shared/debug-prompt.d.ts +5 -0
- package/lib/shared/debug-prompt.d.ts.map +1 -0
- package/lib/shared/debug-prompt.js +25 -0
- package/lib/shared/debug-prompt.js.map +1 -0
- package/lib/{types/test-driver → test-driver}/TestErrorCompletionStream.d.ts +1 -1
- package/lib/test-driver/TestErrorCompletionStream.d.ts.map +1 -0
- package/lib/test-driver/TestErrorCompletionStream.js +16 -0
- package/lib/test-driver/TestErrorCompletionStream.js.map +1 -0
- package/lib/{types/test-driver → test-driver}/TestValidationErrorCompletionStream.d.ts +1 -1
- package/lib/test-driver/TestValidationErrorCompletionStream.d.ts.map +1 -0
- package/lib/{esm/test-driver → test-driver}/TestValidationErrorCompletionStream.js +4 -4
- package/lib/test-driver/TestValidationErrorCompletionStream.js.map +1 -0
- package/lib/{types/test-driver → test-driver}/index.d.ts +3 -3
- package/lib/test-driver/index.d.ts.map +1 -0
- package/lib/{esm/test-driver → test-driver}/index.js +20 -20
- package/lib/test-driver/index.js.map +1 -0
- package/lib/{types/test-driver → test-driver}/utils.d.ts +1 -1
- package/lib/test-driver/utils.d.ts.map +1 -0
- package/lib/{esm/test-driver → test-driver}/utils.js +4 -4
- package/lib/test-driver/utils.js.map +1 -0
- package/lib/togetherai/index.d.ts +15 -0
- package/lib/togetherai/index.d.ts.map +1 -0
- package/lib/togetherai/index.js +54 -0
- package/lib/togetherai/index.js.map +1 -0
- package/lib/{types/togetherai → togetherai}/interfaces.d.ts +1 -1
- package/lib/togetherai/interfaces.d.ts.map +1 -0
- package/lib/{cjs/watsonx → togetherai}/interfaces.js.map +1 -1
- package/lib/vertexai/debug.d.ts +2 -0
- package/lib/vertexai/debug.d.ts.map +1 -0
- package/lib/{esm/vertexai → vertexai}/debug.js +1 -1
- package/lib/vertexai/debug.js.map +1 -0
- package/lib/vertexai/embeddings/embed-legacy-multimodal.d.ts +9 -0
- package/lib/vertexai/embeddings/embed-legacy-multimodal.d.ts.map +1 -0
- package/lib/vertexai/embeddings/embed-legacy-multimodal.js +141 -0
- package/lib/vertexai/embeddings/embed-legacy-multimodal.js.map +1 -0
- package/lib/vertexai/embeddings/embed.d.ts +13 -0
- package/lib/vertexai/embeddings/embed.d.ts.map +1 -0
- package/lib/vertexai/embeddings/embed.js +162 -0
- package/lib/vertexai/embeddings/embed.js.map +1 -0
- package/lib/vertexai/embeddings/source-utils.d.ts +9 -0
- package/lib/vertexai/embeddings/source-utils.d.ts.map +1 -0
- package/lib/vertexai/embeddings/source-utils.js +18 -0
- package/lib/vertexai/embeddings/source-utils.js.map +1 -0
- package/lib/{types/vertexai → vertexai}/index.d.ts +31 -19
- package/lib/vertexai/index.d.ts.map +1 -0
- package/lib/{esm/vertexai → vertexai}/index.js +221 -159
- package/lib/vertexai/index.js.map +1 -0
- package/lib/{types/vertexai → vertexai}/models/claude.d.ts +4 -4
- package/lib/vertexai/models/claude.d.ts.map +1 -0
- package/lib/{esm/vertexai → vertexai}/models/claude.js +17 -20
- package/lib/vertexai/models/claude.js.map +1 -0
- package/lib/{types/vertexai → vertexai}/models/gemini.d.ts +28 -4
- package/lib/vertexai/models/gemini.d.ts.map +1 -0
- package/lib/{esm/vertexai → vertexai}/models/gemini.js +238 -116
- package/lib/vertexai/models/gemini.js.map +1 -0
- package/lib/{types/vertexai → vertexai}/models/imagen.d.ts +12 -11
- package/lib/vertexai/models/imagen.d.ts.map +1 -0
- package/lib/{esm/vertexai → vertexai}/models/imagen.js +74 -47
- package/lib/vertexai/models/imagen.js.map +1 -0
- package/lib/{types/vertexai → vertexai}/models/llama.d.ts +4 -4
- package/lib/vertexai/models/llama.d.ts.map +1 -0
- package/lib/{esm/vertexai → vertexai}/models/llama.js +37 -33
- package/lib/vertexai/models/llama.js.map +1 -0
- package/lib/vertexai/models.d.ts +21 -0
- package/lib/vertexai/models.d.ts.map +1 -0
- package/lib/{esm/vertexai → vertexai}/models.js +12 -9
- package/lib/vertexai/models.js.map +1 -0
- package/lib/{types/watsonx → watsonx}/index.d.ts +4 -3
- package/lib/watsonx/index.d.ts.map +1 -0
- package/lib/{esm/watsonx → watsonx}/index.js +69 -41
- package/lib/watsonx/index.js.map +1 -0
- package/lib/watsonx/interfaces.d.ts.map +1 -0
- package/lib/{esm/watsonx → watsonx}/interfaces.js.map +1 -1
- package/lib/{types/xai → xai}/index.d.ts +4 -4
- package/lib/xai/index.d.ts.map +1 -0
- package/lib/{esm/xai → xai}/index.js +22 -18
- package/lib/xai/index.js.map +1 -0
- package/package.json +87 -36
- package/src/adobe/firefly.ts +60 -43
- package/src/anthropic/index.ts +37 -20
- package/src/azure/azure_foundry.ts +210 -165
- package/src/bedrock/converse.ts +124 -101
- package/src/bedrock/embeddings.test.ts +508 -0
- package/src/bedrock/embeddings.ts +517 -0
- package/src/bedrock/error-handling.test.ts +62 -14
- package/src/bedrock/index.ts +832 -466
- package/src/bedrock/nova-image-payload.ts +98 -91
- package/src/bedrock/payloads.ts +9 -9
- package/src/bedrock/s3.test.ts +39 -0
- package/src/bedrock/s3.ts +57 -38
- package/src/bedrock/streaming-tool-use.test.ts +86 -69
- package/src/bedrock/twelvelabs.ts +35 -32
- package/src/groq/index.ts +143 -60
- package/src/http-timeout-wiring.test.ts +263 -0
- package/src/huggingface_ie.ts +45 -61
- package/src/index.ts +15 -16
- package/src/mistral/index.ts +97 -48
- package/src/mistral/types.ts +13 -12
- package/src/openai/azure_openai.ts +35 -27
- package/src/openai/drivers.ts +9 -0
- package/src/openai/error-handling.test.ts +308 -113
- package/src/openai/index.ts +378 -248
- package/src/openai/openai.ts +6 -13
- package/src/openai/openai_compatible.ts +35 -25
- package/src/openai/openai_format.ts +90 -40
- package/src/replicate.ts +108 -86
- package/src/shared/claude-messages.test.ts +37 -0
- package/src/shared/claude-messages.ts +303 -97
- package/src/shared/claude-thinking.ts +6 -12
- package/src/shared/debug-prompt.ts +28 -0
- package/src/test-driver/TestErrorCompletionStream.ts +9 -10
- package/src/test-driver/TestValidationErrorCompletionStream.ts +9 -10
- package/src/test-driver/index.ts +43 -27
- package/src/test-driver/utils.ts +8 -8
- package/src/togetherai/index.test.ts +32 -0
- package/src/togetherai/index.ts +64 -128
- package/src/togetherai/interfaces.ts +5 -5
- package/src/vertexai/debug.ts +3 -3
- package/src/vertexai/embeddings/embed-legacy-multimodal.ts +211 -0
- package/src/vertexai/embeddings/embed.ts +206 -0
- package/src/vertexai/embeddings/embeddings.test.ts +497 -0
- package/src/vertexai/embeddings/source-utils.test.ts +54 -0
- package/src/vertexai/embeddings/source-utils.ts +29 -0
- package/src/vertexai/index.ts +331 -244
- package/src/vertexai/models/claude-error-handling.test.ts +83 -36
- package/src/vertexai/models/claude-streaming-spacing.test.ts +82 -75
- package/src/vertexai/models/claude.ts +55 -40
- package/src/vertexai/models/gemini-conversation-mutation.test.ts +65 -23
- package/src/vertexai/models/gemini-error-handling.test.ts +126 -10
- package/src/vertexai/models/gemini-orphaned-tool-result.test.ts +118 -0
- package/src/vertexai/models/gemini.ts +349 -172
- package/src/vertexai/models/imagen.ts +164 -100
- package/src/vertexai/models/llama.ts +70 -48
- package/src/vertexai/models.ts +36 -16
- package/src/watsonx/index.ts +101 -65
- package/src/watsonx/interfaces.ts +10 -15
- package/src/xai/index.ts +30 -30
- package/lib/cjs/adobe/firefly.js +0 -120
- package/lib/cjs/adobe/firefly.js.map +0 -1
- package/lib/cjs/anthropic/index.js +0 -64
- package/lib/cjs/anthropic/index.js.map +0 -1
- package/lib/cjs/azure/azure_foundry.js +0 -432
- package/lib/cjs/azure/azure_foundry.js.map +0 -1
- package/lib/cjs/bedrock/converse.js +0 -359
- package/lib/cjs/bedrock/converse.js.map +0 -1
- package/lib/cjs/bedrock/index.js +0 -1508
- package/lib/cjs/bedrock/index.js.map +0 -1
- package/lib/cjs/bedrock/nova-image-payload.js +0 -207
- package/lib/cjs/bedrock/nova-image-payload.js.map +0 -1
- package/lib/cjs/bedrock/payloads.js +0 -3
- package/lib/cjs/bedrock/s3.js +0 -107
- package/lib/cjs/bedrock/s3.js.map +0 -1
- package/lib/cjs/bedrock/twelvelabs.js +0 -87
- package/lib/cjs/bedrock/twelvelabs.js.map +0 -1
- package/lib/cjs/groq/index.js +0 -328
- package/lib/cjs/groq/index.js.map +0 -1
- package/lib/cjs/huggingface_ie.js +0 -201
- package/lib/cjs/huggingface_ie.js.map +0 -1
- package/lib/cjs/index.js +0 -32
- package/lib/cjs/index.js.map +0 -1
- package/lib/cjs/mistral/index.js +0 -176
- package/lib/cjs/mistral/index.js.map +0 -1
- package/lib/cjs/mistral/types.js +0 -83
- package/lib/cjs/mistral/types.js.map +0 -1
- package/lib/cjs/openai/azure_openai.js +0 -72
- package/lib/cjs/openai/azure_openai.js.map +0 -1
- package/lib/cjs/openai/index.js +0 -1135
- package/lib/cjs/openai/index.js.map +0 -1
- package/lib/cjs/openai/openai.js +0 -21
- package/lib/cjs/openai/openai.js.map +0 -1
- package/lib/cjs/openai/openai_compatible.js +0 -63
- package/lib/cjs/openai/openai_compatible.js.map +0 -1
- package/lib/cjs/openai/openai_format.js +0 -131
- package/lib/cjs/openai/openai_format.js.map +0 -1
- package/lib/cjs/package.json +0 -3
- package/lib/cjs/replicate.js +0 -275
- package/lib/cjs/replicate.js.map +0 -1
- package/lib/cjs/shared/claude-messages.js +0 -737
- package/lib/cjs/shared/claude-messages.js.map +0 -1
- package/lib/cjs/shared/claude-thinking.js +0 -60
- package/lib/cjs/shared/claude-thinking.js.map +0 -1
- package/lib/cjs/test-driver/TestErrorCompletionStream.js +0 -20
- package/lib/cjs/test-driver/TestErrorCompletionStream.js.map +0 -1
- package/lib/cjs/test-driver/TestValidationErrorCompletionStream.js +0 -24
- package/lib/cjs/test-driver/TestValidationErrorCompletionStream.js.map +0 -1
- package/lib/cjs/test-driver/index.js +0 -109
- package/lib/cjs/test-driver/index.js.map +0 -1
- package/lib/cjs/test-driver/utils.js +0 -30
- package/lib/cjs/test-driver/utils.js.map +0 -1
- package/lib/cjs/togetherai/index.js +0 -126
- package/lib/cjs/togetherai/index.js.map +0 -1
- package/lib/cjs/togetherai/interfaces.js +0 -3
- package/lib/cjs/togetherai/interfaces.js.map +0 -1
- package/lib/cjs/vertexai/debug.js +0 -12
- package/lib/cjs/vertexai/debug.js.map +0 -1
- package/lib/cjs/vertexai/embeddings/embeddings-image.js +0 -27
- package/lib/cjs/vertexai/embeddings/embeddings-image.js.map +0 -1
- package/lib/cjs/vertexai/embeddings/embeddings-text.js +0 -23
- package/lib/cjs/vertexai/embeddings/embeddings-text.js.map +0 -1
- package/lib/cjs/vertexai/index.js +0 -644
- package/lib/cjs/vertexai/index.js.map +0 -1
- package/lib/cjs/vertexai/models/claude.js +0 -72
- package/lib/cjs/vertexai/models/claude.js.map +0 -1
- package/lib/cjs/vertexai/models/gemini.js +0 -864
- package/lib/cjs/vertexai/models/gemini.js.map +0 -1
- package/lib/cjs/vertexai/models/imagen.js +0 -303
- package/lib/cjs/vertexai/models/imagen.js.map +0 -1
- package/lib/cjs/vertexai/models/llama.js +0 -183
- package/lib/cjs/vertexai/models/llama.js.map +0 -1
- package/lib/cjs/vertexai/models.js +0 -35
- package/lib/cjs/vertexai/models.js.map +0 -1
- package/lib/cjs/watsonx/index.js +0 -161
- package/lib/cjs/watsonx/index.js.map +0 -1
- package/lib/cjs/watsonx/interfaces.js +0 -3
- package/lib/cjs/xai/index.js +0 -65
- package/lib/cjs/xai/index.js.map +0 -1
- package/lib/esm/adobe/firefly.js +0 -116
- package/lib/esm/adobe/firefly.js.map +0 -1
- package/lib/esm/anthropic/index.js.map +0 -1
- package/lib/esm/azure/azure_foundry.js.map +0 -1
- package/lib/esm/bedrock/converse.js.map +0 -1
- package/lib/esm/bedrock/index.js.map +0 -1
- package/lib/esm/bedrock/nova-image-payload.js.map +0 -1
- package/lib/esm/bedrock/payloads.js.map +0 -1
- package/lib/esm/bedrock/s3.js.map +0 -1
- package/lib/esm/bedrock/twelvelabs.js.map +0 -1
- package/lib/esm/groq/index.js.map +0 -1
- package/lib/esm/huggingface_ie.js.map +0 -1
- package/lib/esm/index.js +0 -16
- package/lib/esm/index.js.map +0 -1
- package/lib/esm/mistral/index.js.map +0 -1
- package/lib/esm/mistral/types.js.map +0 -1
- package/lib/esm/openai/azure_openai.js.map +0 -1
- package/lib/esm/openai/index.js.map +0 -1
- package/lib/esm/openai/openai.js +0 -14
- package/lib/esm/openai/openai.js.map +0 -1
- package/lib/esm/openai/openai_compatible.js.map +0 -1
- package/lib/esm/openai/openai_format.js.map +0 -1
- package/lib/esm/replicate.js.map +0 -1
- package/lib/esm/shared/claude-messages.js.map +0 -1
- package/lib/esm/shared/claude-thinking.js.map +0 -1
- package/lib/esm/test-driver/TestErrorCompletionStream.js +0 -16
- package/lib/esm/test-driver/TestErrorCompletionStream.js.map +0 -1
- package/lib/esm/test-driver/TestValidationErrorCompletionStream.js.map +0 -1
- package/lib/esm/test-driver/index.js.map +0 -1
- package/lib/esm/test-driver/utils.js.map +0 -1
- package/lib/esm/togetherai/index.js +0 -122
- package/lib/esm/togetherai/index.js.map +0 -1
- package/lib/esm/togetherai/interfaces.js.map +0 -1
- package/lib/esm/vertexai/debug.js.map +0 -1
- package/lib/esm/vertexai/embeddings/embeddings-image.js +0 -24
- package/lib/esm/vertexai/embeddings/embeddings-image.js.map +0 -1
- package/lib/esm/vertexai/embeddings/embeddings-text.js +0 -20
- package/lib/esm/vertexai/embeddings/embeddings-text.js.map +0 -1
- package/lib/esm/vertexai/index.js.map +0 -1
- package/lib/esm/vertexai/models/claude.js.map +0 -1
- package/lib/esm/vertexai/models/gemini.js.map +0 -1
- package/lib/esm/vertexai/models/imagen.js.map +0 -1
- package/lib/esm/vertexai/models/llama.js.map +0 -1
- package/lib/esm/vertexai/models.js.map +0 -1
- package/lib/esm/watsonx/index.js.map +0 -1
- package/lib/esm/xai/index.js.map +0 -1
- package/lib/types/adobe/firefly.d.ts.map +0 -1
- package/lib/types/anthropic/index.d.ts.map +0 -1
- package/lib/types/azure/azure_foundry.d.ts.map +0 -1
- package/lib/types/bedrock/converse.d.ts.map +0 -1
- package/lib/types/bedrock/index.d.ts.map +0 -1
- package/lib/types/bedrock/nova-image-payload.d.ts.map +0 -1
- package/lib/types/bedrock/payloads.d.ts.map +0 -1
- package/lib/types/bedrock/s3.d.ts.map +0 -1
- package/lib/types/bedrock/twelvelabs.d.ts.map +0 -1
- package/lib/types/groq/index.d.ts.map +0 -1
- package/lib/types/huggingface_ie.d.ts.map +0 -1
- package/lib/types/index.d.ts +0 -16
- package/lib/types/index.d.ts.map +0 -1
- package/lib/types/mistral/index.d.ts.map +0 -1
- package/lib/types/mistral/types.d.ts.map +0 -1
- package/lib/types/openai/azure_openai.d.ts.map +0 -1
- package/lib/types/openai/index.d.ts.map +0 -1
- package/lib/types/openai/openai.d.ts.map +0 -1
- package/lib/types/openai/openai_compatible.d.ts.map +0 -1
- package/lib/types/openai/openai_format.d.ts.map +0 -1
- package/lib/types/replicate.d.ts.map +0 -1
- package/lib/types/shared/claude-messages.d.ts.map +0 -1
- package/lib/types/shared/claude-thinking.d.ts.map +0 -1
- package/lib/types/test-driver/TestErrorCompletionStream.d.ts.map +0 -1
- package/lib/types/test-driver/TestValidationErrorCompletionStream.d.ts.map +0 -1
- package/lib/types/test-driver/index.d.ts.map +0 -1
- package/lib/types/test-driver/utils.d.ts.map +0 -1
- package/lib/types/togetherai/index.d.ts +0 -23
- package/lib/types/togetherai/index.d.ts.map +0 -1
- package/lib/types/togetherai/interfaces.d.ts.map +0 -1
- package/lib/types/vertexai/debug.d.ts +0 -2
- package/lib/types/vertexai/debug.d.ts.map +0 -1
- package/lib/types/vertexai/embeddings/embeddings-image.d.ts +0 -11
- package/lib/types/vertexai/embeddings/embeddings-image.d.ts.map +0 -1
- package/lib/types/vertexai/embeddings/embeddings-text.d.ts +0 -10
- package/lib/types/vertexai/embeddings/embeddings-text.d.ts.map +0 -1
- package/lib/types/vertexai/index.d.ts.map +0 -1
- package/lib/types/vertexai/models/claude.d.ts.map +0 -1
- package/lib/types/vertexai/models/gemini.d.ts.map +0 -1
- package/lib/types/vertexai/models/imagen.d.ts.map +0 -1
- package/lib/types/vertexai/models/llama.d.ts.map +0 -1
- package/lib/types/vertexai/models.d.ts +0 -20
- package/lib/types/vertexai/models.d.ts.map +0 -1
- package/lib/types/watsonx/index.d.ts.map +0 -1
- package/lib/types/watsonx/interfaces.d.ts.map +0 -1
- package/lib/types/xai/index.d.ts.map +0 -1
- package/src/vertexai/embeddings/embeddings-image.ts +0 -52
- package/src/vertexai/embeddings/embeddings-text.ts +0 -54
- /package/lib/{esm/bedrock → bedrock}/payloads.js +0 -0
- /package/lib/{esm/mistral → mistral}/types.js +0 -0
- /package/lib/{esm/togetherai → togetherai}/interfaces.js +0 -0
- /package/lib/{types/watsonx → watsonx}/interfaces.d.ts +0 -0
- /package/lib/{esm/watsonx → watsonx}/interfaces.js +0 -0
|
@@ -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,9 +608,10 @@ 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
617
|
// Token-accounting mismatch: warn-level diagnostic (the call still
|
|
@@ -505,7 +620,7 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
505
620
|
// log aggregators — see the recoverable-tool-call sites below.
|
|
506
621
|
driver.logger.warn(
|
|
507
622
|
{ total: tokenUsage.total, prompt: tokenUsage.prompt, result: tokenUsage.result },
|
|
508
|
-
|
|
623
|
+
'[VertexAI] Gemini token usage mismatch: total does not equal prompt + result',
|
|
509
624
|
);
|
|
510
625
|
}
|
|
511
626
|
|
|
@@ -516,10 +631,14 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
516
631
|
return tokenUsage;
|
|
517
632
|
}
|
|
518
633
|
|
|
519
|
-
async requestTextCompletion(
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
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) {
|
|
523
642
|
region = splits[1];
|
|
524
643
|
}
|
|
525
644
|
const modelName = splits[splits.length - 1];
|
|
@@ -538,12 +657,12 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
538
657
|
prompt.contents = conversation;
|
|
539
658
|
|
|
540
659
|
// TODO: Remove hack, use global endpoint manually if needed.
|
|
541
|
-
if (options.model.includes(
|
|
542
|
-
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
|
|
543
662
|
}
|
|
544
663
|
|
|
545
664
|
const model_options = options.model_options as VertexAIGeminiOptions | undefined;
|
|
546
|
-
const client = driver.getGoogleGenAIClient(region, model_options?.flex ?? false);
|
|
665
|
+
const client = driver.getGoogleGenAIClient(region, model_options?.flex ?? false, options.httpTimeout);
|
|
547
666
|
|
|
548
667
|
const payload = getGeminiPayload(options, prompt);
|
|
549
668
|
const response = await client.models.generateContent(payload);
|
|
@@ -551,22 +670,33 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
551
670
|
const token_usage: ExecutionTokenUsage = this.usageMetadataToTokenUsage(driver, response.usageMetadata);
|
|
552
671
|
|
|
553
672
|
let tool_use: ToolUse[] | undefined;
|
|
554
|
-
let finish_reason: string | undefined, result:
|
|
555
|
-
const candidate = response.candidates
|
|
673
|
+
let finish_reason: string | undefined, result: CompletionResult[] | undefined;
|
|
674
|
+
const candidate = response.candidates?.[0];
|
|
556
675
|
if (candidate) {
|
|
557
676
|
switch (candidate.finishReason) {
|
|
558
|
-
case FinishReason.MAX_TOKENS:
|
|
559
|
-
|
|
560
|
-
|
|
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;
|
|
561
685
|
}
|
|
562
686
|
const content = candidate.content;
|
|
563
687
|
|
|
564
688
|
// Check for unsupported finish reasons, but allow recoverable tool call issues
|
|
565
689
|
const isRecoverableToolCall = recoverableToolCallReasons.includes(candidate.finishReason as string);
|
|
566
|
-
if (
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
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
|
+
);
|
|
570
700
|
}
|
|
571
701
|
|
|
572
702
|
if (content) {
|
|
@@ -580,7 +710,7 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
580
710
|
if (isRecoverableToolCall && tool_use && tool_use.length > 0) {
|
|
581
711
|
driver.logger.warn(
|
|
582
712
|
`[Gemini] Recoverable tool call issue (${candidate.finishReason}): ` +
|
|
583
|
-
|
|
713
|
+
`Model tried to call undeclared tool(s): ${tool_use.map((t) => t.tool_name).join(', ')}`,
|
|
584
714
|
);
|
|
585
715
|
}
|
|
586
716
|
|
|
@@ -589,10 +719,8 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
589
719
|
}
|
|
590
720
|
}
|
|
591
721
|
|
|
592
|
-
|
|
593
|
-
|
|
594
722
|
if (tool_use) {
|
|
595
|
-
finish_reason =
|
|
723
|
+
finish_reason = 'tool_use';
|
|
596
724
|
}
|
|
597
725
|
|
|
598
726
|
// Increment turn counter for deferred stripping
|
|
@@ -603,7 +731,7 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
603
731
|
const stripOptions = {
|
|
604
732
|
keepForTurns: options.stripImagesAfterTurns ?? Infinity,
|
|
605
733
|
currentTurn,
|
|
606
|
-
textMaxTokens: options.stripTextMaxTokens
|
|
734
|
+
textMaxTokens: options.stripTextMaxTokens,
|
|
607
735
|
};
|
|
608
736
|
let processedConversation = stripBase64ImagesFromConversation(conversation, stripOptions);
|
|
609
737
|
|
|
@@ -620,19 +748,23 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
620
748
|
const finalConversation = storeSystemInConversation(processedConversation, prompt.system);
|
|
621
749
|
|
|
622
750
|
return {
|
|
623
|
-
result: result && result.length > 0 ? result : [{ type:
|
|
751
|
+
result: result && result.length > 0 ? result : [{ type: 'text' as const, value: '' }],
|
|
624
752
|
token_usage: token_usage,
|
|
625
753
|
finish_reason: finish_reason,
|
|
626
754
|
original_response: options.include_original_response ? response : undefined,
|
|
627
755
|
conversation: finalConversation,
|
|
628
|
-
tool_use
|
|
756
|
+
tool_use,
|
|
629
757
|
} satisfies Completion;
|
|
630
758
|
}
|
|
631
759
|
|
|
632
|
-
async requestTextCompletionStream(
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
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) {
|
|
636
768
|
region = splits[1];
|
|
637
769
|
}
|
|
638
770
|
const modelName = splits[splits.length - 1];
|
|
@@ -651,12 +783,12 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
651
783
|
const conversation = updateConversation(options.conversation, prompt.contents);
|
|
652
784
|
prompt.contents = conversation;
|
|
653
785
|
|
|
654
|
-
if (options.model.includes(
|
|
655
|
-
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
|
|
656
788
|
}
|
|
657
789
|
|
|
658
790
|
const model_options = options.model_options as VertexAIGeminiOptions | undefined;
|
|
659
|
-
const client = driver.getGoogleGenAIClient(region, model_options?.flex ?? false);
|
|
791
|
+
const client = driver.getGoogleGenAIClient(region, model_options?.flex ?? false, options.httpTimeout);
|
|
660
792
|
|
|
661
793
|
const payload = getGeminiPayload(options, prompt);
|
|
662
794
|
const response = await client.models.generateContentStream(payload);
|
|
@@ -668,23 +800,34 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
668
800
|
let tool_use: ToolUse[] | undefined;
|
|
669
801
|
let finish_reason: string | undefined;
|
|
670
802
|
switch (candidate.finishReason) {
|
|
671
|
-
case FinishReason.MAX_TOKENS:
|
|
672
|
-
|
|
673
|
-
|
|
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;
|
|
674
811
|
}
|
|
675
812
|
// Check for unsupported finish reasons, but allow recoverable tool call issues
|
|
676
813
|
const isRecoverableToolCall = recoverableToolCallReasons.includes(candidate.finishReason as string);
|
|
677
|
-
if (
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
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
|
+
);
|
|
681
824
|
}
|
|
682
825
|
if (candidate.content?.role === 'model') {
|
|
683
826
|
// Collect all parts in order (text and images)
|
|
684
827
|
const combinedResults = extractCompletionResults(candidate.content);
|
|
685
828
|
tool_use = collectToolUseParts(candidate.content);
|
|
686
829
|
if (tool_use) {
|
|
687
|
-
finish_reason =
|
|
830
|
+
finish_reason = 'tool_use';
|
|
688
831
|
// Log warning for recoverable tool call issues — see the
|
|
689
832
|
// matching site in `requestTextCompletion` above for why
|
|
690
833
|
// we route through the driver's logger instead of
|
|
@@ -692,7 +835,7 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
692
835
|
if (isRecoverableToolCall) {
|
|
693
836
|
driver.logger.warn(
|
|
694
837
|
`[Gemini] Recoverable tool call issue (${candidate.finishReason}): ` +
|
|
695
|
-
|
|
838
|
+
`Model tried to call undeclared tool(s): ${tool_use.map((t) => t.tool_name).join(', ')}`,
|
|
696
839
|
);
|
|
697
840
|
}
|
|
698
841
|
}
|
|
@@ -707,8 +850,10 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
707
850
|
}
|
|
708
851
|
//No normal output, returning block reason if it exists.
|
|
709
852
|
return {
|
|
710
|
-
result: item.promptFeedback?.blockReasonMessage
|
|
711
|
-
|
|
853
|
+
result: item.promptFeedback?.blockReasonMessage
|
|
854
|
+
? [{ type: 'text' as const, value: item.promptFeedback.blockReasonMessage }]
|
|
855
|
+
: [],
|
|
856
|
+
finish_reason: item.promptFeedback?.blockReason ?? '',
|
|
712
857
|
token_usage: token_usage,
|
|
713
858
|
};
|
|
714
859
|
});
|
|
@@ -718,11 +863,11 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
718
863
|
|
|
719
864
|
/**
|
|
720
865
|
* Format Google API errors into LlumiverseError with proper status codes and retryability.
|
|
721
|
-
*
|
|
866
|
+
*
|
|
722
867
|
* Google API errors follow AIP-193 standard:
|
|
723
868
|
* - ApiError.status: HTTP status code
|
|
724
869
|
* - ApiError.message: Error message
|
|
725
|
-
*
|
|
870
|
+
*
|
|
726
871
|
* Common error codes:
|
|
727
872
|
* - 400 (INVALID_ARGUMENT): Invalid request parameters
|
|
728
873
|
* - 401 (UNAUTHENTICATED): Authentication required
|
|
@@ -732,15 +877,11 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
732
877
|
* - 500 (INTERNAL): Internal server error
|
|
733
878
|
* - 503 (UNAVAILABLE): Service temporarily unavailable
|
|
734
879
|
* - 504 (DEADLINE_EXCEEDED): Request timeout
|
|
735
|
-
*
|
|
880
|
+
*
|
|
736
881
|
* @see https://google.aip.dev/193
|
|
737
882
|
* @see https://docs.cloud.google.com/vertex-ai/generative-ai/docs/model-reference/api-errors
|
|
738
883
|
*/
|
|
739
|
-
formatLlumiverseError(
|
|
740
|
-
_driver: VertexAIDriver,
|
|
741
|
-
error: unknown,
|
|
742
|
-
context: LlumiverseErrorContext
|
|
743
|
-
): LlumiverseError {
|
|
884
|
+
formatLlumiverseError(_driver: VertexAIDriver, error: unknown, context: LlumiverseErrorContext): LlumiverseError {
|
|
744
885
|
// Check if it's a Google API error with status code
|
|
745
886
|
const isApiError = this.isGoogleApiError(error);
|
|
746
887
|
|
|
@@ -750,11 +891,11 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
750
891
|
throw error;
|
|
751
892
|
}
|
|
752
893
|
|
|
753
|
-
const apiError = error
|
|
894
|
+
const apiError = error;
|
|
754
895
|
const httpStatusCode = apiError.status;
|
|
755
896
|
|
|
756
897
|
// Extract error message
|
|
757
|
-
const message = apiError.message
|
|
898
|
+
const message = apiError.message;
|
|
758
899
|
|
|
759
900
|
// Build user-facing message with status code
|
|
760
901
|
let userMessage = message;
|
|
@@ -765,7 +906,7 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
765
906
|
}
|
|
766
907
|
|
|
767
908
|
// Determine retryability based on Google error codes
|
|
768
|
-
const retryable = this.isGeminiErrorRetryable(httpStatusCode);
|
|
909
|
+
const retryable = this.isGeminiErrorRetryable(httpStatusCode, message);
|
|
769
910
|
|
|
770
911
|
// Extract error name/type from message if present
|
|
771
912
|
const errorName = this.extractErrorName(message);
|
|
@@ -776,34 +917,36 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
776
917
|
context,
|
|
777
918
|
error,
|
|
778
919
|
httpStatusCode,
|
|
779
|
-
errorName
|
|
920
|
+
errorName,
|
|
780
921
|
);
|
|
781
922
|
}
|
|
782
923
|
|
|
783
924
|
/**
|
|
784
925
|
* Type guard to check if error is a Google API error.
|
|
785
926
|
*/
|
|
786
|
-
private isGoogleApiError(error: unknown): error is
|
|
927
|
+
private isGoogleApiError(error: unknown): error is GoogleApiErrorLike {
|
|
787
928
|
return (
|
|
788
929
|
error !== null &&
|
|
789
930
|
typeof error === 'object' &&
|
|
790
931
|
'status' in error &&
|
|
791
|
-
typeof (error as
|
|
792
|
-
'message' in error
|
|
932
|
+
typeof (error as { status?: unknown }).status === 'number' &&
|
|
933
|
+
'message' in error &&
|
|
934
|
+
typeof (error as { message?: unknown }).message === 'string'
|
|
793
935
|
);
|
|
794
936
|
}
|
|
795
937
|
|
|
796
938
|
/**
|
|
797
939
|
* Determine if a Google API error is retryable based on HTTP status code.
|
|
798
|
-
*
|
|
940
|
+
*
|
|
799
941
|
* Retryable errors (per Google AIP-194):
|
|
800
942
|
* - 408 (REQUEST_TIMEOUT): Request timeout
|
|
943
|
+
* - 499 (CANCELLED / Client Closed Request): Transport cancellation
|
|
801
944
|
* - 429 (RESOURCE_EXHAUSTED): Rate limit exceeded, quota exhausted
|
|
802
945
|
* - 500 (INTERNAL): Internal server error
|
|
803
946
|
* - 502 (BAD_GATEWAY): Bad gateway
|
|
804
947
|
* - 503 (UNAVAILABLE): Service temporarily unavailable
|
|
805
948
|
* - 504 (DEADLINE_EXCEEDED): Gateway timeout
|
|
806
|
-
*
|
|
949
|
+
*
|
|
807
950
|
* Non-retryable errors:
|
|
808
951
|
* - 400 (INVALID_ARGUMENT): Invalid request parameters
|
|
809
952
|
* - 401 (UNAUTHENTICATED): Authentication required
|
|
@@ -811,19 +954,48 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
811
954
|
* - 404 (NOT_FOUND): Resource not found
|
|
812
955
|
* - 409 (CONFLICT): Resource conflict
|
|
813
956
|
* - Other 4xx client errors
|
|
814
|
-
*
|
|
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
|
+
*
|
|
815
964
|
* @param httpStatusCode - The HTTP status code from the API error
|
|
965
|
+
* @param message - The error message (used to detect transient 400 sub-cases)
|
|
816
966
|
* @returns True if retryable, false if not retryable, undefined if unknown
|
|
817
967
|
*/
|
|
818
|
-
private isGeminiErrorRetryable(httpStatusCode: number): boolean | undefined {
|
|
968
|
+
private isGeminiErrorRetryable(httpStatusCode: number, message?: string): boolean | undefined {
|
|
969
|
+
if (message && this.isNonRetryableAuthError(message)) return false;
|
|
970
|
+
|
|
819
971
|
// Retryable status codes
|
|
820
972
|
if (httpStatusCode === 408) return true; // Request timeout
|
|
821
973
|
if (httpStatusCode === 429) return true; // Rate limit/quota
|
|
974
|
+
if (httpStatusCode === 499) return true; // Client closed / operation cancelled
|
|
822
975
|
if (httpStatusCode === 502) return true; // Bad gateway
|
|
823
976
|
if (httpStatusCode === 503) return true; // Service unavailable
|
|
824
977
|
if (httpStatusCode === 504) return true; // Gateway timeout
|
|
825
978
|
if (httpStatusCode >= 500 && httpStatusCode < 600) return true; // Other 5xx server errors
|
|
826
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
|
+
|
|
827
999
|
// Non-retryable 4xx client errors
|
|
828
1000
|
if (httpStatusCode >= 400 && httpStatusCode < 500) return false;
|
|
829
1001
|
|
|
@@ -831,6 +1003,11 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
831
1003
|
return undefined;
|
|
832
1004
|
}
|
|
833
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
|
+
|
|
834
1011
|
/**
|
|
835
1012
|
* Extract error type name from error message.
|
|
836
1013
|
* Google errors often include the error type in the message.
|
|
@@ -839,9 +1016,10 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
839
1016
|
private extractErrorName(message: string): string | undefined {
|
|
840
1017
|
// Common Google error patterns
|
|
841
1018
|
const patterns = [
|
|
842
|
-
/^([
|
|
1019
|
+
/^Error code ([a-zA-Z0-9_-]+):/, // "Error code invalid_grant: message"
|
|
1020
|
+
/^([A-Z_]+):/, // "ERROR_NAME: message"
|
|
843
1021
|
/\[([A-Z_]+)\]/, // "[ERROR_NAME] message"
|
|
844
|
-
/^(\w+Error):/,
|
|
1022
|
+
/^(\w+Error):/, // "ErrorTypeError: message"
|
|
845
1023
|
];
|
|
846
1024
|
|
|
847
1025
|
for (const pattern of patterns) {
|
|
@@ -853,31 +1031,30 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
853
1031
|
|
|
854
1032
|
return undefined;
|
|
855
1033
|
}
|
|
856
|
-
|
|
857
1034
|
}
|
|
858
1035
|
|
|
859
|
-
|
|
860
1036
|
/**
|
|
861
1037
|
* Converts functionCall and functionResponse parts to text parts in Gemini Content[].
|
|
862
1038
|
* Preserves tool call information while removing structured parts that require
|
|
863
1039
|
* tools/toolConfig to be defined in the API request.
|
|
864
1040
|
*/
|
|
865
1041
|
export function convertGeminiFunctionPartsToText(contents: Content[]): Content[] {
|
|
866
|
-
return contents.map(content => {
|
|
1042
|
+
return contents.map((content) => {
|
|
867
1043
|
if (!content.parts) return content;
|
|
868
|
-
const hasFunctionParts = content.parts.some(p => p.functionCall || p.functionResponse);
|
|
1044
|
+
const hasFunctionParts = content.parts.some((p) => p.functionCall || p.functionResponse);
|
|
869
1045
|
if (!hasFunctionParts) return content;
|
|
870
1046
|
|
|
871
|
-
const newParts = content.parts.map(part => {
|
|
1047
|
+
const newParts = content.parts.map((part) => {
|
|
872
1048
|
if (part.functionCall) {
|
|
873
1049
|
const argsStr = part.functionCall.args ? JSON.stringify(part.functionCall.args) : '';
|
|
874
|
-
const truncated = argsStr.length > 500 ? argsStr.substring(0, 500)
|
|
1050
|
+
const truncated = argsStr.length > 500 ? `${argsStr.substring(0, 500)}...` : argsStr;
|
|
875
1051
|
return { text: `[Tool call: ${part.functionCall.name}(${truncated})]` };
|
|
876
1052
|
}
|
|
877
1053
|
if (part.functionResponse) {
|
|
878
1054
|
const respStr = part.functionResponse.response
|
|
879
|
-
? JSON.stringify(part.functionResponse.response)
|
|
880
|
-
|
|
1055
|
+
? JSON.stringify(part.functionResponse.response)
|
|
1056
|
+
: 'No response';
|
|
1057
|
+
const truncated = respStr.length > 500 ? `${respStr.substring(0, 500)}...` : respStr;
|
|
881
1058
|
return { text: `[Tool result for ${part.functionResponse.name}: ${truncated}]` };
|
|
882
1059
|
}
|
|
883
1060
|
return part;
|
|
@@ -894,7 +1071,7 @@ function getToolDefinitions(tools: ToolDefinition[] | undefined | null): Tool |
|
|
|
894
1071
|
// For multiple tools, we have multiple functions in one tool.
|
|
895
1072
|
return {
|
|
896
1073
|
functionDeclarations: tools.map(getToolFunction),
|
|
897
|
-
}
|
|
1074
|
+
};
|
|
898
1075
|
}
|
|
899
1076
|
|
|
900
1077
|
function getToolFunction(tool: ToolDefinition): FunctionDeclaration {
|
|
@@ -917,7 +1094,7 @@ function getToolFunction(tool: ToolDefinition): FunctionDeclaration {
|
|
|
917
1094
|
function updateConversation(conversation: unknown, prompt: Content[]): Content[] {
|
|
918
1095
|
// Unwrap array if wrapped, otherwise treat as array
|
|
919
1096
|
const unwrapped = unwrapConversationArray<Content>(conversation);
|
|
920
|
-
const convArray = unwrapped ?? (conversation as Content[] || []);
|
|
1097
|
+
const convArray = unwrapped ?? ((conversation as Content[]) || []);
|
|
921
1098
|
return convArray.concat(prompt);
|
|
922
1099
|
}
|
|
923
1100
|
|
|
@@ -946,7 +1123,7 @@ function extractSystemFromConversation(conversation: unknown): Content | undefin
|
|
|
946
1123
|
function storeSystemInConversation(conversation: unknown, system: Content | undefined): unknown {
|
|
947
1124
|
if (!system) return conversation;
|
|
948
1125
|
if (typeof conversation === 'object' && conversation !== null) {
|
|
949
|
-
return { ...conversation as object, [SYSTEM_KEY]: system };
|
|
1126
|
+
return { ...(conversation as object), [SYSTEM_KEY]: system };
|
|
950
1127
|
}
|
|
951
1128
|
return conversation;
|
|
952
1129
|
}
|
|
@@ -964,10 +1141,10 @@ function storeSystemInConversation(conversation: unknown, system: Content | unde
|
|
|
964
1141
|
*/
|
|
965
1142
|
function formatFunctionResponse(response: string): JSONObject {
|
|
966
1143
|
response = response.trim();
|
|
967
|
-
if (response.startsWith(
|
|
1144
|
+
if (response.startsWith('{') && response.endsWith('}')) {
|
|
968
1145
|
try {
|
|
969
1146
|
return JSON.parse(response);
|
|
970
|
-
} catch
|
|
1147
|
+
} catch {
|
|
971
1148
|
return { output: response };
|
|
972
1149
|
}
|
|
973
1150
|
} else {
|