-
Notifications
You must be signed in to change notification settings - Fork 0
Real on-device inference NOW: CoreAI Apple Text node (FoundationModels, macOS 26) #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| """ | ||
| nodes/apple_text.py — CoreAI Apple Text (FoundationModels). | ||
|
|
||
| REAL on-device text generation using Apple's SYSTEM language model | ||
| (FoundationModels, macOS 26+, Apple Intelligence enabled), via the | ||
| tools/fm-generate Swift CLI. | ||
|
|
||
| This is distinct from the coreai-catalog `.aimodel` vision models (depth, SAM, | ||
| detection, VLM, image-gen) — those run on the coreai-runner over Core AI and need | ||
| macOS 27's `CoreAI` framework. FoundationModels ships with macOS 26 and is | ||
| text-only, so this node runs TODAY without the runner or macOS 27. Useful for | ||
| generating / expanding prompts for the diffusion node, on-device and private. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import logging | ||
| import os | ||
| import subprocess | ||
| from pathlib import Path | ||
| from typing import Any | ||
|
|
||
| logger = logging.getLogger("ComfyUI-CoreAI") | ||
|
|
||
|
|
||
| def _fm_binary() -> str | None: | ||
| """Locate the compiled fm-generate CLI (env override, then repo tools/).""" | ||
| env = os.environ.get("COREAI_FM_PATH") | ||
| if env and Path(env).exists(): | ||
| return env | ||
| # comfyui_coreai/nodes/apple_text.py -> repo root -> tools/fm-generate | ||
| cand = Path(__file__).resolve().parents[2] / "tools" / "fm-generate" | ||
| if cand.exists() and os.access(cand, os.X_OK): | ||
| return str(cand) | ||
| return None | ||
|
|
||
|
|
||
| class CoreAIAppleText: | ||
| """On-device text generation with Apple's FoundationModels (macOS 26+). | ||
|
|
||
| Text-only (FoundationModels has no image input on macOS 26). Requires Apple | ||
| Intelligence enabled; the system model downloads on first use. | ||
| """ | ||
|
|
||
| @classmethod | ||
| def INPUT_TYPES(cls) -> dict[str, Any]: | ||
| return { | ||
| "required": { | ||
| "prompt": ( | ||
| "STRING", | ||
| { | ||
| "default": "Write a vivid one-line image prompt: a serene mountain lake at golden hour.", | ||
| "multiline": True, | ||
| "tooltip": "Apple's on-device text model (FoundationModels). " | ||
| "Runs on macOS 26+ with Apple Intelligence on — no coreai-runner needed.", | ||
| }, | ||
| ), | ||
| } | ||
| } | ||
|
|
||
| RETURN_TYPES = ("STRING",) | ||
| RETURN_NAMES = ("text",) | ||
| FUNCTION = "generate" | ||
| CATEGORY = "CoreAI/Apple" | ||
|
|
||
| def generate(self, prompt: str): | ||
| binary = _fm_binary() | ||
| if not binary: | ||
| return ( | ||
| "[FoundationModels backend not built — run tools/build_fm.sh " | ||
| "(macOS 26+, Apple Silicon).]", | ||
| ) | ||
| try: | ||
| result = subprocess.run( | ||
| [binary, "--prompt", prompt], | ||
| capture_output=True, | ||
| text=True, | ||
| timeout=120, | ||
| ) | ||
| except Exception as e: # noqa: BLE001 | ||
| logger.warning("fm-generate invocation failed: %s", e) | ||
| return (f"[fm-generate failed: {e}]",) | ||
|
|
||
| if result.returncode == 0: | ||
| return (result.stdout.strip(),) | ||
| if result.returncode == 3: | ||
| return ( | ||
| f"[Apple model unavailable — {result.stderr.strip()}. Enable Apple " | ||
| "Intelligence in System Settings; the model downloads on first use.]", | ||
| ) | ||
| return (f"[Apple model error: {result.stderr.strip() or result.returncode}]",) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| #!/usr/bin/env bash | ||
| # Build the FoundationModels backend CLI (macOS 26+, Apple Silicon). | ||
| set -euo pipefail | ||
| cd "$(dirname "$0")" | ||
| swiftc -O -parse-as-library fm-generate.swift -o fm-generate | ||
| echo "✓ built tools/fm-generate — test: ./fm-generate --prompt 'hi'" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| // fm-generate.swift — REAL on-device text generation via Apple's FoundationModels | ||
| // (macOS 26+, Apple Intelligence enabled). This is Apple's SYSTEM language model — | ||
| // NOT a coreai-catalog .aimodel (those are vision models needing macOS 27's CoreAI | ||
| // framework). Text-only: FoundationModels on macOS 26 has no image input. | ||
| // | ||
| // Build: swiftc -O -parse-as-library tools/fm-generate.swift -o tools/fm-generate | ||
| // Run: tools/fm-generate --prompt "Describe the Apple Neural Engine in one sentence." | ||
| // | ||
| // Exit codes: 0 ok · 1 generation error · 3 model unavailable (stderr: FM_UNAVAILABLE:<reason>) | ||
| import Foundation | ||
| import FoundationModels | ||
|
|
||
| @main | ||
| struct FMGenerate { | ||
| static func main() async { | ||
| var prompt = "" | ||
| let args = Array(CommandLine.arguments.dropFirst()) | ||
| var i = 0 | ||
| while i < args.count { | ||
| if args[i] == "--prompt", i + 1 < args.count { prompt = args[i + 1]; i += 2; continue } | ||
| i += 1 | ||
| } | ||
| if prompt.isEmpty { | ||
| FileHandle.standardError.write("usage: fm-generate --prompt <text>\n".data(using: .utf8)!) | ||
| exit(2) | ||
| } | ||
| let model = SystemLanguageModel.default | ||
| guard case .available = model.availability else { | ||
| FileHandle.standardError.write("FM_UNAVAILABLE: \(model.availability)\n".data(using: .utf8)!) | ||
| exit(3) | ||
| } | ||
| do { | ||
| let session = LanguageModelSession() | ||
| let reply = try await session.respond(to: prompt) | ||
| print(reply.content) | ||
| } catch { | ||
| FileHandle.standardError.write("FM_ERROR: \(error)\n".data(using: .utf8)!) | ||
| exit(1) | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a workflow prompt contains private text, passing it as
--promptexposes the full prompt in the helper process's command-line arguments for the duration of generation, where local process-listing tools can read it. This undermines the node's on-device/private use case; send the prompt over stdin or a private temp file instead.Useful? React with 👍 / 👎.