Temporal Annotations for LLMs
temporalBLOCK can attach a temporal annotation to every API response — a compact human-readable label or raw spiral (sin, cos) coordinates — so the LLM receiving your event data understands exactly when something happened in cyclical time. Which format you pick matters: the right choice adds up to 22 percentage points of accuracy on routine temporal tasks; on deep circular-reasoning or composite-cycle workloads the swing can reach 25 pp, and the winning format sometimes flips from the easy-set recommendation.
For routine tasks, request annotationFormat: "compact". It is the most consistent cross-model default and never causes the accuracy regressions that raw floats can for some Anthropic models. Exception for hard circular / composite-cycle workloads: for gpt-5.4, omit both annotationFormat and llmModel — passing llmModel: "gpt-5.4" auto-selects compact for the OpenAI family, which defeats the intent. Also ensure your key has no stored compact default (see below). For deepseek-chat, passing llmModel: "deepseek-chat" is safe — DeepSeek does not trigger compact auto-selection. In both cases, feed meta.spiralCoordinate (always present) directly to the model (+20–25 pp on the hard set). Gemini and Sonnet-4-6 stay on compact even for hard tasks.
Per-model recommendations
Based on Benchmark 5 (40 deterministic MCQs, three annotation conditions, seven mid-to-frontier models, two difficulty sets). One run per condition — directional guidance, not a formal ranking. Task difficulty matters: some models shift their best format on the hard set.
| Model family | Use | Why |
|---|---|---|
| OpenAI gpt-4.x / gpt-5.4 (easy tasks) | compact | Compact labels give the largest gains on routine tasks (+20 pp average). Raw floats add nothing for everyday workloads. |
| OpenAI gpt-5.4 (hard circular / composite-cycle tasks) | spiralCoordinate | Format flips on deep circular-reasoning tasks: spiralCoordinate 27/40 (67.5 %) vs compact 19/40 (47.5 %) — a +20 pp swing. Omit both annotationFormat and llmModel (passing llmModel: "gpt-5.4" auto-selects compact for the OpenAI family). Also ensure your key has no stored compact default. |
| DeepSeek (deepseek-chat) | spiral | Spiral dominates at every difficulty level. Easy set: 73 % → 95 %. Hard set: reaches a perfect 40/40 (100 %) with spiral vs 35/40 (87.5 %) compact. Passing llmModel: "deepseek-chat" is safe — the DeepSeek family does not trigger compact auto-selection. |
| Anthropic Sonnet (sonnet-4-6) — easy tasks | either | Both formats help on routine tasks. Spiral slightly ahead (+7 pp vs +2 pp). For hard tasks see below. |
| Anthropic Sonnet (sonnet-4-6) — hard circular tasks | compact | Compact is clearly better on hard tasks: 36/40 (90 %) vs spiral 30/40 (75 %). Spiral hurts by −15 pp. Sonnet-5 sits at 95 % plain and is annotation-optional. |
| Anthropic Opus (opus-4-x) | compact | Raw floats hurt accuracy (−7 pp). Compact labels recover +15 pp. Opus-5 refuses annotated prompts in all formats — skip annotation for that model. |
| Google Gemini flash (gemini-2.5-flash) | compact | Compact is best at both difficulty levels. Hard set: compact 30/40 (75 %) vs spiral 22/40 (55 %). Spiral floats actively hurt Gemini. |
| Unknown or other | compact | Most consistent cross-model default. Never hurts as much as floats can. |
| Anthropic Opus 5 (opus-5) | none | Auto-skipped. The annotation classifier fires on bracket/label tokens; pass llmModel:"claude-opus-5" and the API omits annotation automatically. |
| Frontier tier (sonnet-5, gpt-5.6-terra/luna) | either | Already at the 98–100 % ceiling in every condition including hard tasks — annotations neither help nor hurt significantly. |
Hard-set task findings
The hard set (Benchmark 5, 40 MCQs, circular-reasoning and composite-cycle questions) reveals format shifts that are invisible in easy-set scores. Numbers below are from results-llm-temporal-hard.json (date: 2026-08-03, deterministic seeds).
| Model | Plain | Spiral | Compact | Best format (hard) |
|---|---|---|---|---|
| gpt-5.4 | 17/40 (42.5 %) | 27/40 (67.5 %) | 19/40 (47.5 %) | spiralCoordinate (+20 pp vs compact) |
| deepseek-chat | 31/40 (77.5 %) | 40/40 (100 %) | 35/40 (87.5 %) | spiralCoordinate (+12.5 pp vs compact) |
| gemini-2.5-flash | 23/40 (57.5 %) | 22/40 (55 %) | 30/40 (75 %) | compact (+17.5 pp vs spiral) |
| claude-sonnet-4-6 | 32/40 (80 %) | 30/40 (75 %) | 36/40 (90 %) | compact (+15 pp vs spiral) |
| claude-sonnet-5 | 38/40 (95 %) | 36/40 (90 %) | 38/40 (95 %) | plain or compact (tie) |
| gpt-5.6-terra / gpt-5.6-luna | 40/40 (100 %) | 40/40 (100 %) | 40/40 (100 %) | ceiling — annotation optional |
meta.spiralCoordinate on hard tasks (+20 pp over compact, +25 pp over plain). To get spiral for gpt-5.4 hard tasks, omit both annotationFormat and llmModel — passing llmModel: "gpt-5.4" auto-selects compact for the OpenAI family. If your key has a stored compact default, clear it first: PATCH /api/keys/:id/annotation-format { annotationFormat: null }. deepseek-chat reaches a perfect 40/40 with spiral; passing llmModel: "deepseek-chat" is safe (DeepSeek does not trigger compact auto-selection). gemini-2.5-flash and claude-sonnet-4-6 stay on compact even for hard tasks.Compact labels
Compact labels encode the current temporal position as a single human-readable string: time-of-day:08:15UTC weekday:Fri week-of-year:wk11/52 month:Mar year:2026. LLMs read and compare these the same way they read any other text — no floating-point arithmetic required.
Request compact labels by passing annotationFormat: "compact" in the request body. The response carries meta.compactAnnotation.
POST /v1/full
X-API-Key: tblk_live_YOUR_KEY
Content-Type: application/json
{
"annotationFormat": "compact"
}
// Response includes:
// meta.compactAnnotation:
// "time-of-day:08:15UTC weekday:Fri week-of-year:wk11/52 month:Mar year:2026"Inject meta.compactAnnotation wherever your prompt describes time — system prompt, user message, or inline with each event. Example:
// Build a system-prompt that includes the annotation
const res = await fetch("https://api.temporalblock.com/v1/full", {
method: "POST",
headers: { "X-API-Key": "tblk_live_...", "Content-Type": "application/json" },
body: JSON.stringify({ annotationFormat: "compact" }),
});
const { meta } = await res.json();
// Prepend the annotation to your prompt
const systemPrompt = `Current time context: ${meta.compactAnnotation}
You are a scheduling assistant ...`;Spiral (sin, cos) coordinates
The spiral coordinate encodes time as a set of (sin, cos) pairs — one per temporal scale from hour-of-day through year-of-decade. These are the same features fed to ML models in Benchmarks 1–4. Most LLMs handle them poorly, but DeepSeek is an exception: raw floats gave a +22 pp gain on temporal reasoning.
The spiral coordinate is returned in every /v1/full response as meta.spiralCoordinate — no extra request parameter required.
POST /v1/full
X-API-Key: tblk_live_YOUR_KEY
Content-Type: application/json
{} // no annotationFormat — spiralCoordinate is always present
// Response includes:
// meta.spiralCoordinate:
// { hr: { sin: 0.707, cos: 0.707 }, day: { sin: -0.866, cos: 0.5 }, ... }// Serialize the spiral coordinate into the prompt for DeepSeek
const res = await fetch("https://api.temporalblock.com/v1/full", {
method: "POST",
headers: { "X-API-Key": "tblk_live_...", "Content-Type": "application/json" },
body: JSON.stringify({}),
});
const { meta } = await res.json();
const sc = meta.spiralCoordinate;
// Format each scale as the LLM will see it
const spiralText = Object.entries(sc)
.map(([scale, v]) => `${scale}: sin=${v.sin.toFixed(3)} cos=${v.cos.toFixed(3)}`)
.join(", ");
const systemPrompt = `Current spiral coordinates: ${spiralText}
You are ...`;llmModel to let the API pick the right format automatically.Pre-built system-prompt paragraph
If you want to skip formatting logic entirely, request includeSpiralBlock: true. The response adds meta.spiralBlock — a full, ready-to-paste system-prompt paragraph that describes the current temporal position in prose. It can be used alongside annotationFormat: "compact" if you want both.
POST /v1/full
X-API-Key: tblk_live_YOUR_KEY
Content-Type: application/json
{
"annotationFormat": "compact",
"includeSpiralBlock": true
}
// Response includes both:
// meta.compactAnnotation — Compact Spiral string
// meta.spiralBlock — full system-prompt paragraph for any modelTerse variants: same accuracy, fewer tokens
Both formats have a terse rendering measured in Benchmark 6: terse compact (18:36Z Thu wk8 Feb-2025) costs 1.68× plain-timestamp tokens, and terse spiral (2-decimal floats, hr/day/mo scales only) costs 2.58× — versus 2.53× and 3.92× for the verbose forms.
The accuracy gains survive the compression. Re-running the Benchmark 5 question set with terse annotations across five mid-tier models: terse compact matched or beat plain timestamps on every model — gpt-5.4 (98% vs 75% plain), gpt-5.2 (95% vs 80%), claude-sonnet-4-6 (83% vs 75%), gemini-2.5-flash (80% vs 78%) — and DeepSeek kept its spiral advantage with terse spiral (85% vs 70% plain). The per-family recommendations above are unchanged; terse is simply the cheaper way to apply them.
meta.compactAnnotation is the verbose rendering (time-of-day:… weekday:…). If token cost matters, compress it client-side into the terse form shown above — the fields map one-to-one, and the terse accuracy numbers here were measured on exactly that rendering.Auto-select with llmModel
If you already know which model you are calling, pass its name as llmModel and omit annotationFormat — the API will apply the benchmark-proven best format for that model family automatically.
| llmModel prefix / family | Auto-selected format |
|---|---|
| gpt-*, o1-*, o3-*, o4-*, chatgpt-* | compact |
| deepseek-* | spiral (meta.spiralCoordinate) |
| claude-opus-5* / *opus5* | none — auto-skipped |
| *opus*, *sonnet*, *haiku* | compact |
| gemini-*, google/* | compact |
| anything else | compact |
An explicit annotationFormat always wins over llmModel. Both /v1/full and GET /v1/spiral accept the parameter.
// /v1/full — let the API pick the right format (easy tasks)
const res = await fetch("https://api.temporalblock.com/v1/full", {
method: "POST",
headers: { "X-API-Key": process.env.TBLK_KEY!, "Content-Type": "application/json" },
body: JSON.stringify({
query: "What happened last week?",
llmModel: "gpt-5.4", // → API sets annotationFormat:"compact" automatically
syncProvider: "openai",
syncApiKey: process.env.OPENAI_KEY!,
}),
});
const { meta } = await res.json();
// meta.compactAnnotation is populated — no annotationFormat param needed
// For hard circular / composite-cycle tasks with gpt-5.4:
// omit BOTH annotationFormat AND llmModel — passing llmModel:"gpt-5.4" auto-selects
// compact for the OpenAI family, overriding the spiral intent (+20 pp at stake).
// Also clear any stored per-key compact default first if needed.
// deepseek-chat → llmModel:"deepseek-chat" is safe (does not trigger compact auto-select);
// or omit both and use meta.spiralCoordinate directly (40/40 hard set)
// gemini-2.5-flash, claude-sonnet-4-6 → annotationFormat:"compact" in all conditions
// GET /v1/spiral — same llmModel param works as a query string
// GET /v1/spiral?llmModel=deepseek-chat
// → spiralCoordinate is always present; no extra annotation added (spiral family)Manual switching per model
If you prefer explicit control, pass annotationFormat directly. The annotation format is a per-request parameter, so you can pick it at call time:
// isHardTask: true for deep circular / composite-cycle reasoning prompts
function pickFormat(model: string, isHardTask = false): string | undefined {
if (model.startsWith("deepseek")) return undefined; // always use spiralCoordinate
if (model.startsWith("claude-opus-5")) return undefined; // auto-skipped (refusal risk)
// gpt-5.4 on hard circular tasks: omit annotationFormat, use meta.spiralCoordinate
// (+20 pp over compact — 27/40 vs 19/40 on hard set)
if (isHardTask && model === "gpt-5.4") return undefined;
// gemini-2.5-flash and claude-sonnet-4-6 stay on compact even for hard tasks
return "compact";
}
const annotationFormat = pickFormat(model, isHardTask);
const body: Record<string, unknown> = { /* your other params */ };
if (annotationFormat) body.annotationFormat = annotationFormat;
// IMPORTANT for gpt-5.4 hard tasks: also omit llmModel from the request body.
// Passing llmModel:"gpt-5.4" auto-selects compact for the OpenAI family
// (resolution chain: annotationFormat > llmModel auto-pick > stored key default).
// For deepseek, llmModel:"deepseek-chat" is safe — it does not trigger compact.
// If your key has a stored compact default, clear it first:
// PATCH /api/keys/:id/annotation-format { "annotationFormat": null }
// meta.spiralCoordinate is always present in every /v1/full response — no extra param needed.
const isGpt54Hard = isHardTask && model === "gpt-5.4";
if (!isGpt54Hard) body.llmModel = model;
const res = await fetch("https://api.temporalblock.com/v1/full", {
method: "POST",
headers: { "X-API-Key": process.env.TBLK_KEY!, "Content-Type": "application/json" },
body: JSON.stringify(body),
});Related
- Benchmark results — the data behind these recommendations
- Spiral coordinate reference — what each (sin, cos) scale represents
- MCP integration — attach temporalBLOCK to Claude Desktop, Cursor, or any MCP client