@elizaos/app-core 2.0.0-beta.1 → 2.0.0-beta.2
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/package.json +2 -2
- package/platforms/electrobun/native/macos/window-effects.mm +103 -0
- package/platforms/electrobun/package.json +9 -0
- package/platforms/electrobun/src/__stubs__/bun-ffi.ts +16 -0
- package/platforms/electrobun/src/libMacWindowEffects.dylib +0 -0
- package/platforms/electrobun/src/native/agent.ts +74 -3
- package/platforms/electrobun/src/native/desktop.ts +39 -6
- package/platforms/electrobun/src/native/mac-window-effects.ts +61 -1
- package/platforms/electrobun/src/native/permissions-shared.ts +3 -2
- package/platforms/electrobun/src/native/permissions.ts +11 -6
- package/platforms/electrobun/src/rpc-handlers.ts +7 -0
- package/platforms/electrobun/src/rpc-schema.ts +39 -4
- package/platforms/electrobun/src/runtime-permissions.ts +7 -1
- package/runtime/ensure-local-inference-handler.d.ts +1 -0
- package/runtime/ensure-local-inference-handler.d.ts.map +1 -1
- package/runtime/ensure-local-inference-handler.js +9 -0
- package/runtime/mode/remote-forwarder.d.ts.map +1 -1
- package/runtime/mode/remote-forwarder.js +1 -1
- package/runtime/mode/runtime-mode.d.ts +20 -2
- package/runtime/mode/runtime-mode.d.ts.map +1 -1
- package/runtime/mode/runtime-mode.js +69 -1
- package/scripts/aosp/stage-default-models.mjs +2 -2
- package/scripts/build-llama-cpp-dflash.mjs +75 -40
- package/scripts/kernel-patches/metal-kernels.mjs +357 -337
- package/scripts/lib/read-app-identity.mjs +5 -1
- package/services/local-inference/catalog.d.ts +2 -1
- package/services/local-inference/catalog.d.ts.map +1 -1
- package/services/local-inference/catalog.js +131 -12
- package/services/local-inference/downloader.d.ts +2 -0
- package/services/local-inference/downloader.d.ts.map +1 -1
- package/services/local-inference/downloader.js +300 -1
- package/services/local-inference/manifest/validator.d.ts.map +1 -1
- package/services/local-inference/manifest/validator.js +48 -0
- package/services/local-inference/providers.d.ts +1 -1
- package/services/local-inference/providers.js +6 -6
- package/services/local-inference/registry.d.ts.map +1 -1
- package/services/local-inference/registry.js +10 -1
- package/services/local-inference/types.d.ts +6 -0
- package/services/local-inference/types.d.ts.map +1 -1
- package/test/helpers/real-runtime.ts +21 -20
- package/platforms/electrobun/src/native/permissions-darwin.ts +0 -342
- package/platforms/electrobun/src/native/permissions-linux.ts +0 -34
- package/platforms/electrobun/src/native/permissions-win32.ts +0 -56
|
@@ -28,6 +28,7 @@ import { localInferenceEngine } from "../services/local-inference/engine";
|
|
|
28
28
|
import { handlerRegistry } from "../services/local-inference/handler-registry";
|
|
29
29
|
import { listInstalledModels } from "../services/local-inference/registry";
|
|
30
30
|
import { installRouterHandler } from "../services/local-inference/router-handler";
|
|
31
|
+
import { getRuntimeMode } from "./mode/runtime-mode";
|
|
31
32
|
const LOCAL_INFERENCE_PROVIDER = "eliza-local-inference";
|
|
32
33
|
const DEVICE_BRIDGE_PROVIDER = "eliza-device-bridge";
|
|
33
34
|
const CAPACITOR_LLAMA_PROVIDER = "capacitor-llama";
|
|
@@ -46,6 +47,9 @@ const AOSP_LLAMA_PROVIDER = "eliza-aosp-llama";
|
|
|
46
47
|
* routing-policy layer picks between them).
|
|
47
48
|
*/
|
|
48
49
|
const LOCAL_INFERENCE_PRIORITY = 0;
|
|
50
|
+
export function shouldRegisterLocalInferenceHandlers(mode) {
|
|
51
|
+
return mode === "local" || mode === "local-only";
|
|
52
|
+
}
|
|
49
53
|
function getLoader(runtime) {
|
|
50
54
|
const candidate = runtime.getService?.("localInferenceLoader");
|
|
51
55
|
if (!candidate || typeof candidate !== "object")
|
|
@@ -278,6 +282,11 @@ async function tryRegisterCapacitorLoader(runtime) {
|
|
|
278
282
|
return false;
|
|
279
283
|
}
|
|
280
284
|
export async function ensureLocalInferenceHandler(runtime) {
|
|
285
|
+
const runtimeMode = getRuntimeMode();
|
|
286
|
+
if (!shouldRegisterLocalInferenceHandlers(runtimeMode)) {
|
|
287
|
+
logger.info(`[local-inference] Runtime mode is ${runtimeMode}; skipping local model handler registration`);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
281
290
|
const runtimeWithRegistration = runtime;
|
|
282
291
|
if (typeof runtimeWithRegistration.getModel !== "function" ||
|
|
283
292
|
typeof runtimeWithRegistration.registerModel !== "function") {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-forwarder.d.ts","sourceRoot":"","sources":["../../../../../../src/runtime/mode/remote-forwarder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAelC,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,OAAO,CAKT;AAgBD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,IAAI,CAAC,mBAAmB,EAClC,UAAU,EAAE,MAAM,EAClB,iBAAiB,EAAE,MAAM,GAAG,IAAI,GAC/B,OAAO,CAkBT;AAUD;;;;;GAKG;AACH,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,IAAI,CAAC,eAAe,EACzB,GAAG,EAAE,IAAI,CAAC,cAAc,GACvB,OAAO,CAAC,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"remote-forwarder.d.ts","sourceRoot":"","sources":["../../../../../../src/runtime/mode/remote-forwarder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAelC,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,OAAO,CAKT;AAgBD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,IAAI,CAAC,mBAAmB,EAClC,UAAU,EAAE,MAAM,EAClB,iBAAiB,EAAE,MAAM,GAAG,IAAI,GAC/B,OAAO,CAkBT;AAUD;;;;;GAKG;AACH,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,IAAI,CAAC,eAAe,EACzB,GAAG,EAAE,IAAI,CAAC,cAAc,GACvB,OAAO,CAAC,OAAO,CAAC,CAkDlB"}
|
|
@@ -96,7 +96,7 @@ export async function forwardRemoteCloudMutation(req, res) {
|
|
|
96
96
|
if (!shouldForwardToRemoteTarget(url.pathname, method))
|
|
97
97
|
return false;
|
|
98
98
|
if (!snapshot.remoteApiBase) {
|
|
99
|
-
sendJsonError(res, 503, "Remote target not configured");
|
|
99
|
+
sendJsonError(res, snapshot.remoteApiBaseError ? 400 : 503, snapshot.remoteApiBaseError ?? "Remote target not configured");
|
|
100
100
|
return true;
|
|
101
101
|
}
|
|
102
102
|
const targetUrl = new URL(`${url.pathname}${url.search}`, snapshot.remoteApiBase);
|
|
@@ -23,9 +23,11 @@ export interface RuntimeModeSnapshot {
|
|
|
23
23
|
mode: RuntimeMode;
|
|
24
24
|
deploymentTarget: DeploymentTargetConfig | null;
|
|
25
25
|
/** Present iff `mode === "remote"`. The local-instance HTTP base the
|
|
26
|
-
* controller proxies to. Cloud bases are rejected
|
|
27
|
-
*
|
|
26
|
+
* controller proxies to. Cloud/public bases are rejected here too so
|
|
27
|
+
* stale or hand-edited config cannot turn remote mode into cloud mode. */
|
|
28
28
|
remoteApiBase: string | null;
|
|
29
|
+
/** Populated when a remote target was configured but rejected. */
|
|
30
|
+
remoteApiBaseError: string | null;
|
|
29
31
|
remoteAccessToken: string | null;
|
|
30
32
|
}
|
|
31
33
|
declare const RuntimeModeConfigSchema: z.ZodObject<{
|
|
@@ -50,5 +52,21 @@ export declare function getRuntimeMode(): RuntimeMode;
|
|
|
50
52
|
export declare function getRuntimeModeSnapshot(): RuntimeModeSnapshot;
|
|
51
53
|
/** True for both `local` and `local-only`. */
|
|
52
54
|
export declare function isLocalRuntime(mode: RuntimeMode): boolean;
|
|
55
|
+
export interface RemoteApiBaseValidationOk {
|
|
56
|
+
ok: true;
|
|
57
|
+
href: string;
|
|
58
|
+
}
|
|
59
|
+
export interface RemoteApiBaseValidationErr {
|
|
60
|
+
ok: false;
|
|
61
|
+
error: string;
|
|
62
|
+
}
|
|
63
|
+
export type RemoteApiBaseValidation = RemoteApiBaseValidationOk | RemoteApiBaseValidationErr;
|
|
64
|
+
/**
|
|
65
|
+
* Remote mode is a thin controller for another local/private Eliza instance,
|
|
66
|
+
* never for Eliza Cloud or a public model API. Accept loopback, private
|
|
67
|
+
* RFC1918/CGNAT/link-local hosts, and .local mDNS names.
|
|
68
|
+
*/
|
|
69
|
+
export declare function validateRemoteApiBase(value: string | null | undefined): RemoteApiBaseValidation;
|
|
70
|
+
export declare function isLocalRemoteHost(hostname: string): boolean;
|
|
53
71
|
export {};
|
|
54
72
|
//# sourceMappingURL=runtime-mode.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-mode.d.ts","sourceRoot":"","sources":["../../../../../../src/runtime/mode/runtime-mode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EACL,KAAK,sBAAsB,EAE5B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,aAAa,qDAKhB,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,WAAW,CAAC;IAClB,gBAAgB,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAChD
|
|
1
|
+
{"version":3,"file":"runtime-mode.d.ts","sourceRoot":"","sources":["../../../../../../src/runtime/mode/runtime-mode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EACL,KAAK,sBAAsB,EAE5B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,aAAa,qDAKhB,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,WAAW,CAAC;IAClB,gBAAgB,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAChD;;+EAE2E;IAC3E,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,kEAAkE;IAClE,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAOD,QAAA,MAAM,uBAAuB;;;;;iBASb,CAAC;AAEjB,KAAK,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAatE;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,sBAAsB,GAAG,IAAI,GAAG,SAAS,GAChD,mBAAmB,CAuCrB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,WAAW,CAE5C;AAED,4BAA4B;AAC5B,wBAAgB,sBAAsB,IAAI,mBAAmB,CAE5D;AAED,8CAA8C;AAC9C,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAEzD;AAED,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,IAAI,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,0BAA0B;IACzC,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,uBAAuB,GAC/B,yBAAyB,GACzB,0BAA0B,CAAC;AAE/B;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC/B,uBAAuB,CAuBzB;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAsB3D"}
|
|
@@ -55,10 +55,13 @@ function parseRuntimeModeConfig(config) {
|
|
|
55
55
|
export function resolveRuntimeMode(config) {
|
|
56
56
|
const deploymentTarget = normalizeDeploymentTargetConfig(config?.deploymentTarget);
|
|
57
57
|
if (deploymentTarget?.runtime === "remote") {
|
|
58
|
+
const remoteApiBase = deploymentTarget.remoteApiBase?.trim() || null;
|
|
59
|
+
const remoteValidation = validateRemoteApiBase(remoteApiBase);
|
|
58
60
|
return {
|
|
59
61
|
mode: "remote",
|
|
60
62
|
deploymentTarget,
|
|
61
|
-
remoteApiBase:
|
|
63
|
+
remoteApiBase: remoteValidation.ok ? remoteValidation.href : null,
|
|
64
|
+
remoteApiBaseError: remoteValidation.ok ? null : remoteValidation.error,
|
|
62
65
|
remoteAccessToken: deploymentTarget.remoteAccessToken?.trim() || null,
|
|
63
66
|
};
|
|
64
67
|
}
|
|
@@ -67,6 +70,7 @@ export function resolveRuntimeMode(config) {
|
|
|
67
70
|
mode: "cloud",
|
|
68
71
|
deploymentTarget,
|
|
69
72
|
remoteApiBase: null,
|
|
73
|
+
remoteApiBaseError: null,
|
|
70
74
|
remoteAccessToken: null,
|
|
71
75
|
};
|
|
72
76
|
}
|
|
@@ -78,6 +82,7 @@ export function resolveRuntimeMode(config) {
|
|
|
78
82
|
mode: cloudExplicitlyDisabled ? "local-only" : "local",
|
|
79
83
|
deploymentTarget: deploymentTarget ?? null,
|
|
80
84
|
remoteApiBase: null,
|
|
85
|
+
remoteApiBaseError: null,
|
|
81
86
|
remoteAccessToken: null,
|
|
82
87
|
};
|
|
83
88
|
}
|
|
@@ -97,3 +102,66 @@ export function getRuntimeModeSnapshot() {
|
|
|
97
102
|
export function isLocalRuntime(mode) {
|
|
98
103
|
return mode === "local" || mode === "local-only";
|
|
99
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Remote mode is a thin controller for another local/private Eliza instance,
|
|
107
|
+
* never for Eliza Cloud or a public model API. Accept loopback, private
|
|
108
|
+
* RFC1918/CGNAT/link-local hosts, and .local mDNS names.
|
|
109
|
+
*/
|
|
110
|
+
export function validateRemoteApiBase(value) {
|
|
111
|
+
const raw = value?.trim();
|
|
112
|
+
if (!raw) {
|
|
113
|
+
return { ok: false, error: "Remote target not configured" };
|
|
114
|
+
}
|
|
115
|
+
let url;
|
|
116
|
+
try {
|
|
117
|
+
url = new URL(raw);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return { ok: false, error: "Remote target must be a valid URL" };
|
|
121
|
+
}
|
|
122
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
123
|
+
return { ok: false, error: "Remote target must use http or https" };
|
|
124
|
+
}
|
|
125
|
+
if (!isLocalRemoteHost(url.hostname)) {
|
|
126
|
+
return {
|
|
127
|
+
ok: false,
|
|
128
|
+
error: "Remote mode can only target loopback, .local, or private-network Eliza instances",
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
url.pathname = url.pathname.replace(/\/+$/, "");
|
|
132
|
+
return { ok: true, href: url.toString() };
|
|
133
|
+
}
|
|
134
|
+
export function isLocalRemoteHost(hostname) {
|
|
135
|
+
const host = hostname.trim().toLowerCase().replace(/^\[(.*)\]$/, "$1");
|
|
136
|
+
if (!host)
|
|
137
|
+
return false;
|
|
138
|
+
if (host === "localhost" || host.endsWith(".localhost"))
|
|
139
|
+
return true;
|
|
140
|
+
if (host.endsWith(".local"))
|
|
141
|
+
return true;
|
|
142
|
+
if (host === "::1")
|
|
143
|
+
return true;
|
|
144
|
+
if (host.startsWith("fe80:"))
|
|
145
|
+
return true;
|
|
146
|
+
const parts = host.split(".");
|
|
147
|
+
if (parts.length !== 4)
|
|
148
|
+
return false;
|
|
149
|
+
const octets = parts.map((p) => Number(p));
|
|
150
|
+
if (!octets.every((n) => Number.isInteger(n) && n >= 0 && n <= 255)) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
const [a, b] = octets;
|
|
154
|
+
if (a === 10)
|
|
155
|
+
return true;
|
|
156
|
+
if (a === 127)
|
|
157
|
+
return true;
|
|
158
|
+
if (a === 169 && b === 254)
|
|
159
|
+
return true;
|
|
160
|
+
if (a === 172 && b >= 16 && b <= 31)
|
|
161
|
+
return true;
|
|
162
|
+
if (a === 192 && b === 168)
|
|
163
|
+
return true;
|
|
164
|
+
if (a === 100 && b >= 64 && b <= 127)
|
|
165
|
+
return true;
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
@@ -68,7 +68,7 @@ const repoRoot = resolveRepoRootFromImportMeta(import.meta.url);
|
|
|
68
68
|
const CHAT_MODEL_ELIZA_1_MOBILE = {
|
|
69
69
|
id: "eliza-1-mobile-1_7b",
|
|
70
70
|
displayName: "Eliza-1 mobile",
|
|
71
|
-
hfRepo: "
|
|
71
|
+
hfRepo: "elizalabs/eliza-1-mobile-1_7b",
|
|
72
72
|
ggufFile: "text/eliza-1-mobile-1_7b-32k.gguf",
|
|
73
73
|
expectedMinBytes: 900 * 1024 * 1024,
|
|
74
74
|
expectedMaxBytes: 1700 * 1024 * 1024,
|
|
@@ -78,7 +78,7 @@ const CHAT_MODEL_ELIZA_1_MOBILE = {
|
|
|
78
78
|
const EMBEDDING_MODEL_ELIZA_1_LITE = {
|
|
79
79
|
id: "eliza-1-lite-0_6b",
|
|
80
80
|
displayName: "Eliza-1 lite",
|
|
81
|
-
hfRepo: "
|
|
81
|
+
hfRepo: "elizalabs/eliza-1-lite-0_6b",
|
|
82
82
|
ggufFile: "text/eliza-1-lite-0_6b-32k.gguf",
|
|
83
83
|
expectedMinBytes: 300 * 1024 * 1024,
|
|
84
84
|
expectedMaxBytes: 800 * 1024 * 1024,
|
|
@@ -647,8 +647,8 @@ function cmakeFlagsForTarget(target, ctx) {
|
|
|
647
647
|
// EMBED_LIBRARY behavior:
|
|
648
648
|
//
|
|
649
649
|
// * iOS (later in this function) sets it to ON unconditionally —
|
|
650
|
-
// the static-archive build needs
|
|
651
|
-
// the framework via .incbin.
|
|
650
|
+
// the static-archive build needs compiled metallib bytes baked
|
|
651
|
+
// into the framework via .incbin.
|
|
652
652
|
// * Darwin desktop (this branch) sets it OFF so the metallib is
|
|
653
653
|
// compiled at build time into a sidecar default.metallib that
|
|
654
654
|
// ships next to llama-server. The kernel-patches/metal-kernels
|
|
@@ -657,13 +657,10 @@ function cmakeFlagsForTarget(target, ctx) {
|
|
|
657
657
|
// is compiled into its own .air and merged into default.metallib
|
|
658
658
|
// alongside ggml-metal.air.
|
|
659
659
|
//
|
|
660
|
-
// The EMBED path is
|
|
661
|
-
//
|
|
662
|
-
//
|
|
663
|
-
//
|
|
664
|
-
// plus QK_POLAR / QK_QJL / QJL_RESIDUAL_BYTES already in
|
|
665
|
-
// ggml-common.h. Wiring them through the EMBED path requires a
|
|
666
|
-
// dup-strip pass that is filed as a separate follow-up.
|
|
660
|
+
// The EMBED path is patched by kernel-patches/metal-kernels.mjs
|
|
661
|
+
// to embed a compiled default.metallib instead of concatenated
|
|
662
|
+
// source. That keeps the standalone shader TUs separate and
|
|
663
|
+
// avoids duplicate block_* / constant declarations.
|
|
667
664
|
flags.push("-DGGML_METAL_EMBED_LIBRARY=OFF");
|
|
668
665
|
} else if (backend === "cuda") {
|
|
669
666
|
flags[flags.indexOf("-DGGML_CUDA=OFF")] = "-DGGML_CUDA=ON";
|
|
@@ -826,13 +823,11 @@ function cmakeFlagsForTarget(target, ctx) {
|
|
|
826
823
|
} else {
|
|
827
824
|
flags.push("-DCMAKE_OSX_SYSROOT=iphoneos");
|
|
828
825
|
}
|
|
829
|
-
// iOS static-archive build needs
|
|
830
|
-
// since there is no
|
|
831
|
-
//
|
|
832
|
-
//
|
|
833
|
-
//
|
|
834
|
-
// Keep this flag correct for the future patch, but do not rely on late
|
|
835
|
-
// requiredKernelsMissing() failure for today's unsupported path.
|
|
826
|
+
// iOS static-archive build needs compiled metallib data baked in via
|
|
827
|
+
// .incbin since there is no sidecar default.metallib next to a console
|
|
828
|
+
// binary. The Metal patcher rewrites the EMBED_LIBRARY CMake branch to
|
|
829
|
+
// compile ggml-metal.metal + the milady standalones into one metallib
|
|
830
|
+
// before embedding it.
|
|
836
831
|
const embedIdx = flags.indexOf("-DGGML_METAL_EMBED_LIBRARY=OFF");
|
|
837
832
|
if (embedIdx !== -1) {
|
|
838
833
|
flags[embedIdx] = "-DGGML_METAL_EMBED_LIBRARY=ON";
|
|
@@ -857,17 +852,6 @@ function targetCompatibility(target, ctx) {
|
|
|
857
852
|
"fused (omnivoice-grafted) targets are desktop/server only — mobile fusion is not wired yet",
|
|
858
853
|
};
|
|
859
854
|
}
|
|
860
|
-
if (platform === "ios" && backend === "metal") {
|
|
861
|
-
return {
|
|
862
|
-
ok: false,
|
|
863
|
-
reason:
|
|
864
|
-
"iOS Metal cannot yet ship the full Eliza-1 kernel set: the current " +
|
|
865
|
-
"metal-kernels.mjs patch wires only the non-EMBED default.metallib " +
|
|
866
|
-
"branch used by darwin host builds. iOS uses GGML_METAL_EMBED_LIBRARY=ON, " +
|
|
867
|
-
"so TurboQuant/QJL/Polar standalones are not embedded. Refusing before " +
|
|
868
|
-
"configure/build; wire the embedded metallib path before enabling this target.",
|
|
869
|
-
};
|
|
870
|
-
}
|
|
871
855
|
if (platform === "darwin" && process.platform !== "darwin") {
|
|
872
856
|
return { ok: false, reason: "darwin target requires macOS host" };
|
|
873
857
|
}
|
|
@@ -1044,6 +1028,12 @@ function ensureCheckout(cacheDir, ref) {
|
|
|
1044
1028
|
if (fs.existsSync(path.join(cacheDir, ".git"))) {
|
|
1045
1029
|
run("git", ["fetch", "--depth=1", "origin", ref], { cwd: cacheDir });
|
|
1046
1030
|
run("git", ["checkout", "FETCH_HEAD"], { cwd: cacheDir });
|
|
1031
|
+
// This checkout is a generated build cache owned by this script. Always
|
|
1032
|
+
// reset tracked and untracked source edits before applying our patch set
|
|
1033
|
+
// so stale kernel-patch artifacts from prior builds cannot leak into a
|
|
1034
|
+
// new artifact.
|
|
1035
|
+
run("git", ["reset", "--hard", "FETCH_HEAD"], { cwd: cacheDir });
|
|
1036
|
+
run("git", ["clean", "-fd"], { cwd: cacheDir });
|
|
1047
1037
|
} else {
|
|
1048
1038
|
fs.mkdirSync(path.dirname(cacheDir), { recursive: true });
|
|
1049
1039
|
run("git", ["clone", "--depth=1", "--branch", ref, REMOTE, cacheDir]);
|
|
@@ -1097,12 +1087,11 @@ function ensureCheckout(cacheDir, ref) {
|
|
|
1097
1087
|
// wiring is a separate fork-internals patch and is the next agent's
|
|
1098
1088
|
// mission.
|
|
1099
1089
|
//
|
|
1100
|
-
// * The EMBED_LIBRARY=ON branch (used by iOS targets) is
|
|
1101
|
-
//
|
|
1102
|
-
//
|
|
1103
|
-
//
|
|
1104
|
-
//
|
|
1105
|
-
// module comment.
|
|
1090
|
+
// * The EMBED_LIBRARY=ON branch (used by iOS targets) is also patched:
|
|
1091
|
+
// it compiles ggml-metal.metal + the milady standalones as separate
|
|
1092
|
+
// .air files, merges them into one default.metallib, and embeds those
|
|
1093
|
+
// compiled bytes. That avoids duplicate source declarations while
|
|
1094
|
+
// shipping the same five kernel symbols as the desktop sidecar.
|
|
1106
1095
|
//
|
|
1107
1096
|
// * Vulkan: the 8 standalone .comp files are staged into
|
|
1108
1097
|
// ggml/src/ggml-vulkan/vulkan-shaders/ (picked up by file(GLOB)),
|
|
@@ -1111,10 +1100,10 @@ function ensureCheckout(cacheDir, ref) {
|
|
|
1111
1100
|
// SPV blobs are linked into libggml-vulkan.so. Symbol audit (`nm
|
|
1112
1101
|
// libggml-vulkan.so | grep milady_`) passes; op-level dispatch routing
|
|
1113
1102
|
// for GGML_OP_ATTN_SCORE_QJL and the milady GGML_TYPE_* values is the
|
|
1114
|
-
// remaining gap and is the next agent's mission.
|
|
1115
|
-
//
|
|
1116
|
-
//
|
|
1117
|
-
//
|
|
1103
|
+
// remaining gap and is the next agent's mission. Incomplete Vulkan
|
|
1104
|
+
// artifacts are now publish-blocking: probeKernels() marks symbol-only
|
|
1105
|
+
// QJL/Polar/Turbo paths false until op-level dispatch is numerically
|
|
1106
|
+
// verified.
|
|
1118
1107
|
function applyForkPatches(cacheDir, backend, target, { dryRun = false } = {}) {
|
|
1119
1108
|
if (backend === "metal") {
|
|
1120
1109
|
patchMetalKernelsImpl(cacheDir, { dryRun });
|
|
@@ -1294,9 +1283,17 @@ function probeKernels(target, buildDir, outDir) {
|
|
|
1294
1283
|
// Honesty gate: Metal/Vulkan standalone shaders can compile and be present
|
|
1295
1284
|
// as symbols/pipelines while still being unreachable or semantically wrong
|
|
1296
1285
|
// through generic llama.cpp ops. Do not let symbol presence satisfy
|
|
1297
|
-
// AGENTS.md §3. Flip these back to true only after dedicated
|
|
1298
|
-
//
|
|
1299
|
-
|
|
1286
|
+
// AGENTS.md §3. Flip these back to true only after dedicated dispatch is
|
|
1287
|
+
// wired. Metal currently has a dedicated GGML_OP_ATTN_SCORE_QJL bridge;
|
|
1288
|
+
// Turbo and Polar remain symbol-only.
|
|
1289
|
+
if (backend === "metal") {
|
|
1290
|
+
const shipped = probeMetalShippedKernelSymbols(buildDir, outDir).symbols;
|
|
1291
|
+
kernels.turbo3 = false;
|
|
1292
|
+
kernels.turbo4 = false;
|
|
1293
|
+
kernels.turbo3_tcq = false;
|
|
1294
|
+
kernels.qjl_full = Boolean(shipped.qjl_full);
|
|
1295
|
+
kernels.polarquant = false;
|
|
1296
|
+
} else if (backend === "vulkan") {
|
|
1300
1297
|
kernels.turbo3 = false;
|
|
1301
1298
|
kernels.turbo4 = false;
|
|
1302
1299
|
kernels.turbo3_tcq = false;
|
|
@@ -1354,6 +1351,41 @@ function requiredKernelsMissing(target, kernels) {
|
|
|
1354
1351
|
return required.filter((k) => !kernels[k]);
|
|
1355
1352
|
}
|
|
1356
1353
|
|
|
1354
|
+
function probeMetalShippedKernelSymbols(buildDir, outDir) {
|
|
1355
|
+
const shipped = {
|
|
1356
|
+
turbo3: false,
|
|
1357
|
+
turbo4: false,
|
|
1358
|
+
turbo3_tcq: false,
|
|
1359
|
+
qjl_full: false,
|
|
1360
|
+
polarquant: false,
|
|
1361
|
+
};
|
|
1362
|
+
const metallibs = [
|
|
1363
|
+
...collectFilesUnder(buildDir, /\.metallib$/),
|
|
1364
|
+
...collectFilesUnder(outDir, /\.metallib$/),
|
|
1365
|
+
];
|
|
1366
|
+
for (const metallibPath of metallibs) {
|
|
1367
|
+
const bytes = fs.readFileSync(metallibPath);
|
|
1368
|
+
const has = (sym) => bytes.indexOf(sym) !== -1;
|
|
1369
|
+
if (has("kernel_turbo3_dot")) shipped.turbo3 = true;
|
|
1370
|
+
if (has("kernel_turbo4_dot")) shipped.turbo4 = true;
|
|
1371
|
+
if (has("kernel_turbo3_tcq_dot")) shipped.turbo3_tcq = true;
|
|
1372
|
+
if (
|
|
1373
|
+
has("kernel_attn_score_qjl1_256") ||
|
|
1374
|
+
has("kernel_get_rows_qjl1_256") ||
|
|
1375
|
+
has("kernel_mul_mv_qjl1_256_f32")
|
|
1376
|
+
) {
|
|
1377
|
+
shipped.qjl_full = true;
|
|
1378
|
+
}
|
|
1379
|
+
if (
|
|
1380
|
+
has("kernel_get_rows_q4_polar") ||
|
|
1381
|
+
has("kernel_mul_mv_q4_polar_f32")
|
|
1382
|
+
) {
|
|
1383
|
+
shipped.polarquant = true;
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
return { metallibs, symbols: shipped };
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1357
1389
|
function writeCapabilities({
|
|
1358
1390
|
outDir,
|
|
1359
1391
|
target,
|
|
@@ -1365,6 +1397,8 @@ function writeCapabilities({
|
|
|
1365
1397
|
const { platform, arch, backend, fused } = parseTarget(target);
|
|
1366
1398
|
const kernels = probeKernels(target, buildDir, outDir);
|
|
1367
1399
|
const missing = requiredKernelsMissing(target, kernels);
|
|
1400
|
+
const shippedKernels =
|
|
1401
|
+
backend === "metal" ? probeMetalShippedKernelSymbols(buildDir, outDir) : null;
|
|
1368
1402
|
const capabilities = {
|
|
1369
1403
|
target,
|
|
1370
1404
|
platform,
|
|
@@ -1375,6 +1409,7 @@ function writeCapabilities({
|
|
|
1375
1409
|
fork: "milady-ai/llama.cpp",
|
|
1376
1410
|
forkCommit,
|
|
1377
1411
|
kernels,
|
|
1412
|
+
shippedKernels,
|
|
1378
1413
|
binaries,
|
|
1379
1414
|
omnivoice,
|
|
1380
1415
|
};
|