Ruby SDK
⚠️
Preview — this feature is being built; the design below is the spec.
The syntic gem is the planned official Ruby client. It is generated from the OpenAPI description and adds idiomatic helpers: blocks for streaming, enumerators for pagination, a tool runner and batch waiting. It is in the second release wave; see Other languages.
Today, use Net::HTTP as shown in Quickstart: Ruby. That code keeps working after the gem ships.
Install
bundle add syntic| Requirement | Value |
|---|---|
| Ruby | 3.1 or newer |
| Gem | syntic on RubyGems |
| Dependencies | net-http (standard library), connection_pool |
Create a client
require "syntic"
client = Syntic::Client.new # reads SYNTIC_API_KEY and SYNTIC_BASE_URL
client = Syntic::Client.new(
api_key: ENV.fetch("SYNTIC_API_KEY"),
base_url: "https://app.syntic.ai/v1",
max_retries: 3,
timeout: 300
)| Option | Default | Notes |
|---|---|---|
api_key | ENV["SYNTIC_API_KEY"] | Private key or workload identity token. |
base_url | ENV["SYNTIC_BASE_URL"], else https://app.syntic.ai/v1 | |
version | The release’s API version | Sent as syntic-version. |
betas | [] | Sent as syntic-beta; methods that need a flag add it. |
max_retries | 2 | rate_limit, overloaded, api_error and network errors. |
timeout | 600 seconds |
A first request
reply = client.chat.completions.create(
model: "Amara S1.2",
max_tokens: 200,
messages: [
{ role: "system", content: "Answer in one sentence." },
{ role: "user", content: "Why is the sky blue?" }
]
)
puts reply.choices.first.message.content
puts "#{reply.usage.total_tokens} tokens, request #{reply.request_id}"Streaming
final = client.chat.completions.stream(
model: "Amara Quick",
messages: [{ role: "user", content: "Write a haiku about rain." }]
) do |text|
print text
end
puts "\n#{final.usage.total_tokens} tokens"The block receives text pieces; the return value is the assembled completion. Breaking out of the block closes the connection.
Tool loop
get_order = Syntic::Tool.define(
name: "get_order",
description: "Look up an order by its id.",
parameters: { order_id: { type: "string", description: "Order id such as 88123", required: true } }
) { |order_id:| { order_id: order_id, status: "shipped" } }
result = client.tools.run(
model: "Amara S1.2",
messages: [{ role: "user", content: "Where is order 88123?" }],
tools: [get_order],
max_rounds: 8
)
puts result.final_message.contentExceptions raised inside a tool are sent back to the model as error results.
Errors
| Exception | error.type |
|---|---|
Syntic::InvalidRequestError | invalid_request |
Syntic::AuthenticationError | authentication |
Syntic::PermissionDeniedError | permission |
Syntic::NotFoundError | not_found |
Syntic::QuotaExhaustedError | quota_exhausted |
Syntic::RateLimitError | rate_limit |
Syntic::OverloadedError | overloaded |
Syntic::InternalServerError | api_error |
Syntic::ConnectionError | No response received |
All API errors inherit from Syntic::APIError with status, type, code, param, details and request_id.
begin
client.chat.completions.create(model: "Amara S1.2", messages: [])
rescue Syntic::QuotaExhaustedError => e
puts "Paused until #{e.details["resets_at"]}"
rescue Syntic::InvalidRequestError => e
puts "#{e.code} #{e.param} #{e.request_id}"
endPagination
client.agents.list(limit: 50).auto_paging_each do |agent|
puts "#{agent.id} #{agent.name}"
endFiles, batches, agents and admin
file = client.files.upload(path: "contract.pdf", purpose: "document")
batch = client.batches.create(input_file_id: file.id)
batch = client.batches.wait(batch.id, poll_interval: 30)
session = client.sessions.create(agent_id: "agt_01JC4R8W6Q2N9X3T5V7Y")
client.sessions.stream(session.id) do |event|
puts event.content if event.type == "message.completed"
end
admin = Syntic::AdminClient.new # reads SYNTIC_ADMIN_KEY
admin.users.list.auto_paging_each { |u| puts u.email }Raw responses
raw = client.chat.completions.with_raw_response.create(model: "Amara Quick", messages: messages)
puts raw.headers["x-syntic-billing-source"]
reply = raw.parseRelated
Was this page helpful?