Code examples
Complete examples you can copy and run. Each reads the API key from the LAYAHOST_API_KEY environment variable, or TYPESAFE_API_KEY for the Jev SDKs.
- Moderate comments before publishing
- Triage a support ticket with the Jev SDKs
- Retry with backoff
- Classify a batch within the rate limit
- Laravel HTTP client
Moderate comments before publishing
Publish clean comments, reject clear abuse, and send the uncertain middle to a person. The thresholds are examples: tune them on your own comments.
async function moderate(comment) {
const res = await fetch("https://layahost.com/v1/decide", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.LAYAHOST_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ text: comment, template: "moderation" }),
});
if (!res.ok) throw new Error(`layahost ${res.status}: ${await res.text()}`);
const { probabilities } = await res.json();
if (probabilities.toxic >= 0.9) return "reject";
if (probabilities.toxic >= 0.5) return "review";
return "publish";
}
console.log(await moderate("Thanks, this guide saved me hours!"));
import os
import requests
def moderate(comment: str) -> str:
res = requests.post(
"https://layahost.com/v1/decide",
headers={"Authorization": f"Bearer {os.environ['LAYAHOST_API_KEY']}"},
json={"text": comment, "template": "moderation"},
timeout=10,
)
res.raise_for_status()
toxic = res.json()["probabilities"]["toxic"]
if toxic >= 0.9:
return "reject"
if toxic >= 0.5:
return "review"
return "publish"
print(moderate("Thanks, this guide saved me hours!"))
function moderate(string $comment): string
{
$client = new GuzzleHttp\Client(['base_uri' => 'https://layahost.com', 'timeout' => 10]);
$res = $client->post('/v1/decide', [
'headers' => ['Authorization' => 'Bearer ' . getenv('LAYAHOST_API_KEY')],
'json' => ['text' => $comment, 'template' => 'moderation'],
]);
$toxic = json_decode($res->getBody(), true)['probabilities']['toxic'];
return match (true) {
$toxic >= 0.9 => 'reject',
$toxic >= 0.5 => 'review',
default => 'publish',
};
}
echo moderate('Thanks, this guide saved me hours!'), PHP_EOL;
Triage a support ticket with the Jev SDKs
Ask three questions about one ticket in a single /v1/systemone request: what the customer wants, how urgent it is, and whether they mention an order number. The answers are typed, so the SDKs know the labels for each question. Set TYPESAFE_BASE_URL=https://layahost.com and TYPESAFE_API_KEY first.
from typesafe_sdk import Choice, Noul, Score, TypeSafeClient
ticket = "Order #48213 still hasn't shipped after 10 days. I need it for a wedding on Saturday!"
with TypeSafeClient() as client: # reads TYPESAFE_BASE_URL and TYPESAFE_API_KEY
result = client.system_one(
model="laya-auto",
state=ticket,
questions={
"intent": Choice(
instructions="What does the customer want?",
criteria={
"delivery_status": "asks where an order is or when it arrives",
"refund": "wants money back",
"cancellation": "wants to cancel an order or account",
"other": "anything else",
},
),
"priority": Score(
instructions="How urgent is this ticket?",
criteria=["low", "normal", "high", "urgent"],
),
"has_order_number": Noul(instructions="Does the message include an order number?"),
},
)
levels = ["low", "normal", "high", "urgent"]
print({
"intent": result.choices["intent"].choice,
"priority": levels[round(result.scores["priority"].score)],
"has_order_number": result.nouls["has_order_number"].noul >= 0.5,
})
import { choice, noul, score, TypeSafeClient } from "@typesafe-ai/sdk";
const ticket = "Order #48213 still hasn't shipped after 10 days. I need it for a wedding on Saturday!";
const client = new TypeSafeClient(); // reads TYPESAFE_BASE_URL and TYPESAFE_API_KEY
const { answers } = await client.systemOne({
model: "laya-auto",
state: ticket,
questions: {
intent: choice("What does the customer want?", {
delivery_status: "asks where an order is or when it arrives",
refund: "wants money back",
cancellation: "wants to cancel an order or account",
other: "anything else",
}),
priority: score("How urgent is this ticket?", ["low", "normal", "high", "urgent"]),
has_order_number: noul("Does the message include an order number?"),
},
});
const levels = ["low", "normal", "high", "urgent"];
console.log({
intent: answers.intent.choice,
priority: levels[Math.round(answers.priority.score)],
has_order_number: answers.has_order_number.noul >= 0.5,
});
This request costs three decisions, $0.000015. See POST /v1/systemone for every field.
Retry with backoff
Retry 429, 503 and 529 after the number of seconds in retry-after, and fail fast on everything else. The Jev SDKs already do this; you only need it for plain HTTP calls.
const RETRYABLE = new Set([429, 503, 529]);
async function callLayahost(path, body, retries = 3) {
for (let attempt = 0; ; attempt++) {
const res = await fetch(`https://layahost.com${path}`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.LAYAHOST_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (res.ok) return res.json();
if (!RETRYABLE.has(res.status) || attempt === retries) {
const requestId = res.headers.get("x-request-id") ?? "no request id";
throw new Error(`layahost ${res.status} (${requestId}): ${await res.text()}`);
}
const retryAfter = Number(res.headers.get("retry-after"));
const delayMs = retryAfter > 0 ? retryAfter * 1000 : 500 * 2 ** attempt;
await new Promise((resolve) => setTimeout(resolve, delayMs));
}
}
const result = await callLayahost("/v1/decide", {
text: "Where is my refund? It has been three weeks.",
template: "support_intent",
});
console.log(result.decision, result.request_id);
import os
import time
import requests
RETRYABLE = {429, 503, 529}
def call_layahost(path: str, body: dict, retries: int = 3) -> dict:
for attempt in range(retries + 1):
res = requests.post(
f"https://layahost.com{path}",
headers={"Authorization": f"Bearer {os.environ['LAYAHOST_API_KEY']}"},
json=body,
timeout=10,
)
if res.ok:
return res.json()
if res.status_code not in RETRYABLE or attempt == retries:
request_id = res.headers.get("x-request-id", "no request id")
raise RuntimeError(f"layahost {res.status_code} ({request_id}): {res.text}")
retry_after = float(res.headers.get("retry-after", 0))
time.sleep(retry_after if retry_after > 0 else 0.5 * 2**attempt)
result = call_layahost("/v1/decide", {
"text": "Where is my refund? It has been three weeks.",
"template": "support_intent",
})
print(result["decision"], result["request_id"])
Classify a batch within the rate limit
Each key may send 600 requests per minute, which is 10 per second. For a batch of texts, space the requests out rather than starting them all at once. These examples allow at most 9 requests per second, with a few in flight at a time.
import os
import threading
import time
from concurrent.futures import ThreadPoolExecutor
import requests
MAX_PER_SECOND = 9 # the limit is 600 requests per minute per key
lock = threading.Lock()
next_slot = time.monotonic()
def wait_for_slot() -> None:
global next_slot
with lock:
now = time.monotonic()
slot = max(now, next_slot)
next_slot = slot + 1 / MAX_PER_SECOND
time.sleep(slot - now)
def sentiment(text: str) -> str:
wait_for_slot()
res = requests.post(
"https://layahost.com/v1/decide",
headers={"Authorization": f"Bearer {os.environ['LAYAHOST_API_KEY']}"},
json={"text": text, "template": "sentiment"},
timeout=10,
)
res.raise_for_status()
return res.json()["decision"]
reviews = [
"Arrived on time and works perfectly.",
"The battery died after two days.",
"It does the job. Nothing special.",
]
with ThreadPoolExecutor(max_workers=4) as pool:
for text, label in zip(reviews, pool.map(sentiment, reviews)):
print(f"{label:>8} {text}")
const MAX_PER_SECOND = 9; // the limit is 600 requests per minute per key
let nextSlot = Date.now();
async function waitForSlot() {
const now = Date.now();
const slot = Math.max(now, nextSlot);
nextSlot = slot + 1000 / MAX_PER_SECOND;
await new Promise((resolve) => setTimeout(resolve, slot - now));
}
async function sentiment(text) {
await waitForSlot();
const res = await fetch("https://layahost.com/v1/decide", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.LAYAHOST_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ text, template: "sentiment" }),
});
if (!res.ok) throw new Error(`layahost ${res.status}: ${await res.text()}`);
return (await res.json()).decision;
}
const reviews = [
"Arrived on time and works perfectly.",
"The battery died after two days.",
"It does the job. Nothing special.",
];
const labels = await Promise.all(reviews.map(sentiment));
reviews.forEach((text, i) => console.log(labels[i].padStart(8), text));
A batch of 10,000 texts takes about 19 minutes at this pace. If you ask several questions about each text, send them together in one /v1/systemone request: the rate limit counts requests, not questions.
Laravel HTTP client
In a Laravel app, add the key to config/services.php as 'layahost' => ['key' => env('LAYAHOST_API_KEY')], then use the HTTP client with retries on temporary errors:
use Illuminate\Http\Client\RequestException;
use Illuminate\Support\Facades\Http;
$result = Http::withToken(config('services.layahost.key'))
->baseUrl('https://layahost.com')
->timeout(10)
->retry(3, 1000, fn ($e) => $e instanceof RequestException
&& in_array($e->response->status(), [429, 503, 529]))
->post('/v1/decide', [
'text' => 'Where is my refund? It has been three weeks.',
'template' => 'support_intent',
])
->json();
echo $result['decision'];
With retry(), a request that still fails after the last attempt throws RequestException. The fixed one-second wait suits 503 and 529; a 429 can ask you to wait up to a minute.