@groundcover/gpu-schedule
v0.1.0
Published
Independent browser schedule model for silence windows
Keywords
Readme
Turns a typed silence schedule, such as Monday 8pm-10pm, mornings or
tomorrow 9am for 2 hours, into a one-time, daily or weekly schedule. A small
model labels each word, and plain code turns the labels into windows. It runs
in the browser or Node.js with no server call: the weights are inside the
module. It has no runtime dependency on gpu-timerange.
Unsupported expressions can return a refusal reason. The model can also assign
incorrect labels and return unintended windows; ok: true does not guarantee
that the user's meaning was understood. Show the resolved schedule to the user.
Install
npm install @groundcover/gpu-scheduleThe module is about 99 KB, 47 KB with Brotli. To load it only when text is typed, import it dynamically:
const { defineParser } = await import("@groundcover/gpu-schedule");Use
import { defineParser } from "@groundcover/gpu-schedule";
const parser = await defineParser();
const result = await parser.parse("Monday 8pm-10pm", {
reference: "2026-10-14T14:00:00+03:00",
timeZone: "Asia/Jerusalem",
weekStart: "MO",
weekend: ["FR", "SA"],
dateOrder: "DM",
});
if (result.ok) {
result.schedule;
// { kind: "weekly", days: { MO: [{ start: "20:00", end: "22:00" }] } }
} else {
result.reason; // why the text was refused
}
parser.dispose(); // when finished with the parserContext
| Field | Required | Description |
|---|---|---|
| reference | Required | The reference instant, written as YYYY-MM-DDTHH:MM, …:SS or …:SS.sss, followed by Z or an offset such as +03:00. Relative dates are resolved from this instant. |
| timeZone | Required | An IANA timezone, such as Asia/Jerusalem, used for local dates and clocks unless the text supplies a timezone. |
| weekStart | Required | The first weekday: "MO", "TU", "WE", "TH", "FR", "SA" or "SU". Used for calendar-week expressions. |
| weekend | Optional | Distinct consecutive weekend days, such as ["FR", "SA"]. Without this field, a schedule using weekend is refused. weekdays always means Monday through Friday. |
| dateOrder | Optional | With "MD", 03/04 is March 4; with "DM", it is April 3. Numeric dates needing this convention are refused when it is absent. |
Result
A result is { ok: true, schedule } or { ok: false, reason }.
There are three schedule shapes:
import type { Schedule } from "@groundcover/gpu-schedule";
// One explicit window. ISO timestamps use the context's timezone offset.
const oneTime: Schedule = {
kind: "one-time",
start: "2026-10-15T09:00:00+03:00",
end: "2026-10-15T11:00:00+03:00",
};
// Local-clock windows repeated every day.
const daily: Schedule = { kind: "daily", windows: [{ start: "06:00", end: "12:00" }] };
// Local-clock windows for each enabled weekday.
const weekly: Schedule = { kind: "weekly", days: { MO: [{ start: "20:00", end: "22:00" }] } };At the reference time above:
| Text | Schedule |
|---|---|
| tomorrow 9am for 2 hours | One-time: October 15, 09:00–11:00 |
| morning or mornings | Daily: 06:00–12:00 |
| noon | Daily: 11:00–13:00 |
| Monday 8pm-10pm | Weekly: Monday 20:00–22:00 |
| Monday 10pm-2am | Weekly: Monday 22:00–24:00 and Tuesday 00:00–02:00 |
| banana | Refused: ok: false, with a reason |
Recurring windows use HH:MM. 24:00 is an end-of-day boundary, not an input
clock. A full day is 00:00–24:00. Overlapping or touching windows are merged
and ordered by start time. Recurring schedules use context.timeZone unless
an explicit timezone adds a schedule.timeZone override; save that override
or the context timezone alongside the windows. One-time timestamps already
identify their instants.
Every result also has:
backend:"cpu"or"webgpu", whichever ran inference.timings:{ inferenceMs, totalMs }, in milliseconds.fallbackReason: set when automatic WebGPU initialization or execution fails and the parser uses the CPU.tokens: when requested with the option below and the input reaches inference. Empty inputs and inputs over the token limit have no tokens.
How a text is read
everyis implicit for unqualified weekdays, day groups, day parts and lone clocks.Monday 8pm-10pmandevery Monday 8pm-10pmare equivalent;morningandmorningsboth mean daily 06:00–12:00.- Dated anchors stay one-time:
tomorrow,tonight,next Monday,this morningand full calendar dates.every tomorrowis refused. - A clock range without a day, such as
8pm-10pm, selects the nearest upcoming one-time window. Usedaily 8pm-10pmto repeat it. - A lone clock has a margin of one unit of its smallest written part on
each side:
Thursday 3pmis Thursday 14:00–16:00, andThursday 15:00is Thursday 14:59–15:01. Barenoonis daily 11:00–13:00. - Day parts are morning 06:00–12:00, afternoon 12:00–17:00, evening
17:00–21:00 and night 21:00–06:00 the following day. A narrower range wins:
every night 10pm-11pmmeans 22:00–23:00.tonight to 11means tonight 21:00–23:00. Monday and Friday 8pm-10pmapplies the window to both days.Monday 9am-10am and 1pm-2pmhas two Monday windows. Complete adjacent clauses can omitand:Monday morning Tuesday nightmeans the same asMonday morning and Tuesday night.- Daily and weekly clauses combine into a weekly schedule.
daily 9am-10am and Friday noon-1pmcovers 09:00–10:00 on all seven days, plus 12:00–13:00 on Friday. - Overnight recurring windows split across midnight. Recurring durations
are local-clock windows:
Monday 9pm for 2 hoursmeans Monday 21:00–23:00, even when daylight-saving changes affect elapsed time. One-time silences can retain seconds and explicit fractional durations. - A timezone written anywhere applies to the entire input. Multiple timezone
expressions are refused, even if they name the same zone. Written zones
support
UTC,GMT,Zand numeric offsets; application context supplies the IANA timezone. Recurring offsets must be representable as a whole-hour fixed IANA zone; other offsets are refused for recurring schedules.
Options
import { defineParser } from "@groundcover/gpu-schedule";
const parser = await defineParser({ backend: "auto", tokens: false });backend:
"auto", the default: tries WebGPU at initialization. If initialization fails, including when WebGPU is unavailable, it uses the CPU and setsfallbackReason. If later GPU inference fails, it switches to the CPU for that call and subsequent calls."webgpu": always WebGPU. Initialization rejects when it is unavailable; GPU execution failures reject instead of falling back."cpu": always CPU, without initializing WebGPU.
tokens adds { start, end, text, kind, label, score } for each token.
Offsets are UTF-16 string indices; end is exclusive. kind is "word",
"number", "punctuation" or "space". Spaces have null labels and scores.
ScheduleLabel lists the label values, such as WEEKDAY, DAYPART and JOIN.
Scores are emission softmax scores, not calibrated confidence in the schedule.
parser.parse(text, context) reads one string. Reuse the parser across calls.
parser.dispose() frees GPU resources; later calls and calls still awaiting
inference reject. There is no batch parsing method.
Errors
Unrecognized or unsupported schedules return ok: false. Empty inputs are
refused, and inputs above 128 tokens are refused without truncation; this
limit includes spaces and punctuation tokens.
Invalid application context throws when a successfully compiled input reaches
the resolver. Calls after dispose() reject. With backend: "webgpu", GPU
initialization and execution failures reject. Callers should handle rejected
promises separately from a returned refusal.
Requirements
- An ES module runtime with ES2023 array methods, including
toSorted, plusatob,performanceand Unicode regular expressions. Intl.DateTimeFormatwith IANA timezone data.- WebGPU is optional. Using it requires an available adapter and a browser context where WebGPU is enabled.
Known limitations
A one-time schedule has one continuous window. Multiple separate one-time
windows, or a mixture of one-time and recurring clauses, are refused. Recurring
windows require whole-minute endpoints. weekly 9am-10am needs a weekday and
is refused rather than choosing one.
Calendar quarters, abbreviated month ranges such as 6-8/2025, spoken clocks
such as half past three or six o'clock, and explicit elapsed-duration
wording are unsupported. Supported calendar dates are in the years 1970–2099.
The initial model, V1, is seed 3, epoch 19 of the internal schedule-series-v8
run. It passed promotion without an override: 463/463 authored cases,
1,786/1,786 mandatory cases and 9,995/10,000 generated validation cases. The
five remaining generated misses are typo inputs that the runtime refuses;
they remain accepted limitations. CPU and Chrome WebGPU parity passed on
445/445 reference inputs, with 463/463 authored cases on each backend.
These recorded results do not guarantee every possible wording.
Types
The package exports Context, Schedule, ScheduleLabel, Weekday,
Window, Backend, ParsedToken and ParseResult types. modelInfo exposes
architecture, checkpointSha256, epoch and quantizationBits.
Development
Build and check the package from the repository root:
npm run test:package -w @groundcover/gpu-scheduleFor the Groundcover frontend, use its pinned pnpm 12.3.1. From frontend, link
directly to the package in your checkout:
SCHEDULE_PACKAGE=/absolute/path/to/gpu-timerange/packages/schedule
corepack pnpm --filter @groundcover/app add "@groundcover/gpu-schedule@link:$SCHEDULE_PACKAGE" --ignore-scriptsRebuild after package changes; restart Vite if its dependency cache still has the old bundle. Training, export and parity checks are described in the training guide. The package builds from checked-in weights; consumers do not need to train or export a model.
License
MIT
