LLM call (h2oGPTe)
A composite action that runs a single h2oGPTe LLM call — the Workflows equivalent of an LLM step. It supports three modes: direct inference, structured JSON output enforced by a schema (guided_json), and multi-turn agent mode (use_agent, with configurable accuracy, max turns, and agent type).
Authentication needs no configuration: by default the h2oGPTe URL is resolved via H2O Cloud Discovery and access tokens are minted from the platform token auto-injected into every runner (H2O_CLOUD_CLIENT_PLATFORM_TOKEN), so the call runs as the user who triggered the workflow. Pass h2ogpte_address and/or api_key only to override (e.g. to reach an h2oGPTe outside the current cloud).
Because the action runs inline, it shares the calling job's filesystem in both directions:
- The answer is written to a local file (
answer_file, defaultanswer.md) that later steps read directly — no storage round-trip. It is a file rather than an action output because step outputs are single-linekey=valuepairs meant for small values; LLM answers are long and multiline. - The
context_filesinput names local files whose contents are appended to the prompt — so earlier steps (ordownloadsteps) can assemble context for the call.
Action
id: h2ogpte-llm
name: h2oGPTe LLM Step
inputs:
prompt:
type: string
required: true
description: "User message sent to the LLM"
system_prompt:
type: string
default: ""
description: "System prompt defining the LLM's role and output format"
answer_file:
type: string
default: "answer.md"
description: "Local file path where the answer is written, readable by later steps in the job"
context_files:
type: string
default: ""
description: "Whitespace-separated local file paths appended to the prompt as context"
h2ogpte_address:
type: string
default: ""
description: "h2oGPTe URL override (default: resolved via H2O Cloud Discovery)"
api_key:
type: string
default: ""
secret: true
description: "h2oGPTe API key override (default: platform token of the triggering user)"
llm:
type: string
default: ""
description: "Model name, e.g. claude-sonnet-4-6 (empty = h2oGPTe default)"
use_agent:
type: bool
default: false
description: "Run as a multi-turn h2oGPTe agent instead of direct inference"
agent_accuracy:
type: string
default: "standard"
description: "Agent accuracy: quick, basic, standard, or maximum (agent mode only)"
agent_max_turns:
type: int
default: 5
description: "Maximum agent turns (agent mode only)"
agent_type:
type: string
default: ""
description: "Agent flavor: search, data_science, or deep_research (agent mode only)"
json_schema:
type: string
default: ""
description: "JSON schema enforcing structured output via guided_json (non-agent mode only)"
collection_id:
type: string
default: ""
description: "h2oGPTe collection ID for RAG grounding (empty = no RAG)"
timeout:
type: int
default: 600
description: "Query timeout in seconds"
outputs:
chat_session_id:
description: "h2oGPTe chat session ID of the call, for follow-ups or auditing"
value: ${{ .steps.query.outputs.chat_session_id }}
steps:
- name: "Install h2oGPTe client"
run: sudo uv pip install --system h2ogpte h2o-authn h2o-cloud-discovery
- id: query
name: "Query h2oGPTe"
timeout: "30m"
env:
H2OGPTE_ADDRESS: "${{ .inputs.h2ogpte_address }}"
H2OGPTE_API_KEY: "${{ .inputs.api_key }}"
PROMPT: "${{ .inputs.prompt }}"
SYSTEM_PROMPT: "${{ .inputs.system_prompt }}"
ANSWER_FILE: "${{ .inputs.answer_file }}"
CONTEXT_FILES: "${{ .inputs.context_files }}"
LLM: "${{ .inputs.llm }}"
USE_AGENT: "${{ .inputs.use_agent }}"
AGENT_ACCURACY: "${{ .inputs.agent_accuracy }}"
AGENT_MAX_TURNS: "${{ .inputs.agent_max_turns }}"
AGENT_TYPE: "${{ .inputs.agent_type }}"
JSON_SCHEMA: "${{ .inputs.json_schema }}"
COLLECTION_ID: "${{ .inputs.collection_id }}"
TIMEOUT: "${{ .inputs.timeout }}"
run: |
cat > llm_step.py <<'PY'
import json
import os
from h2ogpte import H2OGPTE
address = os.environ.get('H2OGPTE_ADDRESS')
api_key = os.environ.get('H2OGPTE_API_KEY')
if api_key and address:
client = H2OGPTE(address=address, api_key=api_key)
else:
# Default: resolve h2oGPTe via H2O Cloud Discovery and mint access
# tokens from the runner's auto-injected platform token, so the
# call runs as the user who triggered the workflow.
import h2o_authn
import h2o_discovery
disc = h2o_discovery.discover()
if not address:
address = disc.services['h2ogpte'].uri.rstrip('/')
token_provider = h2o_authn.TokenProvider(
refresh_token=os.environ['H2O_CLOUD_CLIENT_PLATFORM_TOKEN'],
client_id=disc.clients.get('platform').oauth2_client_id,
)
client = H2OGPTE(address=address, token_provider=token_provider)
message = os.environ['PROMPT']
for path in os.environ.get('CONTEXT_FILES', '').split():
with open(path) as f:
message += f"\n\n=== {path} ===\n{f.read()}"
collection_id = os.environ.get('COLLECTION_ID') or None
chat_session_id = client.create_chat_session(collection_id)
print(f'Chat session: {chat_session_id}')
llm_args = {}
if os.environ['USE_AGENT'] == 'true':
llm_args.update({
'use_agent': True,
'agent_accuracy': os.environ['AGENT_ACCURACY'],
'agent_max_turns': int(os.environ['AGENT_MAX_TURNS']),
})
if os.environ.get('AGENT_TYPE'):
llm_args['agent_type'] = os.environ['AGENT_TYPE']
elif os.environ.get('JSON_SCHEMA'):
llm_args.update({
'response_format': 'json_object',
'guided_json': json.loads(os.environ['JSON_SCHEMA']),
})
query_kwargs = {
'message': message,
'timeout': int(os.environ['TIMEOUT']),
}
if os.environ.get('SYSTEM_PROMPT'):
query_kwargs['system_prompt'] = os.environ['SYSTEM_PROMPT']
if os.environ.get('LLM'):
query_kwargs['llm'] = os.environ['LLM']
if llm_args:
query_kwargs['llm_args'] = llm_args
if not collection_id:
query_kwargs['rag_config'] = {'rag_type': 'llm_only'}
with client.connect(chat_session_id) as session:
reply = session.query(**query_kwargs)
if not reply or not reply.content:
raise SystemExit('h2oGPTe returned an empty response')
# reply.content can be truncated for long or agentic responses;
# the stored chat message is authoritative.
stored = client.list_chat_messages(chat_session_id, 0, 1)[-1].content
answer = stored if len(stored) > len(reply.content) else reply.content
with open(os.environ['ANSWER_FILE'], 'w') as f:
f.write(answer)
print(f'--- answer ({len(answer)} chars) ---')
print(answer)
with open(os.environ['H2O_WORKFLOWS_OUTPUT'], 'a') as f:
f.write(f'chat_session_id={chat_session_id}\n')
PY
python3 -u llm_step.py
Publish the action to make it callable from workflows.
Use it from your workflow
The answer lands in answer.md on the job filesystem, so the next step reads it directly:
id: ask-llm
name: Ask LLM
jobs:
ask:
timeout: "45m"
steps:
- uses: <workspace-id>:h2ogpte-llm@latest
id: llm
with:
prompt: "Explain the difference between AutoML and manual model tuning in two paragraphs."
llm: "claude-sonnet-4-6"
- name: Show answer
run: cat answer.md
Add system_prompt, collection_id (RAG grounding), json_schema (structured output), or the use_agent inputs listed above as needed. To feed files produced by earlier steps into the prompt, list them in context_files.
- Submit and view feedback for this page
- Send feedback about H2O Workflows to cloud-feedback@h2o.ai