Connect your RubyLLM-powered agents to ups.dev status pages.
After each LLM response, the gem sends a lightweight heartbeat to ups.dev — model, provider, response time, tool count. No message content is ever sent. Status degradation and incidents are reported by you, when you decide they matter.
gem "ruby_llm-ups"RubyLLM::Ups.configure do |c|
c.api_key = "ups_live_abc123_xyz789"
c.status_page_id = "my-page-slug" # your status page slug or numeric ID
endIn Rails, credentials are auto-loaded — no initializer needed if you set:
# config/credentials.yml.enc
ups:
api_key: ups_live_abc123_xyz789
status_page_id: my-page-slugclass ApplicationAgent < RubyLLM::Agent
include RubyLLM::Ups::Monitored
endThat's it. Every agent that inherits from ApplicationAgent gets its own component on ups.dev, auto-created by name. ResearchAgent becomes "Research Agent", DataProcessor becomes "Data Processor Agent".
class ResearchAgent < ApplicationAgent
model "claude-sonnet-4-20250514"
tools SearchTool, SummarizeTool
end
agent = ResearchAgent.new # → "Research Agent" component created on ups.dev
agent.ask("Hello") # → heartbeat: operational + metadataOverride the default name with ups_component:
class MyAgent < ApplicationAgent
ups_component "Customer Support Bot"
endEvery successful LLM response sends:
{
model: "claude-sonnet-4-20250514", # which model handled the request
provider: "anthropic", # the LLM provider
last_response_time_ms: 1230, # end-to-end response time
tool_count: 3 # number of tools available
}Failed LLM calls report nothing — the monitor only fires on successful responses. You decide when a failure is worth reporting (see below).
Use report_status when you detect a problem — partial failures, elevated error rates, slow responses:
RubyLLM::Ups.report_status(:degraded_performance,
component_id: component_id,
agent_metadata: {
failed_step: "summarization",
error: "Provider timeout"
}
)Create incidents for failures your users should know about:
RubyLLM::Ups.create_incident(
title: "Processing pipeline down",
impact: :major,
status: :investigating,
description: "Failed at enrichment stage: API rate limit exceeded"
)A common pattern for multi-stage agent pipelines:
class Pipeline
def call
stages.each { |stage| run_stage(stage) }
ups_report_operational
rescue => e
ups_create_incident(e)
raise
end
def run_stage(stage)
stage.call
rescue => e
if optional_stage?(stage)
ups_report_degraded(stage, e) # partial failure — degraded, not down
else
raise # critical failure — will create incident
end
end
private
def pipeline_component_id
@pipeline_component_id ||= begin
component = RubyLLM::Ups.component_registry.find_or_create("My Pipeline")
component["id"]
end
end
def ups_report_operational
RubyLLM::Ups.report_status(:operational, component_id: pipeline_component_id)
rescue => e
Rails.logger.warn("[ups.dev] #{e.message}")
end
def ups_report_degraded(stage, error)
RubyLLM::Ups.report_status(:degraded_performance,
component_id: pipeline_component_id,
agent_metadata: { failed_stage: stage.name, error: error.message }
)
rescue => e
Rails.logger.warn("[ups.dev] #{e.message}")
end
def ups_create_incident(error)
RubyLLM::Ups.create_incident(
title: "Pipeline failed",
impact: :major,
status: :investigating,
description: error.message
)
rescue => e
Rails.logger.warn("[ups.dev] #{e.message}")
end
endKey points:
- Use
component_registry.find_or_createto get a named component for non-agent things - Always rescue ups.dev calls — monitoring failures should never break your app
report_status(:operational)after success resets a previously degraded component
# Open
incident = RubyLLM::Ups.create_incident(
title: "Agent responding slowly",
impact: :minor, # :none, :minor, :major, :critical
status: :investigating # :investigating, :identified, :monitoring, :resolved
)
# Update
RubyLLM::Ups.update_incident(incident["incident"]["id"],
status: :identified,
update_message: "Root cause: Anthropic API degradation"
)
# Resolve
RubyLLM::Ups.resolve_incident(incident["incident"]["id"])| Option | Default | Description |
|---|---|---|
api_key |
required | Your ups.dev API key |
status_page_id |
required | Status page slug or numeric ID |
component_id |
— | Default component for monitor and report_status (optional with Monitored) |
base_url |
https://ups.dev |
API base URL |
request_timeout |
30 |
HTTP timeout in seconds |
on_error |
stderr | Error handler proc |
async |
true |
Send heartbeats in a background thread |
flush_interval |
5 |
Seconds between async flushes |
circuit_breaker_threshold |
5 |
Consecutive failures before opening circuit |
circuit_breaker_timeout |
60 |
Seconds to keep circuit open before retrying |
Monitoring errors never interrupt your LLM workflows. Route them somewhere useful:
RubyLLM::Ups.configure do |c|
c.on_error = ->(e) { Rails.logger.warn("[ups.dev] #{e.message}") }
endIf you don't want Monitored, call monitor() directly:
RubyLLM::Ups.configure do |c|
c.api_key = "..."
c.status_page_id = "..."
c.component_id = "my-agent-id"
end
chat = RubyLLM.chat(model: "claude-sonnet-4-20250514")
RubyLLM::Ups.monitor(chat)
chat.ask("Hello") # → heartbeat sentclient = RubyLLM::Ups.client
client.list_components
client.create_component(name: "New Agent", component_type: "agent")
client.update_component("comp-id", status: :operational, agent_metadata: { model: "gpt-4" })MIT