Skip to content

LLM Integration

Use AI for text classification, generation, and decision-making within your workflows.

Overview

SideButton's workflow engine ships three LLM steps, backed by your choice of provider:

  • Classification (llm.classify) — Categorize text into predefined buckets
  • Generation (llm.generate) — Create text based on prompts
  • Decision (llm.decide) — Pick one action from a list, for branching workflows

Setup

Supported Providers

ProviderConfigurationNotes
OpenAIOPENAI_API_KEYDefault provider
AnthropicANTHROPIC_API_KEYClaude models
Ollamanone — localNo API key. Defaults to http://localhost:11434/api/generate and model llama2; override both in the LLM settings

Configuration

The LLM configuration has four fields — provider, model, api_key, and base_url — settable in the dashboard or via environment:

Option 1: Environment Variable

bash
export OPENAI_API_KEY=sk-your-key-here     # or ANTHROPIC_API_KEY for Anthropic
npx sidebutton

Option 2: Settings (Dashboard)

  1. Open localhost:9876
  2. Go to Settings
  3. Configure the LLM provider, model, and key (Ollama needs no key — just point base_url at your Ollama instance if it isn't on the default port)

LLM Steps

llm.classify

Categorize text into one of several categories:

yaml
- type: llm.classify
  input: "{{email_content}}"
  categories:
    - urgent
    - important
    - normal
    - spam
  as: priority

Parameters:

ParameterTypeRequiredDescription
inputstringYesText to classify
categoriesarrayYesList of possible categories
asstringYesVariable for result

Output: One of the category strings (e.g., "urgent")

Use cases:

  • Email triage
  • Support ticket routing
  • Content moderation
  • Lead qualification

llm.generate

Generate text based on a prompt:

yaml
- type: llm.generate
  prompt: |
    Summarize this article in 3 bullet points:

    {{article_content}}
  as: summary

Parameters:

ParameterTypeRequiredDescription
promptstringYesPrompt for the LLM
asstringYesVariable for result

Output: Generated text string

Use cases:

  • Summarization
  • Reply drafting
  • Content creation
  • Code explanation

llm.decide

Choose one action from a list — the branching primitive for agent-style workflows:

yaml
- type: llm.decide
  input: "{{ticket_content}}"
  actions:
    - id: escalate
      description: "Severe or customer-blocking — route to an engineer now"
    - id: reply
      description: "Answerable from documentation — draft a response"
    - id: archive
      description: "Spam or duplicate — close without action"
  as: next_action

- type: control.if
  condition: "{{next_action}} == 'escalate'"
  then:
    - type: issues.transition
      status: "Urgent"

Parameters:

ParameterTypeRequiredDescription
inputstringYesThe situation to decide on
actionsarrayYesAvailable actions, each with an id and a description
asstringYesVariable for the chosen action id

Output: Exactly one of the action id strings.

The step is defensive about model output: if the reply is not exactly an action id, the closest id contained in the reply is used, and if nothing matches, the first action in the list is chosen — so order your actions with the safest default first.

Use cases:

  • Ticket routing with different follow-up steps per outcome
  • Choosing between reply / escalate / ignore in inbox automations
  • Letting a role-scoped workflow pick its next move

User Contexts

Customize AI behavior for specific workflows using LLM Contexts in Settings.

Creating an LLM Context

  1. Go to SettingsUser Contexts
  2. Click Add Context
  3. Choose type: LLM
  4. Fill in:
    • Industry: Match by category domain (e.g., Sales)
    • Domain: Match by allowed_domains (e.g., linkedin.com)
    • Context: Instructions for the AI

Example Context

Industry: Sales
Domain: linkedin.com
Context: |
  You are a professional sales assistant.
  Keep responses concise and business-appropriate.
  Focus on building relationships, not hard selling.
  Never use emojis or overly casual language.

How Contexts Are Applied

When an LLM step runs:

  1. The system checks workflow's category.domain and policies.allowed_domains
  2. Matching contexts are prepended to the prompt
  3. The combined prompt is sent to the API

This applies to all three LLM steps — llm.decide includes the matched contexts ahead of its decision prompt.

Examples

Email Priority Classification

yaml
id: classify_email
title: "Classify Email Priority"

steps:
  - type: browser.extract
    selector: ".email-body"
    as: email

  - type: llm.classify
    input: "{{email}}"
    categories:
      - urgent_action_needed
      - important_but_not_urgent
      - informational
      - spam_or_marketing
    as: priority

  - type: control.if
    condition: "{{priority}} == 'urgent_action_needed'"
    then:
      - type: browser.click
        selector: ".flag-important"

Article Summarization

yaml
id: summarize_article
title: "Summarize Article"

steps:
  - type: browser.extract
    selector: "article"
    as: content

  - type: llm.generate
    prompt: |
      Summarize this article:

      {{content}}

      Format your response as:
      - HEADLINE: (one line summary)
      - KEY POINTS: (3 bullet points)
      - TAKEAWAY: (one sentence)
    as: summary

  - type: control.stop
    message: "{{summary}}"

Draft Reply with Context

yaml
id: draft_linkedin_reply
title: "Draft LinkedIn Reply"

category:
  domain: sales

policies:
  allowed_domains:
    - "*.linkedin.com"

steps:
  - type: browser.extractAll
    selector: ".msg-s-message-group"
    as: conversation

  - type: llm.generate
    prompt: |
      Draft a reply to this LinkedIn conversation:

      {{conversation}}

      The reply should be professional and move the conversation forward.
    as: reply

  - type: browser.type
    selector: ".msg-form__contenteditable"
    text: "{{reply}}"

Sentiment Analysis

yaml
id: analyze_sentiment
title: "Analyze Sentiment"

steps:
  - type: browser.extractAll
    selector: ".review-text"
    as: reviews

  - type: llm.classify
    input: "{{reviews}}"
    categories:
      - very_positive
      - positive
      - neutral
      - negative
      - very_negative
    as: sentiment

  - type: control.stop
    message: "Overall sentiment: {{sentiment}}"

Best Practices

1. Be Specific in Prompts

yaml
# Bad - vague
prompt: "Write something about {{topic}}"

# Good - specific
prompt: |
  Write a 100-word summary of {{topic}}.
  Include:
  - Main definition
  - Key applications
  - One interesting fact

2. Use Categories Thoughtfully

yaml
# Bad - overlapping categories
categories:
  - good
  - positive
  - great

# Good - distinct categories
categories:
  - positive
  - neutral
  - negative

3. Provide Context

yaml
prompt: |
  You are analyzing customer support tickets.

  Ticket: {{ticket_content}}

  Classify the urgency and suggest a response.

4. Handle Failures Gracefully

yaml
- type: control.retry
  max_attempts: 2
  delay_ms: 1000
  steps:
    - type: llm.generate
      prompt: "{{input}}"
      as: result

Troubleshooting

"API key not configured"

Set the provider's environment variable (OPENAI_API_KEY / ANTHROPIC_API_KEY) before starting the server, or configure the key in Settings. Ollama needs no key — check the base_url instead.

Rate limiting errors

  • Add delays between LLM calls
  • Use control.retry with exponential backoff
  • Consider batching requests

Unexpected output

  • Check your prompt is clear
  • Use few-shot examples
  • Add output format instructions
  • For llm.decide, remember the fallback order: exact id → closest match → first action

High latency

  • Keep prompts concise
  • Extract only needed content
  • Cache results if appropriate
  • For Ollama, latency depends on your local hardware and model size

API Usage & Costs

LLM steps make API calls that may incur costs (Ollama runs locally and is free):

Step TypeTypical Usage
llm.classify~100-500 tokens
llm.generate~500-2000 tokens
llm.decide~100-500 tokens

Token usage is tracked per run across all providers. Monitor provider-side usage in your provider's dashboard.

Next Steps

Released under the Apache-2.0 License.