apple-llm 0.1.0 → 0.2.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.
- package/CHANGELOG.md +94 -0
- package/README.md +251 -112
- package/dist/ai-sdk.cjs +281 -0
- package/dist/ai-sdk.d.cts +54 -0
- package/dist/ai-sdk.d.ts +54 -0
- package/dist/ai-sdk.js +281 -0
- package/dist/chunk-GM325EMJ.js +2584 -0
- package/dist/chunk-NRDZIP5G.cjs +2588 -0
- package/dist/chunk-OQATUZWF.cjs +81 -0
- package/dist/chunk-ZB4RDEPW.js +81 -0
- package/dist/cli.js +3163 -24
- package/dist/client-CXewzZTj.d.cts +1160 -0
- package/dist/client-CXewzZTj.d.ts +1160 -0
- package/dist/index.cjs +110 -1667
- package/dist/index.d.cts +84 -627
- package/dist/index.d.ts +84 -627
- package/dist/index.js +31 -1
- package/dist/server.cjs +361 -0
- package/dist/server.d.cts +51 -0
- package/dist/server.d.ts +51 -0
- package/dist/server.js +361 -0
- package/package.json +66 -14
- package/swift/helper.swift +748 -255
- package/dist/chunk-FQTRQ3KP.js +0 -1590
- package/dist/cli.cjs +0 -2027
package/dist/server.js
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TempImages
|
|
3
|
+
} from "./chunk-ZB4RDEPW.js";
|
|
4
|
+
import {
|
|
5
|
+
AbortError,
|
|
6
|
+
AppleLLM,
|
|
7
|
+
AppleLLMError,
|
|
8
|
+
ContextLengthError,
|
|
9
|
+
ModelBusyError,
|
|
10
|
+
ModelUnavailableError,
|
|
11
|
+
QuotaError,
|
|
12
|
+
RefusalError,
|
|
13
|
+
SchemaRejectedError,
|
|
14
|
+
SchemaValidationError,
|
|
15
|
+
SetupRequiredError,
|
|
16
|
+
TimeoutError,
|
|
17
|
+
UnsupportedError,
|
|
18
|
+
probe,
|
|
19
|
+
tool
|
|
20
|
+
} from "./chunk-GM325EMJ.js";
|
|
21
|
+
|
|
22
|
+
// src/server.ts
|
|
23
|
+
import http from "http";
|
|
24
|
+
import { randomUUID } from "crypto";
|
|
25
|
+
var DEFAULT_PORT = 11436;
|
|
26
|
+
var MODELS = {
|
|
27
|
+
"apple-on-device": "device",
|
|
28
|
+
"apple-private-cloud": "cloud"
|
|
29
|
+
};
|
|
30
|
+
function errorResponse(error) {
|
|
31
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
32
|
+
const make = (status, type, code) => ({
|
|
33
|
+
status,
|
|
34
|
+
body: { error: { message, type, code: code ?? null, param: null } }
|
|
35
|
+
});
|
|
36
|
+
if (error instanceof ContextLengthError) return make(400, "invalid_request_error", "context_length_exceeded");
|
|
37
|
+
if (error instanceof SchemaRejectedError || error instanceof SchemaValidationError) {
|
|
38
|
+
return make(400, "invalid_request_error", "invalid_schema");
|
|
39
|
+
}
|
|
40
|
+
if (error instanceof UnsupportedError) return make(400, "invalid_request_error", "unsupported");
|
|
41
|
+
if (error instanceof RefusalError) return make(400, "invalid_request_error", "content_filter");
|
|
42
|
+
if (error instanceof QuotaError) return make(429, "rate_limit_error", "quota_exceeded");
|
|
43
|
+
if (error instanceof TimeoutError) return make(504, "timeout_error");
|
|
44
|
+
if (error instanceof AbortError) return make(499, "request_cancelled");
|
|
45
|
+
if (error instanceof ModelBusyError) return make(503, "service_unavailable", "model_busy");
|
|
46
|
+
if (error instanceof ModelUnavailableError || error instanceof SetupRequiredError) {
|
|
47
|
+
return make(503, "service_unavailable", "model_unavailable");
|
|
48
|
+
}
|
|
49
|
+
if (error instanceof HttpError) return make(error.status, "invalid_request_error");
|
|
50
|
+
if (error instanceof AppleLLMError) return make(500, "server_error");
|
|
51
|
+
return make(500, "server_error");
|
|
52
|
+
}
|
|
53
|
+
var HttpError = class extends Error {
|
|
54
|
+
constructor(status, message) {
|
|
55
|
+
super(message);
|
|
56
|
+
this.status = status;
|
|
57
|
+
}
|
|
58
|
+
status;
|
|
59
|
+
};
|
|
60
|
+
function textOf(content) {
|
|
61
|
+
if (typeof content === "string") return content;
|
|
62
|
+
if (content === null || content === void 0) return "";
|
|
63
|
+
if (Array.isArray(content)) {
|
|
64
|
+
return content.filter((p) => p.type === "text" || p.type === "input_text").map((p) => p.text ?? "").join("\n");
|
|
65
|
+
}
|
|
66
|
+
return String(content);
|
|
67
|
+
}
|
|
68
|
+
async function toMessages(raw, images, signal) {
|
|
69
|
+
if (!Array.isArray(raw) || raw.length === 0) throw new HttpError(400, "`messages` must be a non-empty array.");
|
|
70
|
+
const out = [];
|
|
71
|
+
for (const message of raw) {
|
|
72
|
+
switch (message.role) {
|
|
73
|
+
case "system":
|
|
74
|
+
case "developer":
|
|
75
|
+
out.push({ role: "system", content: textOf(message.content) });
|
|
76
|
+
break;
|
|
77
|
+
case "user": {
|
|
78
|
+
const paths = [];
|
|
79
|
+
if (Array.isArray(message.content)) {
|
|
80
|
+
for (const part of message.content) {
|
|
81
|
+
if (part.type === "image_url" && part.image_url !== void 0) {
|
|
82
|
+
const url = typeof part.image_url === "string" ? part.image_url : part.image_url.url;
|
|
83
|
+
paths.push(await images.add(url, void 0, signal));
|
|
84
|
+
} else if (part.type !== "text" && part.type !== "input_text") {
|
|
85
|
+
throw new HttpError(400, `Content part "${part.type}" is not supported.`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
out.push({ role: "user", content: textOf(message.content), ...paths.length > 0 ? { images: paths } : {} });
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
case "assistant":
|
|
93
|
+
out.push({
|
|
94
|
+
role: "assistant",
|
|
95
|
+
content: textOf(message.content),
|
|
96
|
+
...message.tool_calls !== void 0 && message.tool_calls.length > 0 ? {
|
|
97
|
+
toolCalls: message.tool_calls.map((call) => ({
|
|
98
|
+
id: call.id,
|
|
99
|
+
name: call.function.name,
|
|
100
|
+
arguments: parseArguments(call.function.arguments)
|
|
101
|
+
}))
|
|
102
|
+
} : {}
|
|
103
|
+
});
|
|
104
|
+
break;
|
|
105
|
+
case "tool":
|
|
106
|
+
out.push({ role: "tool", toolCallId: String(message.tool_call_id ?? ""), name: message.name, content: textOf(message.content) });
|
|
107
|
+
break;
|
|
108
|
+
default:
|
|
109
|
+
throw new HttpError(400, `Message role "${message.role}" is not supported.`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
function parseArguments(text) {
|
|
115
|
+
try {
|
|
116
|
+
return JSON.parse(text);
|
|
117
|
+
} catch {
|
|
118
|
+
return text;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function openAIToolCalls(calls) {
|
|
122
|
+
return calls.filter((call) => call.output === void 0).map((call, index) => ({
|
|
123
|
+
index,
|
|
124
|
+
id: call.id,
|
|
125
|
+
type: "function",
|
|
126
|
+
function: { name: call.name, arguments: JSON.stringify(call.arguments ?? {}) }
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
function finishReason(result) {
|
|
130
|
+
return result.finishReason === "tool-calls" ? "tool_calls" : result.finishReason;
|
|
131
|
+
}
|
|
132
|
+
function usageOf(result) {
|
|
133
|
+
if (result.usage === void 0) return void 0;
|
|
134
|
+
return {
|
|
135
|
+
prompt_tokens: result.usage.inputTokens,
|
|
136
|
+
completion_tokens: result.usage.outputTokens,
|
|
137
|
+
total_tokens: result.usage.totalTokens
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
async function readBody(req, limit = 32 * 1024 * 1024) {
|
|
141
|
+
const chunks = [];
|
|
142
|
+
let size = 0;
|
|
143
|
+
for await (const chunk of req) {
|
|
144
|
+
size += chunk.length;
|
|
145
|
+
if (size > limit) throw new HttpError(413, "Request body too large.");
|
|
146
|
+
chunks.push(chunk);
|
|
147
|
+
}
|
|
148
|
+
const text = Buffer.concat(chunks).toString("utf8");
|
|
149
|
+
if (text === "") return {};
|
|
150
|
+
try {
|
|
151
|
+
return JSON.parse(text);
|
|
152
|
+
} catch {
|
|
153
|
+
throw new HttpError(400, "Request body is not valid JSON.");
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function createServer(options = {}) {
|
|
157
|
+
const { port: _port, host: _host, tier: defaultTier = "device", apiKey, cors, log, ...llmOptions } = options;
|
|
158
|
+
const clients = /* @__PURE__ */ new Map();
|
|
159
|
+
const clientFor = (tier) => {
|
|
160
|
+
let client = clients.get(tier);
|
|
161
|
+
if (client === void 0) {
|
|
162
|
+
client = new AppleLLM({ ...llmOptions, tier });
|
|
163
|
+
clients.set(tier, client);
|
|
164
|
+
}
|
|
165
|
+
return client;
|
|
166
|
+
};
|
|
167
|
+
const server = http.createServer((req, res) => {
|
|
168
|
+
const started = Date.now();
|
|
169
|
+
const controller = new AbortController();
|
|
170
|
+
res.on("close", () => {
|
|
171
|
+
if (!res.writableFinished) controller.abort();
|
|
172
|
+
});
|
|
173
|
+
const send = (status, body) => {
|
|
174
|
+
if (res.headersSent) return;
|
|
175
|
+
res.writeHead(status, { "content-type": "application/json", ...corsHeaders() });
|
|
176
|
+
res.end(JSON.stringify(body));
|
|
177
|
+
};
|
|
178
|
+
const corsHeaders = () => cors === void 0 ? {} : {
|
|
179
|
+
"access-control-allow-origin": cors,
|
|
180
|
+
"access-control-allow-headers": "authorization, content-type",
|
|
181
|
+
"access-control-allow-methods": "GET, POST, OPTIONS"
|
|
182
|
+
};
|
|
183
|
+
const route = async () => {
|
|
184
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
185
|
+
const pathname = url.pathname.replace(/\/+$/, "") || "/";
|
|
186
|
+
if (req.method === "OPTIONS") {
|
|
187
|
+
res.writeHead(204, corsHeaders());
|
|
188
|
+
res.end();
|
|
189
|
+
return void 0;
|
|
190
|
+
}
|
|
191
|
+
if (apiKey !== void 0 && req.headers.authorization !== `Bearer ${apiKey}`) {
|
|
192
|
+
send(401, { error: { message: "Invalid API key.", type: "invalid_request_error", code: "invalid_api_key", param: null } });
|
|
193
|
+
return void 0;
|
|
194
|
+
}
|
|
195
|
+
if (req.method === "GET" && (pathname === "/health" || pathname === "/v1/health")) {
|
|
196
|
+
const state = await probe();
|
|
197
|
+
send(state.device.available || state.cloud.available ? 200 : 503, {
|
|
198
|
+
status: state.device.available || state.cloud.available ? "ok" : "unavailable",
|
|
199
|
+
device: { available: state.device.available, variant: state.device.variant, contextSize: state.device.contextSize, reason: state.device.reason },
|
|
200
|
+
cloud: { available: state.cloud.available, quota: state.cloud.quota?.status, reason: state.cloud.reason }
|
|
201
|
+
});
|
|
202
|
+
return void 0;
|
|
203
|
+
}
|
|
204
|
+
if (req.method === "GET" && pathname === "/v1/models") {
|
|
205
|
+
const created = Math.floor(Date.now() / 1e3);
|
|
206
|
+
send(200, {
|
|
207
|
+
object: "list",
|
|
208
|
+
data: Object.keys(MODELS).map((id) => ({ id, object: "model", created, owned_by: "apple" }))
|
|
209
|
+
});
|
|
210
|
+
return void 0;
|
|
211
|
+
}
|
|
212
|
+
if (req.method === "POST" && pathname === "/v1/chat/completions") {
|
|
213
|
+
const body = await readBody(req);
|
|
214
|
+
return chatCompletions(body);
|
|
215
|
+
}
|
|
216
|
+
send(404, { error: { message: `No route for ${req.method} ${pathname}.`, type: "invalid_request_error", code: "not_found", param: null } });
|
|
217
|
+
return void 0;
|
|
218
|
+
};
|
|
219
|
+
const chatCompletions = async (body) => {
|
|
220
|
+
const requested = body.model ?? "";
|
|
221
|
+
const tier = requested in MODELS ? MODELS[requested] : defaultTier;
|
|
222
|
+
const model = requested in MODELS ? requested : tier === "cloud" ? "apple-private-cloud" : "apple-on-device";
|
|
223
|
+
if (body.n !== void 0 && body.n !== 1) throw new HttpError(400, "Only n=1 is supported.");
|
|
224
|
+
const images = new TempImages();
|
|
225
|
+
try {
|
|
226
|
+
const messages = await toMessages(body.messages, images, controller.signal);
|
|
227
|
+
const functions = [];
|
|
228
|
+
if (body.tool_choice !== "none") {
|
|
229
|
+
for (const def of body.tools ?? []) {
|
|
230
|
+
if (def.type !== "function" || def.function === void 0) continue;
|
|
231
|
+
functions.push(tool({ name: def.function.name, description: def.function.description, parameters: def.function.parameters }));
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
let schema;
|
|
235
|
+
if (body.response_format?.type === "json_schema" && body.response_format.json_schema?.schema !== void 0) {
|
|
236
|
+
schema = body.response_format.json_schema.schema;
|
|
237
|
+
} else if (body.response_format?.type === "json_object") {
|
|
238
|
+
messages.unshift({ role: "system", content: "Reply with a single JSON object and nothing else." });
|
|
239
|
+
}
|
|
240
|
+
let sampling;
|
|
241
|
+
if (body.top_p !== void 0) sampling = { mode: "threshold", p: body.top_p, seed: body.seed };
|
|
242
|
+
else if (body.seed !== void 0) sampling = { mode: "topK", k: 50, seed: body.seed };
|
|
243
|
+
const call = {
|
|
244
|
+
temperature: body.temperature,
|
|
245
|
+
maxTokens: body.max_completion_tokens ?? body.max_tokens,
|
|
246
|
+
sampling,
|
|
247
|
+
tools: functions.length > 0 ? functions : void 0,
|
|
248
|
+
signal: controller.signal
|
|
249
|
+
};
|
|
250
|
+
const client = clientFor(tier);
|
|
251
|
+
const id = `chatcmpl-${randomUUID().replace(/-/g, "").slice(0, 24)}`;
|
|
252
|
+
const created = Math.floor(Date.now() / 1e3);
|
|
253
|
+
if (body.stream === true) {
|
|
254
|
+
res.writeHead(200, {
|
|
255
|
+
"content-type": "text/event-stream",
|
|
256
|
+
"cache-control": "no-cache",
|
|
257
|
+
connection: "keep-alive",
|
|
258
|
+
...corsHeaders()
|
|
259
|
+
});
|
|
260
|
+
const event = (payload) => {
|
|
261
|
+
if (!res.destroyed) res.write(`data: ${typeof payload === "string" ? payload : JSON.stringify(payload)}
|
|
262
|
+
|
|
263
|
+
`);
|
|
264
|
+
};
|
|
265
|
+
const chunk = (delta, finish = null) => {
|
|
266
|
+
event({ id, object: "chat.completion.chunk", created, model, choices: [{ index: 0, delta, finish_reason: finish, logprobs: null }] });
|
|
267
|
+
};
|
|
268
|
+
chunk({ role: "assistant", content: "" });
|
|
269
|
+
try {
|
|
270
|
+
let result2;
|
|
271
|
+
if (schema === void 0) {
|
|
272
|
+
const stream = client.stream(messages, call);
|
|
273
|
+
for await (const delta of stream) chunk({ content: delta });
|
|
274
|
+
result2 = await stream.result;
|
|
275
|
+
} else {
|
|
276
|
+
result2 = await client.generate(messages, { ...call, schema });
|
|
277
|
+
if (result2.finishReason !== "tool-calls") chunk({ content: result2.text });
|
|
278
|
+
}
|
|
279
|
+
const calls2 = openAIToolCalls(result2.toolCalls);
|
|
280
|
+
if (calls2.length > 0 && result2.finishReason === "tool-calls") chunk({ tool_calls: calls2 });
|
|
281
|
+
chunk({}, finishReason(result2));
|
|
282
|
+
if (body.stream_options?.include_usage === true) {
|
|
283
|
+
event({ id, object: "chat.completion.chunk", created, model, choices: [], usage: usageOf(result2) ?? null });
|
|
284
|
+
}
|
|
285
|
+
} catch (error) {
|
|
286
|
+
event(errorResponse(error).body);
|
|
287
|
+
if (!res.destroyed) res.end("data: [DONE]\n\n");
|
|
288
|
+
return `${model} stream failed: ${error instanceof Error ? error.message.split("\n")[0] : String(error)}`;
|
|
289
|
+
}
|
|
290
|
+
if (!res.destroyed) res.end("data: [DONE]\n\n");
|
|
291
|
+
return `${model} stream`;
|
|
292
|
+
}
|
|
293
|
+
const result = schema === void 0 ? await client.generate(messages, call) : await client.generate(messages, { ...call, schema });
|
|
294
|
+
const calls = result.finishReason === "tool-calls" ? openAIToolCalls(result.toolCalls) : [];
|
|
295
|
+
send(200, {
|
|
296
|
+
id,
|
|
297
|
+
object: "chat.completion",
|
|
298
|
+
created,
|
|
299
|
+
model,
|
|
300
|
+
choices: [
|
|
301
|
+
{
|
|
302
|
+
index: 0,
|
|
303
|
+
message: {
|
|
304
|
+
role: "assistant",
|
|
305
|
+
content: result.finishReason === "tool-calls" ? null : result.text,
|
|
306
|
+
...calls.length > 0 ? { tool_calls: calls.map(({ index: _i, ...rest }) => rest) } : {},
|
|
307
|
+
refusal: null
|
|
308
|
+
},
|
|
309
|
+
finish_reason: finishReason(result),
|
|
310
|
+
logprobs: null
|
|
311
|
+
}
|
|
312
|
+
],
|
|
313
|
+
usage: usageOf(result) ?? { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }
|
|
314
|
+
});
|
|
315
|
+
return model;
|
|
316
|
+
} finally {
|
|
317
|
+
await images.dispose();
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
route().then((what) => {
|
|
321
|
+
if (what !== void 0) log?.(`${req.method} ${req.url} ${what} ${res.statusCode} ${Date.now() - started}ms`);
|
|
322
|
+
}).catch((error) => {
|
|
323
|
+
const { status, body } = errorResponse(error);
|
|
324
|
+
if (error instanceof ModelBusyError && !res.headersSent) res.setHeader("retry-after", "2");
|
|
325
|
+
send(status, body);
|
|
326
|
+
log?.(`${req.method} ${req.url} ${status} ${Date.now() - started}ms ${error instanceof Error ? error.message.split("\n")[0] : ""}`);
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
server.on("close", () => {
|
|
330
|
+
for (const client of clients.values()) client.close();
|
|
331
|
+
});
|
|
332
|
+
return server;
|
|
333
|
+
}
|
|
334
|
+
async function serve(options = {}) {
|
|
335
|
+
const server = createServer(options);
|
|
336
|
+
const port = options.port ?? DEFAULT_PORT;
|
|
337
|
+
const host = options.host ?? "127.0.0.1";
|
|
338
|
+
await new Promise((resolve, reject) => {
|
|
339
|
+
server.once("error", reject);
|
|
340
|
+
server.listen(port, host, () => {
|
|
341
|
+
server.off("error", reject);
|
|
342
|
+
resolve();
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
const address = server.address();
|
|
346
|
+
const bound = typeof address === "object" && address !== null ? address.port : port;
|
|
347
|
+
return {
|
|
348
|
+
server,
|
|
349
|
+
url: `http://${host === "0.0.0.0" ? "127.0.0.1" : host}:${bound}/v1`,
|
|
350
|
+
close: () => new Promise((resolve) => {
|
|
351
|
+
server.close(() => resolve());
|
|
352
|
+
server.closeAllConnections?.();
|
|
353
|
+
})
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
export {
|
|
357
|
+
DEFAULT_PORT,
|
|
358
|
+
MODELS,
|
|
359
|
+
createServer,
|
|
360
|
+
serve
|
|
361
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apple-llm",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Apple's on-device (Foundation Models) and Private Cloud Compute LLMs from Node. No API key, no account
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Apple's on-device (Foundation Models) and Private Cloud Compute LLMs from Node: tools, streaming, structured output, an OpenAI-compatible server and a Vercel AI SDK provider. No API key, no account.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -15,13 +15,39 @@
|
|
|
15
15
|
"type": "module",
|
|
16
16
|
"main": "./dist/index.cjs",
|
|
17
17
|
"module": "./dist/index.js",
|
|
18
|
-
"types": "./dist/index.d.
|
|
18
|
+
"types": "./dist/index.d.cts",
|
|
19
19
|
"exports": {
|
|
20
20
|
".": {
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
"import": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"default": "./dist/index.js"
|
|
24
|
+
},
|
|
25
|
+
"require": {
|
|
26
|
+
"types": "./dist/index.d.cts",
|
|
27
|
+
"default": "./dist/index.cjs"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"./ai-sdk": {
|
|
31
|
+
"import": {
|
|
32
|
+
"types": "./dist/ai-sdk.d.ts",
|
|
33
|
+
"default": "./dist/ai-sdk.js"
|
|
34
|
+
},
|
|
35
|
+
"require": {
|
|
36
|
+
"types": "./dist/ai-sdk.d.cts",
|
|
37
|
+
"default": "./dist/ai-sdk.cjs"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"./server": {
|
|
41
|
+
"import": {
|
|
42
|
+
"types": "./dist/server.d.ts",
|
|
43
|
+
"default": "./dist/server.js"
|
|
44
|
+
},
|
|
45
|
+
"require": {
|
|
46
|
+
"types": "./dist/server.d.cts",
|
|
47
|
+
"default": "./dist/server.cjs"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"./package.json": "./package.json"
|
|
25
51
|
},
|
|
26
52
|
"bin": {
|
|
27
53
|
"apple-llm": "dist/cli.js"
|
|
@@ -30,32 +56,58 @@
|
|
|
30
56
|
"dist",
|
|
31
57
|
"swift",
|
|
32
58
|
"README.md",
|
|
59
|
+
"CHANGELOG.md",
|
|
33
60
|
"LICENSE"
|
|
34
61
|
],
|
|
62
|
+
"sideEffects": false,
|
|
35
63
|
"engines": {
|
|
36
64
|
"node": ">=18"
|
|
37
65
|
},
|
|
38
66
|
"keywords": [
|
|
67
|
+
"ai-sdk",
|
|
39
68
|
"apple",
|
|
40
|
-
"
|
|
69
|
+
"apple-intelligence",
|
|
70
|
+
"apple-silicon",
|
|
41
71
|
"foundation-models",
|
|
72
|
+
"function-calling",
|
|
73
|
+
"llm",
|
|
74
|
+
"local-llm",
|
|
75
|
+
"macos",
|
|
42
76
|
"on-device",
|
|
77
|
+
"openai-compatible",
|
|
43
78
|
"private-cloud-compute",
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
79
|
+
"streaming",
|
|
80
|
+
"structured-output",
|
|
81
|
+
"tool-calling",
|
|
82
|
+
"vercel-ai",
|
|
83
|
+
"zod"
|
|
48
84
|
],
|
|
49
85
|
"scripts": {
|
|
50
86
|
"build": "node ../../scripts/embed-helper.mjs && tsup",
|
|
51
87
|
"test": "vitest run",
|
|
52
88
|
"test:live": "APPLE_LLM_LIVE=1 vitest run",
|
|
53
|
-
"typecheck": "tsc --noEmit"
|
|
89
|
+
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.examples.json",
|
|
90
|
+
"check:package": "npm run build && publint && attw --pack . --profile node16"
|
|
91
|
+
},
|
|
92
|
+
"peerDependencies": {
|
|
93
|
+
"@ai-sdk/provider": ">=3.0.0"
|
|
94
|
+
},
|
|
95
|
+
"peerDependenciesMeta": {
|
|
96
|
+
"@ai-sdk/provider": {
|
|
97
|
+
"optional": true
|
|
98
|
+
}
|
|
54
99
|
},
|
|
55
100
|
"devDependencies": {
|
|
101
|
+
"@ai-sdk/provider": "^4.0.18",
|
|
102
|
+
"@arethetypeswrong/cli": "^0.18.5",
|
|
56
103
|
"@types/node": "^22.10.2",
|
|
104
|
+
"ai": "^7.0.114",
|
|
105
|
+
"openai": "^7.23.0",
|
|
106
|
+
"publint": "^0.3.24",
|
|
57
107
|
"tsup": "^8.3.5",
|
|
108
|
+
"tsx": "^4.23.15",
|
|
58
109
|
"typescript": "^5.7.2",
|
|
59
|
-
"vitest": "^2.1.8"
|
|
110
|
+
"vitest": "^2.1.8",
|
|
111
|
+
"zod": "^4.6.5"
|
|
60
112
|
}
|
|
61
113
|
}
|