Skip to main content

OpenAI Activities

One activity, openai.chat.completions, sends a chat completion request to any OpenAI-compatible endpoint and returns the assistant's message.

Use it for a single prompt-and-response step: classify something, extract fields, draft text. When the step is open-ended enough that you cannot specify it in advance, use claude_agent.query instead — an agent loop reasons over several turns and calls tools on its own.

Setup

The API key is named, not inlined: apikey_secret_key is the name of a secret holding the key. A bare NAME resolves a user-scoped secret, global/NAME a global one.

Endpoint and model fall back to the worker's defaults:

Input fieldEnvironment default
base_urlMOCO_LLM_DEFAULT_BASE_URL
model_nameMOCO_LLM_DEFAULT_MODEL_NAME

A missing default is an error rather than a silent fallback, so a deployment either configures them or every workflow supplies them.

base_url is used verbatim — include the version path

Nothing is appended. Use https://my-gateway/v1, http://localhost:11434/v1 for Ollama, or https://generativelanguage.googleapis.com/v1beta/openai/ for Gemini. A bare host will not work. The same rule applies to llama_index model blocks.

Defaults

60 s timeout, 3 attempts — the platform default, and short for a real completion. Raise timeout_sec for anything longer than a sentence or two, and consider lowering max_attempts: three attempts means paying for the generation up to three times.

Applied inside the activity when you do not set them: max_tokens: 8000, temporature: 0.1. SDK-level retries are disabled; retrying is the workflow runtime's job.


openai.chat.completions

Input

FieldTypeRequiredDefaultDescription
apikey_secret_keystryesSecret name holding the API key
messageslist[object]yesThe conversation, in OpenAI chat format — see Messages
base_urlstrnoMOCO_LLM_DEFAULT_BASE_URLEndpoint base URL, version path included
model_namestrnoMOCO_LLM_DEFAULT_MODEL_NAMEModel to call
proxystrnonullProxy URL for this request
response_formatobjectnonull{type: text}, {type: json_object}, or a json_schema block for structured output
toolslist[object]nonullFunction tool definitions the model may call
tool_choicestr | objectnonullnone, auto, required, or a named tool
streamboolnofalseStream the response internally — see the note below
max_tokensintno8000Maximum tokens to generate
temporaturenumberno0.1Sampling temperature
temporature, not temperature

The field name carries a typo in the platform's input model. Spelling it correctly means the value is ignored and the default of 0.1 applies.

Messages

Each message is {role, content} with role one of system, developer, user, assistant, tool or function. content is a string, or a list of typed parts for multimodal input (text, image_url, input_audio, file). Assistant messages may carry tool_calls; tool messages carry a tool_call_id.

The shapes are exactly the OpenAI SDK's — see the chat completions API reference for the full set of fields, including tools and response_format.

Output

The assistant's message:

FieldTypeDescription
rolestrAlways "assistant"
contentstr | nullThe generated text; null when the model only called tools
tool_callslist[object] | nullTool calls the model made, each with id, type and function{name, arguments}
refusalstr | nullRefusal message, when the model declined

function.arguments is a JSON string, not an object — parse it before use.

Examples

Structured output, from moco-examples/openai-demo/src/openai-demo.yaml:

- activity:
type: openai.chat.completions
name: generate_reminders
retry_policy:
timeout_sec: 120
max_attempts: 2
input_data:
apikey_secret_key: "{{ llm_apikey_secret_key }}"
base_url: "{{ llm_base_url }}"
model_name: "{{ llm_model_name }}"
max_tokens: 800
temporature: 0.2
response_format:
type: json_object
messages:
- role: system
content: |
You are a personal assistant that reads a weather forecast and
writes short, actionable reminders for the day.
- role: user
content: "{{ forecast_text }}"
output_name: reminders
- transform:
output_data:
- parsed: "{{ json.loads(reminders['content']) }}"

Asking the model to pick a tool:

- activity:
name: route-request
type: openai.chat.completions
input_data:
apikey_secret_key: "MY_LLM_TOKEN"
model_name: "{{ llm_model_name }}"
messages:
- role: user
content: "{{ user_request }}"
tools:
- type: function
function:
name: lookup_order
description: Look up an order by its id
parameters:
type: object
properties:
order_id: {type: string}
required: [order_id]
tool_choice: auto
retry_policy:
timeout_sec: 120
output_name: routing

- transform:
condition: "{{ routing['tool_calls'] }}"
output_data:
- order_id: "{{ json.loads(routing['tool_calls'][0]['function']['arguments'])['order_id'] }}"
Streaming is internal

stream: true streams from the provider and assembles the chunks inside the activity; the workflow still receives one complete message. It exists to avoid gateway idle timeouts on long generations, not to surface tokens to the workflow — there is no relay topic here. To stream progress to a client, use claude_agent.query, which does.

TLS verification is disabled for this provider

The OpenAI client is constructed with certificate verification off. Keep that in mind when pointing base_url at anything outside a trusted network.

The proxy is not inherited

Unlike http.request, this provider ignores MOCO_HTTP_PROXY. Set proxy explicitly if the endpoint needs one.