id: llm-gateway-fallback-vllm-to-gemini-with-langfuse
namespace: company.ai
description: |
Production LLM Gateway routing prompts to a cost-effective local vLLM or Ollama instance,
with automatic resilience failover to Google Gemini on error or timeout, and centralized
latency, cost, and routing observability tracked in Langfuse.
inputs:
- id: prompt
type: STRING
displayName: User Prompt
description: Prompt payload to route through the LLM gateway.
defaults: "Summarize the key architectural principles of event-driven workflow
orchestration in three concise bullet points."
- id: system_prompt
type: STRING
displayName: System Prompt
description: System instructions guiding the tone and constraints of the assistant.
defaults: "You are an expert enterprise software architect. Provide direct,
concise, and structured answers."
- id: vllm_endpoint
type: STRING
displayName: Local vLLM / Ollama Base URL
description: Host endpoint for local inference (use
http://host.docker.internal:11434 when Kestra runs inside Docker).
defaults: "http://localhost:11434"
- id: vllm_model
type: STRING
displayName: Local Model Name
description: Name of the local model hosted on vLLM or Ollama (must be pulled in
advance).
defaults: "llama3.2:3b"
- id: gemini_model
type: STRING
displayName: Cloud Fallback Model
description: Google Gemini model version to serve as the high-availability failover.
defaults: "gemini-3.1-flash-lite"
triggers:
- id: on_request
type: io.kestra.plugin.core.trigger.Webhook
description: Authenticated HTTP webhook endpoint enabling upstream microservices
or apps to query the gateway.
key: "{{ secret('WEBHOOK_KEY') }}"
- id: nightly_health_check
type: io.kestra.plugin.core.trigger.Schedule
description: Automated sanity check validating endpoint availability and
fallback resilience. Shipped disabled by default.
cron: "0 8 * * 1-5"
disabled: true
tasks:
- id: call_local_vllm
type: io.kestra.plugin.ai.completion.ChatCompletion
description: Query the local vLLM or Ollama model first for fast, zero-egress,
zero-token-cost inference.
allowFailure: true
timeout: PT15S
provider:
type: io.kestra.plugin.ai.provider.Ollama
endpoint: "{{ inputs.vllm_endpoint }}"
modelName: "{{ inputs.vllm_model }}"
messages:
- type: SYSTEM
content: "{{ inputs.system_prompt }}"
- type: USER
content: "{{ inputs.prompt }}"
- id: check_fallback
type: io.kestra.plugin.core.flow.If
description: Evaluate if local inference timed out, failed, or was unreachable,
and route to Google Gemini.
condition: "{{ outputs.call_local_vllm.textOutput is not defined }}"
then:
- id: call_gemini_fallback
type: io.kestra.plugin.ai.completion.ChatCompletion
description: Cloud fallback querying Google Gemini to maintain uptime during
local server outages.
provider:
type: io.kestra.plugin.ai.provider.GoogleGemini
modelName: "{{ inputs.gemini_model }}"
apiKey: "{{ secret('GEMINI_API_KEY') }}"
messages:
- type: SYSTEM
content: "{{ inputs.system_prompt }}"
- type: USER
content: "{{ inputs.prompt }}"
- id: notify_slack_fallback
type: io.kestra.plugin.slack.notifications.SlackIncomingWebhook
description: Alert AI engineers on Slack when the gateway triggers a cloud failover.
url: "{{ secret('SLACK_WEBHOOK_URL') }}"
payload: |
{
"text": "⚠️ *LLM Gateway Fallback Alert*: Local inference timed out or failed on `{{ inputs.vllm_model }}`. Execution `<{{ (kestra.url ?? 'http://localhost:8080/') ~ 'ui/' ~ execution.namespace ~ '/executions/' ~ execution.id }}|{{ execution.id }}>` seamlessly failed over to Google Gemini (`{{ inputs.gemini_model }}`)."
}
else:
- id: log_local_success
type: io.kestra.plugin.core.log.Log
description: Record zero-cost local inference completion.
message: "✅ Local vLLM/Ollama inference succeeded with model {{
inputs.vllm_model }}."
- id: record_langfuse_trace
type: io.kestra.plugin.scripts.python.Script
description: Push unified telemetry, prompt-response logs, and routing metadata
to Langfuse for LLMOps observability.
containerImage: python:3.11-slim
beforeCommands:
- pip install -q --no-cache-dir "langfuse>=2.0.0"
env:
LANGFUSE_PUBLIC_KEY: "{{ secret('LANGFUSE_PUBLIC_KEY') }}"
LANGFUSE_SECRET_KEY: "{{ secret('LANGFUSE_SECRET_KEY') }}"
LANGFUSE_HOST: "{{ secret('LANGFUSE_HOST') ?? 'https://cloud.langfuse.com' }}"
PROMPT: "{{ inputs.prompt }}"
RESPONSE_TEXT: "{{ outputs.call_gemini_fallback.textOutput ??
outputs.call_local_vllm.textOutput ?? 'No response returned' }}"
PROVIDER_USED: "{{ outputs.call_local_vllm.textOutput is defined ? 'ollama_vllm'
: 'google_gemini' }}"
FALLBACK_TRIGGERED: "{{ outputs.call_local_vllm.textOutput is not defined ?
'true' : 'false' }}"
MODEL_USED: "{{ outputs.call_local_vllm.textOutput is defined ?
inputs.vllm_model : inputs.gemini_model }}"
EXECUTION_ID: "{{ execution.id }}"
script: |
import os
import json
from langfuse import Langfuse
public_key = os.environ.get("LANGFUSE_PUBLIC_KEY")
secret_key = os.environ.get("LANGFUSE_SECRET_KEY")
host = os.environ.get("LANGFUSE_HOST", "https://cloud.langfuse.com")
langfuse = Langfuse(public_key=public_key, secret_key=secret_key, host=host)
trace = langfuse.trace(
name="llm-gateway-routing",
input=os.environ.get("PROMPT"),
output=os.environ.get("RESPONSE_TEXT"),
metadata={
"provider": os.environ.get("PROVIDER_USED"),
"fallback_triggered": os.environ.get("FALLBACK_TRIGGERED") == "true",
"model": os.environ.get("MODEL_USED"),
"execution_id": os.environ.get("EXECUTION_ID"),
},
)
langfuse.flush()
trace_url = f"{host.rstrip('/')}/trace/{trace.id}"
print(f"Langfuse trace recorded: {trace_url}")
print("::" + json.dumps({"outputs": {"trace_id": trace.id, "trace_url": trace_url}}) + "::")
- id: gateway_output
type: io.kestra.plugin.core.output.OutputValues
description: Expose final response text and routing telemetry as typed Kestra outputs.
values:
response: "{{ outputs.call_gemini_fallback.textOutput ??
outputs.call_local_vllm.textOutput }}"
provider_used: "{{ outputs.call_local_vllm.textOutput is defined ? 'ollama_vllm'
: 'google_gemini' }}"
model_used: "{{ outputs.call_local_vllm.textOutput is defined ?
inputs.vllm_model : inputs.gemini_model }}"
fallback_triggered: "{{ outputs.call_local_vllm.textOutput is not defined }}"
langfuse_trace_url: "{{ outputs.record_langfuse_trace.vars.trace_url }}"
errors:
- id: alert_gateway_crash
type: io.kestra.plugin.slack.notifications.SlackIncomingWebhook
description: High-severity alert dispatched if both primary and fallback providers fail.
url: "{{ secret('SLACK_WEBHOOK_URL') }}"
payload: |
{
"text": "🔥 *Fatal Outage in LLM Gateway* for execution `<{{ (kestra.url ?? 'http://localhost:8080/') ~ 'ui/' ~ execution.namespace ~ '/executions/' ~ execution.id }}|{{ execution.id }}>`. Both local inference and cloud fallback failed."
}