apple-llm 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,690 @@
1
+ interface Progress {
2
+ status: string;
3
+ percent?: number;
4
+ }
5
+ type OnProgress = (p: Progress) => void;
6
+ /**
7
+ * Where the compiled helper is cached.
8
+ *
9
+ * Deliberately not namespaced per language binding: the npm and the pip package
10
+ * embed byte-identical Swift, so they derive the same fingerprint and share one
11
+ * compiled binary. Installing both costs one compile, not two.
12
+ */
13
+ declare function cacheDir(): string;
14
+ /**
15
+ * The cache key: sha256 over the Swift source, a newline, and the target triple,
16
+ * truncated to 12 hex characters.
17
+ *
18
+ * The exact recipe is load-bearing — the Python package computes the same string
19
+ * and must agree byte for byte, or the two would each compile their own copy.
20
+ * A helper edit or an OS upgrade changes it, so both rebuild automatically.
21
+ */
22
+ declare function fingerprint(source: string, triple: string): string;
23
+ /** The embedded Swift source, shipped as package data beside the built output. */
24
+ declare function helperSource(): Promise<string>;
25
+ /**
26
+ * Compile the helper on first use and cache it. Never called at install time —
27
+ * importing this package on Linux, an Intel Mac or macOS 25 must not fail.
28
+ *
29
+ * `force` bypasses the in-process memo, so a caller can rebuild after the cached
30
+ * binary is removed or found corrupt without restarting the program.
31
+ *
32
+ * The memo is fingerprint-aware: a helper edit or OS upgrade changes the
33
+ * fingerprint, so a stale in-process memo is discarded rather than reused.
34
+ */
35
+ declare function ensureBinary(onProgress?: OnProgress, options?: {
36
+ force?: boolean;
37
+ }): Promise<string>;
38
+
39
+ /**
40
+ * Apple's `GenerationSchema` is `Decodable` from JSON Schema, but only accepts a
41
+ * restricted dialect. These rules were established empirically in api-scribe, by
42
+ * decoding a real schema against macOS 26 and 27 until it was accepted:
43
+ *
44
+ * 1. Union types are rejected — `type: ['string','null']` fails with
45
+ * "Expected value of type String". Nullability is expressed by leaving the
46
+ * property out of `required` instead.
47
+ * 2. Every object schema requires an `x-order` array naming its properties in
48
+ * generation order. Omitting it fails with "Key 'x-order' not found".
49
+ * 3. `enum` is not recognized. A node may only carry `type`, `const`, `$ref` or
50
+ * `anyOf`, so enums become `anyOf` of `const` branches.
51
+ * 4. Every object schema *and* every `anyOf` schema requires a `title`, and
52
+ * `$ref` resolves by that title rather than by JSON pointer — so a
53
+ * definition's title must equal the key it is referenced by.
54
+ * 5. Every object schema must state `additionalProperties`; omitting it fails
55
+ * with "Key 'additionalProperties' not found". macOS 26's decoder tolerated
56
+ * its absence, so this one only surfaced on macOS 27.
57
+ *
58
+ * 6. `const` is decoded as a String, always. A numeric member (`const: 200`)
59
+ * fails with "Expected value of type String". Stringifying it is accepted
60
+ * but changes the output type — the model then emits `"200"` rather than
61
+ * `200` — so a numeric enum is converted to its underlying type instead and
62
+ * the literal constraint is dropped. api-scribe reached the same conclusion
63
+ * the hard way: it settled on a plain `integer` for HTTP status after a
64
+ * string-typed union produced junk like `": 201"`.
65
+ * 7. An object with no properties still needs an explicit `properties: {}`.
66
+ * Without it Apple reads `additionalProperties` in its other JSON Schema
67
+ * sense — a schema for the values — and fails with "Expected value of type
68
+ * Dictionary<String, Any>". Supplying a dictionary there is accepted but
69
+ * turns the field into a free-form map the model fills with invented keys.
70
+ *
71
+ * 8. A `title` on a `type: "string"` node is rejected: Apple reads it as a
72
+ * "named string type" and fails with "Named string types must have a
73
+ * non-empty enum field". Other primitives (integer, number, boolean) and
74
+ * arrays accept a title happily. Titles are therefore stripped from plain
75
+ * strings. This matters most for pydantic, whose `model_json_schema()`
76
+ * titles every single property.
77
+ *
78
+ * Rules 6, 7 and 8 were found by decoding this package's own fixture corpus
79
+ * against macOS 27. api-scribe hit none of them: it had only string enums, no
80
+ * empty objects, and hand-written schemas that never titled a string.
81
+ *
82
+ * `$ref` / `$defs` are otherwise supported and pass through untouched.
83
+ *
84
+ * Why this matters more than it looks: constrained decoding makes a schema
85
+ * mistake invisible but total. Collapsing a `["number","string"]` union to
86
+ * `"string"` made it physically impossible for the model to emit a status code —
87
+ * it wrote `": 201"` and the literal `"default"` instead. Never collapse a
88
+ * multi-type union to one branch; convert it to `anyOf`.
89
+ */
90
+ type JsonSchema = Record<string, unknown>;
91
+ /**
92
+ * Translate a standard JSON Schema into the dialect Apple's `GenerationSchema`
93
+ * accepts. Constrained decoding then makes the shape of the reply a guarantee
94
+ * rather than a request.
95
+ */
96
+ declare function toAppleSchema(schema: JsonSchema, rootName?: string): JsonSchema;
97
+
98
+ /**
99
+ * Sampling temperature. Not zero on purpose.
100
+ *
101
+ * Constrained decoding already guarantees the schema, so greedy decoding buys
102
+ * nothing and reliably degenerates: at 0 the model padded an unbounded array
103
+ * forever, then ran away *inside a single string*, emitting 2.7KB of
104
+ * "tasks-tasks-tasks-…". A 2s call became 20s. Apple honours `maxItems` but
105
+ * ignores `maxLength`, so bounding strings is not available as a fix — a little
106
+ * sampling is. Nothing about this is a quality/creativity tradeoff.
107
+ */
108
+ declare const DEFAULT_TEMPERATURE = 0.4;
109
+ /**
110
+ * Cap on generated tokens. Left uncapped, the model occasionally runs away and
111
+ * only stops when it exhausts the context window — one observed run burned
112
+ * ~206s before failing. This bounds that to well under a minute while leaving
113
+ * room for a few paragraphs of prose.
114
+ */
115
+ declare const DEFAULT_MAX_TOKENS = 2048;
116
+ /** What the model can actually do, as reported by the framework (macOS 27+). */
117
+ interface ModelCapabilities {
118
+ vision: boolean;
119
+ guidedGeneration: boolean;
120
+ reasoning: boolean;
121
+ toolCalling: boolean;
122
+ }
123
+ /** Siri-parity feature flags reported by --probe (absent on older helpers). */
124
+ interface HelperFeatures {
125
+ streaming: boolean;
126
+ sessions: boolean;
127
+ history: boolean;
128
+ labelledAttachments: boolean;
129
+ builtInTools: string[];
130
+ }
131
+ /** An image attachment: a bare path, or a path with a Siri-style label. */
132
+ type ImageAttachment = string | {
133
+ path: string;
134
+ label?: string;
135
+ };
136
+ /** Apple's built-in on-device tools (all macOS 27+, all local). */
137
+ type BuiltInTool = 'ocr' | 'barcode' | 'spotlight';
138
+ /** A history turn mirrored by the helper for a named session. */
139
+ interface HistoryTurn {
140
+ role: string;
141
+ content: string;
142
+ }
143
+ /**
144
+ * Private Cloud Compute quota, read from the framework without calling it.
145
+ *
146
+ * PCC *inference* needs an entitlement no installable package can ship, which is
147
+ * why the cloud tier goes through Shortcuts — but `quotaUsage` is readable from
148
+ * an unentitled process, so this is real first-party quota state rather than a
149
+ * guess parsed out of an error message.
150
+ */
151
+ interface CloudQuota {
152
+ isAvailable: boolean;
153
+ status: 'belowLimit' | 'limitReached' | 'unknown';
154
+ approachingLimit?: boolean;
155
+ resetDate?: string;
156
+ }
157
+ interface DeviceProbe {
158
+ available: boolean;
159
+ reason?: string;
160
+ contextSize?: number;
161
+ variant?: string;
162
+ /** macOS 27+ only; absent on macOS 26. */
163
+ capabilities?: ModelCapabilities;
164
+ useCases?: string[];
165
+ /** Helper feature flags; absent when the cached binary predates them. */
166
+ features?: HelperFeatures;
167
+ /** PCC quota, surfaced here because the on-device helper is what can read it. */
168
+ cloud?: CloudQuota;
169
+ }
170
+ /**
171
+ * How tokens are chosen.
172
+ *
173
+ * `greedy` is deterministic but degenerates under guided generation — it is the
174
+ * `temperature: 0` trap by another name. The seeded modes give the same
175
+ * reproducibility *without* that failure: `{ mode: 'topK', k: 50, seed: 42 }`
176
+ * returned byte-identical output across three runs here. Determinism relies on
177
+ * a fresh session per request, which is the default.
178
+ */
179
+ type SamplingMode = {
180
+ mode: 'greedy';
181
+ } | {
182
+ mode: 'topK';
183
+ k?: number;
184
+ seed?: number;
185
+ } | {
186
+ mode: 'threshold';
187
+ p?: number;
188
+ seed?: number;
189
+ };
190
+ /** Apple ships a use case specialised for tagging and topic extraction. */
191
+ type UseCase = 'general' | 'contentTagging';
192
+ /**
193
+ * `permissive` selects `permissiveContentTransformations`, which relaxes the
194
+ * guardrails for content *transformation* — rewriting or summarising text the
195
+ * default guardrails would refuse to touch.
196
+ */
197
+ type Guardrails = 'default' | 'permissive';
198
+ interface DeviceRequest {
199
+ system?: string;
200
+ prompt: string;
201
+ schema?: JsonSchema | null;
202
+ temperature?: number;
203
+ maxTokens?: number;
204
+ /** Image attachments. Entries may carry a label for follow-up turns. */
205
+ images?: ImageAttachment[];
206
+ /** Text documents inlined into the prompt client-side (see inlineDocuments). */
207
+ documents?: string[];
208
+ /** Named multi-turn conversation; the helper keeps the native transcript. */
209
+ sessionId?: string;
210
+ /** Built-in Apple tools: on-device OCR, barcode reading, Spotlight RAG. */
211
+ tools?: BuiltInTool[];
212
+ useCase?: UseCase;
213
+ guardrails?: Guardrails;
214
+ sampling?: SamplingMode;
215
+ /** Send the schema in the prompt as well as to the decoder. See helper.swift. */
216
+ includeSchemaInPrompt?: boolean;
217
+ /**
218
+ * Reuse one session across calls, as api-scribe did. Off by default: it makes
219
+ * unrelated calls share a transcript. See the note in helper.swift.
220
+ * Prefer sessionId for conversations; this flag is the legacy single slot.
221
+ */
222
+ reuseSession?: boolean;
223
+ }
224
+ /** Assert every requested tool is one the helper knows, before spending a call. */
225
+ declare function assertTools(tools: BuiltInTool[] | undefined): void;
226
+ /** Probe the on-device model without constructing a client. */
227
+ declare function probeDevice(onProgress?: OnProgress): Promise<DeviceProbe>;
228
+ /**
229
+ * The on-device tier: Apple's `FoundationModels` framework, reached through a
230
+ * self-compiled Swift helper. Nothing leaves the machine.
231
+ */
232
+ interface DeviceClientOptions {
233
+ useCase?: UseCase;
234
+ guardrails?: Guardrails;
235
+ }
236
+ declare class DeviceClient {
237
+ private readonly options;
238
+ private binary?;
239
+ private probeResult?;
240
+ private server?;
241
+ constructor(options?: DeviceClientOptions);
242
+ get label(): string;
243
+ get contextSize(): number | undefined;
244
+ ensureReady(onProgress?: OnProgress): Promise<void>;
245
+ getProbe(): DeviceProbe | undefined;
246
+ /** Send one envelope and return the parsed reply, raising a typed error on failure. */
247
+ private exchange;
248
+ /** One request. Returns the helper's `content` string, unparsed. */
249
+ complete(request: DeviceRequest): Promise<string>;
250
+ /**
251
+ * Streaming text. Deltas arrive via onDelta as the model generates; the
252
+ * promise resolves with the full text. Text only: the helper rejects
253
+ * schema+stream, because partial JSON is not a usable delta.
254
+ */
255
+ stream(prompt: string, options?: Omit<DeviceRequest, 'prompt' | 'schema'> & {
256
+ onDelta?: (delta: string) => void;
257
+ }): Promise<string>;
258
+ /**
259
+ * Conversation history for a named session: the mirrored turns the helper
260
+ * persisted, oldest first. Survives helper restarts; the native transcript
261
+ * does not, so treat a restart as a context break, not a loss.
262
+ */
263
+ history(sessionId: string): Promise<{
264
+ instructions: string;
265
+ history: HistoryTurn[];
266
+ }>;
267
+ /** Drop a named session (and its persisted history), or all of them. */
268
+ resetSession(sessionId?: string): Promise<void>;
269
+ /**
270
+ * How many tokens a prompt costs, before sending it.
271
+ *
272
+ * The point is to turn a ContextLengthError into an arithmetic check: compare
273
+ * against `contextSize` and trim, rather than discovering the ceiling by
274
+ * hitting it. Counts the instructions too, since they share the window.
275
+ */
276
+ countTokens(prompt: string, options?: {
277
+ system?: string;
278
+ images?: ImageAttachment[];
279
+ tools?: BuiltInTool[];
280
+ }): Promise<{
281
+ tokens: number;
282
+ contextSize: number;
283
+ }>;
284
+ /**
285
+ * Load the model assets now so the first real call does not pay for it.
286
+ *
287
+ * Cheap and idempotent, but do not expect much on a warm machine: with the
288
+ * assets already resident this measured 0.31s against 0.36s for an
289
+ * unprewarmed first call — inside the noise. The win is on a genuinely cold
290
+ * system, where the very first call to the framework here took 7.8s. Worth
291
+ * calling at startup when you know a request is coming; not worth building
292
+ * around.
293
+ */
294
+ prewarm(system?: string): Promise<void>;
295
+ text(prompt: string, options?: Omit<DeviceRequest, 'prompt' | 'schema'>): Promise<string>;
296
+ json(prompt: string, options: Omit<DeviceRequest, 'prompt'> & {
297
+ schema: JsonSchema;
298
+ }): Promise<unknown>;
299
+ /** Shut the helper process down. Safe to call more than once. */
300
+ close(): void;
301
+ }
302
+ /**
303
+ * Inline text documents into the prompt client-side.
304
+ *
305
+ * The helper's vision path handles images; plain-text sources (.txt/.md/.json
306
+ * and friends) are cheaper to splice here than to teach the Swift side about.
307
+ * Binary files are refused loudly — silently skipping a document the caller
308
+ * asked about would be the vision-drop trap by another name.
309
+ */
310
+ declare function withDocuments(prompt: string, documents: string[] | undefined): Promise<string>;
311
+ /** Parse a CLI --image value: "path" or "path::label". */
312
+ declare function parseImageFlag(value: string): ImageAttachment;
313
+
314
+ /**
315
+ * Apple's Private Cloud Compute model, reached through the Shortcuts action
316
+ * `is.workflow.actions.askllm`.
317
+ *
318
+ * Why this route rather than the framework: macOS 27 exposes
319
+ * `FoundationModels.PrivateCloudComputeLanguageModel` as public API and it even
320
+ * reports `isAvailable: true`, but every call fails with `ModelManagerError
321
+ * 1046` unless the process carries `com.apple.developer.private-cloud-compute`.
322
+ * That entitlement is AMFI-restricted — an ad-hoc-signed binary carrying it is
323
+ * SIGKILLed (exit 137), and wrapping it in a signed .app with a real bundle ID
324
+ * does not help. It needs a paid Developer Program provisioning profile, which
325
+ * no installable package can ship. Shortcuts.app already holds the entitlement
326
+ * and `/usr/bin/shortcuts` is public, so a generated shortcut is the only route
327
+ * that works. Do not spend time re-confirming this.
328
+ *
329
+ * This tier sends your prompt off the machine. It is not local.
330
+ *
331
+ * Measured on an M4 Air: ~2s typical, ~11s at 14k tokens.
332
+ */
333
+ /** Distinctive, so `shortcuts run` cannot match a shortcut the user wrote. */
334
+ declare const CLOUD_SHORTCUT_NAME = "Apple LLM Cloud";
335
+ /** Web search is fixed in the shortcut at install time, so it needs its own copy. */
336
+ declare const CLOUD_SHORTCUT_NAME_WEB = "Apple LLM Cloud Web";
337
+ /** Context window, measured empirically: 14.4k succeeds, ~33.5k is refused. */
338
+ declare const CLOUD_CONTEXT_TOKENS = 32768;
339
+ interface CloudProbe {
340
+ available: boolean;
341
+ reason?: string;
342
+ installed?: boolean;
343
+ contextSize?: number;
344
+ /**
345
+ * Real quota state, read from `PrivateCloudComputeLanguageModel.quotaUsage`
346
+ * by the on-device helper. Absent when the helper could not run (macOS 26, or
347
+ * no on-device model), since that is the only thing that can read it.
348
+ */
349
+ quota?: CloudQuota;
350
+ }
351
+ /**
352
+ * The shortcut definition, as plain JSON.
353
+ *
354
+ * Every key here was confirmed against a shortcut built in the Shortcuts GUI and
355
+ * exported, rather than guessed — the difference matters because a wrong
356
+ * parameter name does not fail loudly. Shortcuts imports the action, silently
357
+ * discards the unrecognised parameter, and the action then blocks on its
358
+ * interactive prompt at run time, forever. This is the single most expensive
359
+ * mistake available on this path.
360
+ *
361
+ * In particular the prompt key is `WFLLMPrompt`, *not* the `WFInput` that the
362
+ * action's own localised strings suggest.
363
+ *
364
+ * The model key is deliberately absent: with no model key the action uses its
365
+ * default, which is the Cloud (Private Cloud Compute) tier.
366
+ */
367
+ declare function shortcutDefinition(webSearch?: boolean): Record<string, unknown>;
368
+ declare function cloudSetupHint(): string;
369
+ /**
370
+ * Generate, sign and install the shortcut. Idempotent unless `force` is set.
371
+ *
372
+ * Signing needs no developer account and no signing identity: `shortcuts sign -m
373
+ * anyone` issues a per-signature certificate that chains to Apple Root CA - G3
374
+ * on the device. Every user signs their own copy, so nothing has to be
375
+ * pre-signed, hosted, or shipped in the package.
376
+ */
377
+ declare function installCloudShortcut(onProgress?: OnProgress, options?: {
378
+ force?: boolean;
379
+ webSearch?: boolean;
380
+ }): Promise<void>;
381
+ /** Probe the cloud tier without constructing a client. */
382
+ declare function probeCloud(): Promise<CloudProbe>;
383
+ interface CloudRequest {
384
+ system?: string;
385
+ prompt: string;
386
+ /** Run with "Use Broad World Knowledge" — needs the web shortcut installed. */
387
+ webSearch?: boolean;
388
+ }
389
+ /**
390
+ * The Private Cloud Compute tier. Free but quota'd, and text-out only: there is
391
+ * no constrained decoding here, so `json()` asks for JSON in the prompt and
392
+ * recovers it from prose rather than guaranteeing it.
393
+ */
394
+ declare class CloudClient {
395
+ private ready;
396
+ /** Last known quota, set by `probe()` so a call can fail fast. */
397
+ private quota?;
398
+ /**
399
+ * Tell the client what the framework reported about the quota.
400
+ *
401
+ * Worth doing because a `shortcuts run` against an exhausted quota costs a
402
+ * full round trip to find out; this turns that into an immediate typed error.
403
+ */
404
+ setQuota(quota: CloudQuota | undefined): void;
405
+ private assertQuota;
406
+ get label(): string;
407
+ get contextSize(): number;
408
+ ensureReady(onProgress?: OnProgress): Promise<void>;
409
+ text(request: CloudRequest): Promise<string>;
410
+ /**
411
+ * There is no constrained decoding on this tier, so the schema is spelled out
412
+ * in the prompt and the reply is mined for JSON. The shape is a request here,
413
+ * not a guarantee — unlike on device.
414
+ */
415
+ json(request: CloudRequest & {
416
+ schema: JsonSchema;
417
+ }): Promise<unknown>;
418
+ close(): void;
419
+ }
420
+
421
+ /**
422
+ * Recover JSON from free text.
423
+ *
424
+ * Only the cloud tier needs this. On device, constrained decoding guarantees the
425
+ * shape and the reply is already JSON. Private Cloud Compute returns prose, so
426
+ * the object has to be dug out of whatever the model wrapped around it.
427
+ *
428
+ * api-scribe's version only stripped fences when the reply *started* with one,
429
+ * which fails on the common "Here is the JSON:\n```json\n{...}\n```". This one
430
+ * also scans for a balanced object or array.
431
+ */
432
+ /** Strip a Markdown code fence anywhere in the text, preferring a json-tagged one. */
433
+ declare function stripCodeFences(text: string): string;
434
+ /**
435
+ * The first balanced `{...}` or `[...]` in the text, respecting strings and
436
+ * escapes so a brace inside a string value cannot end the scan early.
437
+ */
438
+ declare function extractJsonSpan(text: string): string | null;
439
+ /**
440
+ * Parse a model reply as JSON, tolerating fences and surrounding prose.
441
+ * Throws with a truncated echo of the reply, which is what a caller needs to see.
442
+ */
443
+ declare function parseLlmJson(raw: string): unknown;
444
+
445
+ /**
446
+ * True when this machine could plausibly run Apple's on-device model: Apple
447
+ * Silicon on macOS. Availability itself is confirmed by probing the helper.
448
+ */
449
+ declare function isAppleSiliconMac(): boolean;
450
+ /**
451
+ * Build the compile target from an SDK version string, e.g. `27.0` ->
452
+ * `arm64-apple-macos27.0`.
453
+ *
454
+ * swiftc's own default triple carries a patch component
455
+ * (`arm64-apple-macosx27.0.0`) that no shipped stdlib matches, so the target has
456
+ * to be spelled out. Pure so it can be tested against captured `xcrun` output on
457
+ * any machine.
458
+ */
459
+ declare function targetTripleFrom(versionOutput: string, arch?: string): string | null;
460
+ /** Ask the SDK, then the OS, for a version to build the triple from. */
461
+ declare function hostTarget(): Promise<string | null>;
462
+
463
+ /**
464
+ * Typed errors, so callers branch on a class rather than matching a message.
465
+ * Apple rewords its diagnostics between OS releases; the helper classifies
466
+ * failures at the Swift boundary and this module names them.
467
+ */
468
+ type Tier$1 = 'device' | 'cloud';
469
+ declare class AppleLLMError extends Error {
470
+ readonly tier?: Tier$1 | undefined;
471
+ constructor(message: string, tier?: Tier$1 | undefined);
472
+ }
473
+ /** Why the on-device model cannot be used. Distinguishable, because the fix differs. */
474
+ type UnavailableReason = 'appleIntelligenceNotEnabled' | 'modelNotReady' | 'deviceNotEligible' | 'unsupportedPlatform' | 'unsupportedOSVersion' | 'noSwiftCompiler' | 'unknown';
475
+ declare class ModelUnavailableError extends AppleLLMError {
476
+ readonly reason: UnavailableReason;
477
+ constructor(message: string, reason: UnavailableReason, tier?: Tier$1);
478
+ }
479
+ /** Apple's `GenerationSchema` decoder refused the schema. Almost always a dialect rule. */
480
+ declare class SchemaRejectedError extends AppleLLMError {
481
+ }
482
+ /** The prompt (plus transcript) exceeded the model's context window. */
483
+ declare class ContextLengthError extends AppleLLMError {
484
+ readonly contextSize?: number | undefined;
485
+ constructor(message: string, tier?: Tier$1, contextSize?: number | undefined);
486
+ }
487
+ /** Private Cloud Compute is rate limited, or the on-device model reported `rateLimited`. */
488
+ declare class QuotaError extends AppleLLMError {
489
+ readonly resetDate?: Date | undefined;
490
+ constructor(message: string, tier?: Tier$1, resetDate?: Date | undefined);
491
+ }
492
+ declare class TimeoutError extends AppleLLMError {
493
+ }
494
+ /** A one-time setup step has not been run — currently only the cloud shortcut. */
495
+ declare class SetupRequiredError extends AppleLLMError {
496
+ readonly step: string;
497
+ constructor(message: string, step: string, tier?: Tier$1);
498
+ }
499
+ /** The model declined to answer (guardrail or refusal). */
500
+ declare class RefusalError extends AppleLLMError {
501
+ }
502
+
503
+ /**
504
+ * apple-llm — one library for Apple's on-device and Private Cloud Compute models.
505
+ *
506
+ * macOS 26+ on Apple Silicon only. No API key, no account, no developer program.
507
+ * Extracted from api-scribe (MIT), which discovered and shipped both routes.
508
+ */
509
+
510
+ type Tier = 'device' | 'cloud' | 'auto';
511
+ interface ProbeResult {
512
+ device: DeviceProbe;
513
+ cloud: CloudProbe;
514
+ }
515
+ /**
516
+ * What this machine can actually do. Never throws: on Linux, an Intel Mac or
517
+ * macOS 25 it returns `available: false` with a reason naming the fix.
518
+ */
519
+ declare function probe(onProgress?: OnProgress): Promise<ProbeResult>;
520
+ interface AppleLLMOptions {
521
+ tier?: Tier;
522
+ /** Default sampling temperature. See DEFAULT_TEMPERATURE — do not set 0. */
523
+ temperature?: number;
524
+ maxTokens?: number;
525
+ /** Called for the one-time compile and the shortcut install; both take seconds. */
526
+ onProgress?: OnProgress;
527
+ /** `contentTagging` selects Apple's tagging-specialised model. Device tier only. */
528
+ useCase?: UseCase;
529
+ /** `permissive` relaxes guardrails for rewriting tasks. Device tier only. */
530
+ guardrails?: Guardrails;
531
+ /** Default sampling mode; a seeded one makes output reproducible. */
532
+ sampling?: SamplingMode;
533
+ }
534
+ interface TextOptions {
535
+ system?: string;
536
+ temperature?: number;
537
+ maxTokens?: number;
538
+ /** Cloud tier only: run with "Use Broad World Knowledge". */
539
+ webSearch?: boolean;
540
+ /** Image attachments; entries may carry a label for follow-up turns. */
541
+ images?: ImageAttachment[];
542
+ /** Text documents inlined into the prompt (device tier). */
543
+ documents?: string[];
544
+ /** Named multi-turn conversation (device tier). */
545
+ sessionId?: string;
546
+ /** Built-in Apple tools: ocr, barcode, spotlight (device, macOS 27+). */
547
+ tools?: BuiltInTool[];
548
+ useCase?: UseCase;
549
+ guardrails?: Guardrails;
550
+ sampling?: SamplingMode;
551
+ }
552
+ interface StreamOptions extends TextOptions {
553
+ onDelta?: (delta: string) => void;
554
+ }
555
+ interface JsonOptions extends TextOptions {
556
+ schema: JsonSchema;
557
+ }
558
+ /**
559
+ * The main entry point.
560
+ *
561
+ * const llm = new AppleLLM({ tier: 'device' });
562
+ * await llm.text('Summarize this');
563
+ * await llm.json('Extract the fields', { schema });
564
+ * llm.close();
565
+ */
566
+ declare class AppleLLM {
567
+ private device?;
568
+ private cloud?;
569
+ /** Which tier `auto` settled on, once resolved. */
570
+ private resolved?;
571
+ private readonly options;
572
+ constructor(options?: AppleLLMOptions);
573
+ /** Per-call device settings, falling back to the constructor defaults. */
574
+ private deviceDefaults;
575
+ get tier(): Tier;
576
+ /** Human-readable name of the tier in use, for logs. */
577
+ get label(): string;
578
+ /**
579
+ * Resolve the tier and do any one-time setup. Called automatically, but
580
+ * exposed so a caller can pay the compile cost up front with a progress bar.
581
+ */
582
+ ensureReady(onProgress?: OnProgress): Promise<void>;
583
+ text(prompt: string, options?: TextOptions): Promise<string>;
584
+ /**
585
+ * Streaming text (device tier only). Deltas arrive via onDelta as the model
586
+ * generates; the promise resolves with the full text. The Siri-app shape:
587
+ * partials first, final answer at the end.
588
+ */
589
+ stream(prompt: string, options?: StreamOptions): Promise<string>;
590
+ /** Conversation history for a named session (device tier only). */
591
+ history(sessionId: string): Promise<{
592
+ instructions: string;
593
+ history: HistoryTurn[];
594
+ }>;
595
+ /** Drop a named session, or all sessions when omitted (device tier only). */
596
+ resetSession(sessionId?: string): Promise<void>;
597
+ /**
598
+ * A named conversation: text/stream calls sharing one native transcript,
599
+ * like one thread in the Siri app. History persists across helper restarts;
600
+ * the native transcript does not (treated as a context break, not a loss).
601
+ */
602
+ conversation(sessionId: string, options?: {
603
+ system?: string;
604
+ }): Conversation;
605
+ /**
606
+ * Write with Siri, anywhere you type: drafting, rewriting and feedback
607
+ * built on the permissive-content-transformation guardrails. Device tier
608
+ * only — these are transformation tasks the default guardrails refuse.
609
+ */
610
+ rewrite(text: string, options?: {
611
+ instruction?: string;
612
+ system?: string;
613
+ } & TextOptions): Promise<string>;
614
+ proofread(text: string, options?: TextOptions): Promise<string>;
615
+ summarize(text: string, options?: {
616
+ length?: string;
617
+ } & TextOptions): Promise<string>;
618
+ draft(topic: string, options?: {
619
+ kind?: string;
620
+ } & TextOptions): Promise<string>;
621
+ tone(text: string, tone: string, options?: TextOptions): Promise<string>;
622
+ /**
623
+ * Ask about what's on screen: captures a screenshot (interactive selection
624
+ * by default, like Cmd+Shift+Space Visual Intelligence) and asks the model
625
+ * about it with vision. Device tier, macOS 27+.
626
+ */
627
+ askScreen(question: string, options?: {
628
+ mode?: 'interactive' | 'window' | 'fullscreen';
629
+ } & TextOptions): Promise<string>;
630
+ /**
631
+ * Ask for JSON. On device the schema is *guaranteed* by constrained decoding.
632
+ * On cloud it is requested in the prompt and recovered from the reply — the
633
+ * cloud tier has no constrained decoding, so a bad shape is possible there.
634
+ */
635
+ json(prompt: string, options: JsonOptions): Promise<unknown>;
636
+ /**
637
+ * api-scribe's `LlmClient` shape, so it can drop its four files and depend on
638
+ * this instead. Not used internally.
639
+ */
640
+ completeJson(system: string, user: string, schema: JsonSchema): Promise<unknown>;
641
+ /**
642
+ * How many tokens a prompt costs, before sending it. Device tier only.
643
+ *
644
+ * Turns a ContextLengthError into arithmetic: compare against `contextSize`
645
+ * and trim, rather than finding the ceiling by hitting it.
646
+ */
647
+ countTokens(prompt: string, options?: {
648
+ system?: string;
649
+ images?: ImageAttachment[];
650
+ tools?: BuiltInTool[];
651
+ }): Promise<{
652
+ tokens: number;
653
+ contextSize: number;
654
+ }>;
655
+ /**
656
+ * Load the model assets now so the first real call does not pay for it.
657
+ * Device tier only; a no-op elsewhere. See `DeviceClient.prewarm` for what it
658
+ * is actually worth (little, on a warm machine).
659
+ */
660
+ prewarm(system?: string): Promise<void>;
661
+ /** Release the long-lived helper process. Safe to call more than once. */
662
+ close(): void;
663
+ }
664
+ /**
665
+ * One thread in the Siri-app sense: every call carries the same sessionId,
666
+ * so the helper's native transcript accumulates across turns.
667
+ */
668
+ declare class Conversation {
669
+ private readonly llm;
670
+ readonly sessionId: string;
671
+ private readonly defaults;
672
+ constructor(llm: AppleLLM, sessionId: string, defaults?: {
673
+ system?: string;
674
+ });
675
+ text(prompt: string, options?: TextOptions): Promise<string>;
676
+ stream(prompt: string, options?: StreamOptions): Promise<string>;
677
+ history(): Promise<{
678
+ instructions: string;
679
+ history: HistoryTurn[];
680
+ }>;
681
+ reset(): Promise<void>;
682
+ }
683
+ /**
684
+ * Capture a screenshot to a temp file. Interactive selection mirrors the
685
+ * Visual Intelligence entry point (Cmd+Shift+Space): drag to select, and the
686
+ * path comes back ready to pass as an image attachment.
687
+ */
688
+ declare function captureScreenshot(mode?: 'interactive' | 'window' | 'fullscreen'): Promise<string>;
689
+
690
+ export { AppleLLM, AppleLLMError, type AppleLLMOptions, type BuiltInTool, CLOUD_CONTEXT_TOKENS, CLOUD_SHORTCUT_NAME, CLOUD_SHORTCUT_NAME_WEB, CloudClient, type CloudProbe, type CloudQuota, type CloudRequest, ContextLengthError, Conversation, DEFAULT_MAX_TOKENS, DEFAULT_TEMPERATURE, DeviceClient, type DeviceClientOptions, type DeviceProbe, type DeviceRequest, type Guardrails, type HelperFeatures, type HistoryTurn, type ImageAttachment, type JsonOptions, type JsonSchema, type ModelCapabilities, ModelUnavailableError, type OnProgress, type ProbeResult, type Progress, QuotaError, RefusalError, type SamplingMode, SchemaRejectedError, SetupRequiredError, type StreamOptions, type TextOptions, type Tier, TimeoutError, type UnavailableReason, type UseCase, assertTools, cacheDir, captureScreenshot, cloudSetupHint, ensureBinary, extractJsonSpan, fingerprint, helperSource, hostTarget, installCloudShortcut, isAppleSiliconMac, parseImageFlag, parseLlmJson, probe, probeCloud, probeDevice, shortcutDefinition, stripCodeFences, targetTripleFrom, toAppleSchema, withDocuments };