brew

Activity, over time

Daily observations from the public event archive.

Attention

Daily stars & forks

Loading attention chart

Star accumulation

Cumulative stars

Loading star accumulation chart

Pull requests

Daily opens & merges

Loading pull requests chart

Conversations

Daily issues & comments

Loading conversations chart

Community

Daily accounts & reviews

Loading community chart

Releases

Daily releases

Loading releases chart

Mentions

4posts · 30d
Nov 30 posts
  1. 開発toolのinstall手順、Brewfileで共有できていますか? Homebrewはsoftware package manager。`brew bundle`とBrewfileを使い、必要なformulaやcaskをまとめて記述し、環境構築の手順として共有できます。 Homebrew/brew

    25 views
  2. DEV · · @merbayerp

    I Have 188 Packages, Brew Refuses to Read Two of Them My intention was modest: free up some disk space. I typed brew list --formula | wc -l and got 188. Add five application bundles (casks) to that. Then I asked "how many of these did I actually ask for?" and the whole thing turned on me. My package manager answers that question with three different commands, and they give three different numbers — and in the gap between those answers sit two packages that appear in no list at all. The space-clearing job never finished. What came out instead was a measurement of how much a machine remembers about its own past. I'll walk through all of it, because every number here has a mechanism underneath it, and the mechanisms turned out to be more talkative than I expected. Three commands, three answers brew list --formula counts the 188 packages on disk. brew leaves says 51: "the ones that aren't a dependency of another installed formula or cask." `brew autoremove --dry-run` says nothing at all: the output is completely empty, there is nothing safe to remove. One question, three numbers: 188, 51, 0. They aren't actually asking the same question, and that's what I had failed to read properly. brew list enumerates directories in the Cellar: what is on disk. brew leaves collects the runtime-dependency names from the receipts of installed packages and prints the ones that don't appear in that set: the leaves of the graph. autoremove asks a third question: which package is both a leaf and carries the "I didn't ask for this" record. Putting the four numbers side by side, I finally saw what I had been asking: | Command | Answer | What it really asks | |---|---|---| | brew list --formula | 188 | How many directories are in the Cellar? | | installed_on_request: true in receipts | 64 | How many installs did I request? | | brew leaves | 51 | How many are leaves in the graph of loadable formulae? | | brew autoremove --dry-run | 0 | How many are considered safe to delete? | The Cellar totals 8.76 GiB. The 64 packages I asked for hold 5.77 GiB of that; the 124 that came along hold 2.99 GiB. I assumed the single largest item on my disk was mine: llvm really is (1,797 MiB), but right behind it comes llvm@21 (1,561 MiB), which I never asked for. zig and lld@21 pulled it in. The receipts say the same thing: llvm's record carries the request flag, llvm@21's does not. One of the two largest items on my disk is a decision of mine; the other is a side effect. "Why are you here?" has exactly one bit of answer Every package carries an INSTALL_RECEIPT.json in its own directory. All 188 receipts have the installed_on_request key: 64 say true, 124 say false. So my machine's memory of "you asked for this" is a single boolean per package. There used to be a second field: installed_as_dependency. It is missing from 85 of my receipts, true in 74, and false in 29. I found the reason in Homebrew's own code; the comment in tab.rb refers to the field as "long-removed" and keeps backward compatibility so old receipts still load. So those 85 receipts were written by a newer brew that no longer records it. One bit is what's left. Up to here it's an engineering decision, and a reasonable one. What stopped me was looking at my own records. My shell history sits at its ceiling: the file holds 1,225 lines, but 225 of them end in a backslash continuation, so the record count is exactly 1,000 — my SAVEHIST limit. The only brew line in there is a brew upgrade; there is no brew install line at all. [Every new command of mine deletes an old one](mustafaerbay.com.tr), and somewhere among those old ones every single install line went away. The only record of my intent is that bit, kept by the package manager itself. My first count was wrong, by the way: grep -c brew ~/.zsh_history returned nothing and I wrote down "zero". The file isn't plain text — file calls it extended ASCII, so grep treats it as binary and goes quiet. Asked with grep -a, one line showed up. The same thing happens to me a couple more times in the rest of this piece. !Diagram The thirteen packages between 51 and 64 Only 51 of the 64 packages I requested are leaves. The first 11 of the remaining 13 are explainable: even though I asked for them, they are now something else's dependency. 37 packages lean on openssl@3, 20 on libtiff, 12 on glib. If I tried to delete those, half of them would come straight back down. The last two taught me a lesson about measurement. The first time I compared the lists, I found a package that was "in leaves but not in my requested set": powershell/tap/powershell. No cause for panic, because the cause wasn't real — brew leaves prints packages by their full name (tap prefix included), while the Cellar directory is just powershell. The difference between the sets was in my text formatting, not in the world. In this kind of measurement, the first finding being an artifact of your own script is a common event; once I normalised the names, the gap closed. But two names refused to close: sshpass and supabase. Both are packages I requested, both say installed_on_request: true in their receipts, and neither is a dependency of anything, which by definition makes them leaves. `brew leaves` doesn't count them. It doesn't error. It doesn't warn. They're just absent. Two packages on disk that the manager won't read One command made the reason obvious: $ brew info sshpass Error: Refusing to load formula hudochenkov/sshpass/sshpass from untrusted tap hudochenkov/sshpass. Run `brew trust --formula hudochenkov/sshpass/sshpass` or `brew trust hudochenkov/sshpass` to trust it. Homebrew now refuses to load formulae from third-party taps unless you have explicitly trusted that tap. The code has a crisp two-line definition: for a tap to count as implicitly trusted it must be official and come from its canonical remote (official? && canonical_remote?). Every other tap needs approval, and approvals live in ~/.homebrew/trust.json. My file is 63 bytes and holds a single-entry list: powershell/tap/powershell. The file's modification time is 15 October, 09:05; the pwsh symlink is dated 09:06. So I granted an approval in the same minute I installed PowerShell. How that approval is given matters, because there is no spontaneous "do you trust this?" prompt anywhere in the code: a trust record only comes from a hand-typed brew trust command or from brew bundle's explicit flag, and every other path stops at a single error. The full name in the file (powershell/tap/powershell) says the same thing: I had the tap's identity in front of me as I typed that line. The gate asked me for the right thing; I'm the one who reduced it to a formality. A one-second decision, made so as not to interrupt an install, turned out months later to be the only thing keeping two packages inside the inventory. That enforcement entered Homebrew with the Add tap trust enforcement commit dated 30 May 2026, but the commit message itself says it was opt-in there: the rule only applied when HOMEBREW_REQUIRE_TAP_TRUST was set. It became the default through another commit on 8 June, which shipped first in version 6.0.0 (11 June 2026). So the gate has been shut on this machine for four and a half months; I'm on 7.0.10 today. I installed sshpass on 24 February 2026 and supabase two days later: both were inside before the gate existed, the gate was fitted afterwards, and both ended up on the outside of it. Now the concrete consequence: brew list shows these two packages, because that command only looks at Cellar directories. brew leaves doesn't, because it cannot load the formula. brew outdated reports 120 outdated formulae and neither of them is on that list. Asked by name, it throws the error above. So the scope of my update scan is not 188 packages, it's 186. These two aren't dormant files either. The /opt/homebrew/bin/sshpass and /opt/homebrew/bin/supabase links are in place, both are in my PATH, and both run. sshpass -V prints version 1.06 and a 2011 copyright line. supabase is 2.75.0; that release went out on 2 February 2026, and the newest one upstream was published this morning. Eight months sit in between. The instructive part is this: my package manager didn't tell me, the package itself did. Run supabase --version and two lines appear under the version number saying a newer release exists and 2.75.0 is what's installed. The update information is right there on the machine; the tool whose job is to collect it and show it to me in one list has dropped that package from its list. brew leaves being silent is, to me, the actual defect. When it can't load a formula it drops the package from the list and doesn't tell me; the command's error stream comes back completely empty. A one-line "I couldn't read these two packages" would have replaced my hand-rolled set comparison. What the code does is technically consistent — an unloadable formula has no place in the graph — but an inventory command should at least confess that its count is incomplete. If it were mine, I'd print the refusal as a warning rather than an error: the behaviour stays the same, and the user's blind spot closes. I have no objection to the approval mechanism itself, quite the opposite. A formula is Ruby code that runs during installation; the build steps, the download addresses, the post-install scripts all come from that file. An unaudited repository quietly updating that code amounts to permission to execute code on my computer. I'd put the gate in the same place. My objection isn't to the gate but to what happens behind it going unrecorded. Refusing to load a formula I haven't trusted is the right call; dropping it from the inventory as well erases the difference between "I won't load this" and "this isn't here". And these two packages don't leave the disk or the PATH, and they keep running: they just stop being counted. The security side turns into a pleasant irony here. The gate's purpose is right: don't blindly run code from a tap I haven't audited. But once the gate closes, whoever is left inside also falls outside of management. NIST's guide to enterprise patch management planning puts inventory second among its planning recommendations (§3.2, Inventory Your Software and Assets) and says inventories must be constantly updated or they go stale quickly; the same section notes that a software bill of materials (SBOM) from a vendor can augment that inventory. Mine is two lines short, and noticing it required comparing sets by hand. Why autoremove said zero I had a bit of paranoia here: I didn't request 124 packages, so some of them must be orphans. That turned out to be wrong. The graph, assembled from the runtime-dependency lists in the receipts, shows that every one of the 124 unrequested packages has at least one requester; zero orphans. That's the real reason autoremove came back empty. But reading the code showed me there are three separate ways for it to come back empty. autoremove only deletes packages that pass all three filters: nothing in the graph depends on them, their receipt says installed_on_request is explicitly false, and they were poured from a bottle (pre-built). Packages built from source never even enter the list; three of them are like that on this machine (powershell, sshpass, supabase). Cask dependencies are separately protected as well. On this machine the first of those three filters was enough on its own: with no orphaned package, the list is empty before the others matter. The other two are waiting for the day the graph changes: the three source-built packages on this machine will never enter autoremove's list even if their requesters disappear. For someone who actually wants to free space, the useful information is elsewhere: which of my requests is single-handedly holding how many packages? My poppler install holds 18 packages on its own, ffmpeg and ghostscript six each. Requesting composer brings 56 packages into the closure, php 55, poppler 51. A single brew uninstall decision frees 18 packages by that count — and none at all by autoremove's answer today. There are leftovers, too: brew cleanup --dry-run says roughly 41.9 MB. Four packages have more than one version sitting in the Cellar; ca-certificates has three at once (versions dated 19 March, 13 August and 25 September 2026). Against 8.76 GiB, 41.9 MB is a comic number for my disk-space goal, but at least nobody is hiding that number from me. After the measurement where I deleted apps and [left 661 MiB of data behind](mustafaerbay.com.tr), I pay more attention to where the residue hides than to how big it is. How far I can trust the receipt With the measurement done, I was going to look at the dates in the receipts and draw an "age distribution of my packages" chart. Luckily I tripped over an odd line first: git-filter-repo appears to have been installed on 20 October 2026, yet the same receipt's homebrew_version field says 4.6.9-6-g24e7d8f. That tag is dated 3 September 2025, thirteen months before the install. Of the 70 packages installed after 24 June 2026, 24 carry a receipt naming a brew version older than 6.0. The answer is in the source again. When installing from a bottle, brew doesn't write the receipt from scratch; it loads the one that came inside the bottle and then refreshes only specific fields — install time, installed_on_request, architecture, source path and so on. homebrew_version is not among the refreshed fields. So the brew version in a receipt is the version of the brew that built that bottle, not the one that installed it. The chart I was planning to draw would have shown the history of Homebrew's build servers, not of my machine. The install time (time) field isn't an intent date either: since every upgrade rewrites the receipt, that field holds "the moment this version was installed". My oldest receipt is 21 January 2025 (git), the newest 20 October 2026. Between those two dates, "when did I first ask for this package?" is written down nowhere. The intent bit surviving an upgrade is not an accident but deliberate: the code reads the value from the existing receipt and carries it into the new one. The only thing carried over is that bit — not the date. Running the same measurement on your own machine None of the numbers above needs a special tool; they all come from reading the receipts in the Cellar. This single line gives you the request/dependency split: for r in /opt/homebrew/Cellar/*/*/INSTALL_RECEIPT.json; do /usr/bin/python3 -c 'import json,sys; j=json.load(open(sys.argv[1])); \ print(j.get("installed_on_request"))' "$r" done | sort | uniq -c On my machine this prints 128 False and 65 True; that totals 193, not 188. The difference comes from four packages that keep more than one version in the Cellar: the command counts every version directory, while my table takes the newest receipt per package (64/124). If you're going to compare your number with somebody else's, make sure both are counting the same thing. To find the invisible ones, comparing two lists is enough, but do not forget to strip the tap prefixes, or you'll see the same ghost difference I saw on my first pass: comm -23 \ <(ls /opt/homebrew/Cellar | sort) \ <(brew leaves | sed 's|.*/||' | sort) For me this command dumps 137 names (188 minus 51), and every one of them means "on disk, not in the leaf list". Most are legitimate, in that they're somebody's dependency; brew info <name> tells you in one line which ones can't be read. The paths differ by architecture: /opt/homebrew on Apple Silicon, /usr/local on Intel Macs. There is, however, a far shorter way to do this whole comparison, and I took the long road until I found it. First, a single flag: brew list --formula --full-name | grep / That prints every package coming from a third-party tap by its full name — for me, exactly three lines: the list of candidates for becoming invisible. Second, open the gate for the length of one command and measure the difference: HOMEBREW_NO_REQUIRE_TAP_TRUST=1 brew leaves | wc -l # 53 for me, normally 51 HOMEBREW_NO_REQUIRE_TAP_TRUST=1 brew outdated --formula # 121 for me, normally 120 The difference is exactly the two packages I expected, and the only one added to the update list is supabase: so of the two that fall out of scope, one really is behind, and the other (sshpass 1.06) is the newest version in its own tap. Homebrew marks this variable as one that will be removed in a later release, so it isn't a permanent fix; but for a once-a-year inventory audit it's exactly what I was looking for. Your numbers will obviously come out different; the value of the measurement isn't in the number, it's in how far the four answers drift apart. What I'll do tonight At the end of the measurement my to-do list got shorter but clearer: - Settle the two invisible packages. Do I need sshpass? The cost of keeping a binary with a 2011 copyright in my PATH is higher than either making it visible with brew trust or removing it. Before deciding, I'll scan the scripts I use. - Move supabase to where the manager can see it. There's no defensible reason to keep a CLI that's eight months behind outside the patch scan. - Keep my intent somewhere other than brew. A 64-line Brewfile is a better memory than one bit per package; my shell history clearly doesn't remember. - Read the tap inventory once a year. I have four taps, three of them third-party. Adding a line to the trust file took me a second; reading it takes a second too. There's no new tool on that list, because the missing piece was never a tool. My package manager answers three questions from three different places, and I had been treating all three as the same question. The number that stays with me isn't 188 or 0; it's 2. When a security gate goes up, we usually think "that thing can't get in anymore". But the gate can also make invisible what is already inside: the same mechanism both protects and drops things out of the catalogue. The inventory our machines report to us is not an inventory of what they measure, but of what they can read. Official Sources - Homebrew — the tap trust enforcement commit - Homebrew — Library/Homebrew/trust.rb - Homebrew — the brew leaves implementation - Homebrew — the autoremove filters - Homebrew — the install receipt (tab.rb) - Homebrew — tap definition and implicitly_trusted? (tap.rb) - NIST SP 800-40r4 — Guide to Enterprise Patch Management Planning - CISA — Software Bill of Materials

    2 reactions
  3. Qiita · · @suzuyui

    Mac mini (M6 / 32GB) でローカル LLM を試す はじめに ローカル LLM は、2023年の llama.cpp や Ollama の登場によって、個人の PC でも動かしやすくなった 2024年11月の M4 Mac mini 発売以降は、Apple Silicon のユニファイドメモリや電力効率を活かしたローカル LLM 環境にも関心が集まっている。2025年には DeepSeek-R1 や gpt-oss などのモデルも登場し、個人で試せる選択肢が広がった やや流行りに乗り遅れたが、今回は2026年9月発売の Mac mini M6 を購入し、ローカル LLM の検証環境を自宅ラボに構築した。本記事では、環境構築から初期動作確認までの手順を、実際の出力結果とともにまとめる 使用するのは Mac mini(M6 / 32GB)で、Ollama + gpt-oss 20B と Splash + Qwen3.8-27B の2構成を試す。購入直後のため、Homebrew 未導入の状態から準備を進めた ■ 概要図 !macmini-llm-overview.png 同一 Mac 内の試験では 127.0.0.1、LAN 試験では今回の Mac mini に割り当てた IP アドレス 192.168.129.6 を待ち受け先に使う。LAN IP アドレスは利用環境に合わせて読み替える。2つのモデルは同時にロードせず、順番に検証する ■ Splash の WebUI !macmini-splash-webchat.png 本記事で実施する内容は下記のとおり。 - 環境準備 - Homebrew のインストールと PATH 設定 - macOS ファイアウォールと API 接続元の制限 - インストール - Ollama + gpt-oss 20B のインストールと起動 - Splash + Qwen3.8-27B のインストールと起動 - 動作確認 - 日本語での質問と回答内容の確認 - GPU の利用状況とメモリ使用量の確認 - 同じ Mac と LAN 内の別PC・サーバからの API 呼び出し - API の応答時間と導入前後のディスク容量の確認 両構成で日本語の回答と API 経由の応答を確認できたが、技術的な説明には一部誤りがあった。設計や設定に使う内容は、公式資料との照合が必要となる 今回の結果は少数の質問による初期確認として掲載する。モデル・量子化・思考設定が異なる構成のため、モデルや実行ツール単体の性能・優劣を判断するものではない 実施環境 使用する環境は下記のとおり (2026.10.18 実施時点) | 項目 | 内容 | | :--- | :--- | | 本体 | Mac mini (Mac18,5) | | チップ | Apple M6 | | CPU | 12コア(スーパーコア × 2、高性能コア × 4、高効率コア × 6) | | GPU | 12コア | | ユニファイドメモリ | 32GB(CPU・GPU で共有) | | メモリ帯域幅 | 170GB/s(32GB構成の公称値) | | 内蔵 SSD | 512GB | | OS | macOS 27 Golden Gate 27.0.1 | | Ollama | 0.40.6 | | Splash | 1.3.2 | | Homebrew | 7.0.9 | | Command Line Tools for Xcode | 27.0(Homebrew インストーラで導入) | | Ollama のモデル | gpt-oss:20b / MXFP4 | | Splash のモデル | Qwen3.8-27B / Unsloth UD-IQ4_XS | | Mac mini の LAN IP | 192.168.129.6 | | API 接続を許可するネットワーク | 192.168.129.0/24 | 使用する実行ツール 今回は Ollama と Splash を使う。どちらもローカルで LLM を動かし、HTTP API で呼び出せる。まず各ツールの役割と選定理由を整理する 普及状況の参考として GitHub のスター数や公式組織のフォロワー数も記載する。いずれも関心の目安で、実際の利用者数や利用率を示すものではなく、異なる指標同士の単純比較はできない Ollama Ollama は、LLM のダウンロードや実行、API 経由での利用ができるツールとなる Ollama チームが開発・公開するオープンソースのツールで、macOS・Windows・Linux に対応している。2026年10月18日の確認時点で GitHub のスターは約18.4万あり、公式 README には多数の連携ツールも掲載されている。公式リポジトリ 今回は、モデルの取得から API サーバの操作までコマンドで手順を残せる Homebrew の CLI 版を選んだ。Splash と導入・更新方法を揃え、最初のチャット・API 試験の比較基準にする。Homebrew の Ollama Splash Splash は、Apple Silicon 向けの推論エンジン兼 API サーバとなる Inco AI が開発・公開するオープンソースのソフトウェアとなる。2026年10月18日の確認時点で GitHub のスターは約1,500となる。公式リポジトリ 対応モデル向けの Metal カーネルや、ドラフトモデルの先読み候補を対象モデルで検証する投機的デコードなどで高速化する。この最適化を使った構成の使用感を確認するために選んだ。OpenAI 互換 API にも対応し、Ollama と同じ形式で質問を送れる LM Studio(今回は未実施) LM Studio は、GUI でモデルを検索し、設定や回答を確認しながら試せる。API 提供にも対応しており、llmster を使えば GUI なしでの運用もできる。ヘッドレス運用 Element Labs, Inc. が開発するアプリケーションで、公式 GitHub では CLI や SDK なども公開している。2026年10月18日の確認時点で、GitHub の公式組織のフォロワーは約1.23万となる。 今回は CLI・API 中心で導入する Ollama と、Apple Silicon 向けの高速化を試す Splash に絞るため、試してはいない API の利用と認証 API では、モデル名と質問を JSON 形式で HTTP サーバに送り、生成された回答を受け取る。今回は両方が対応する POST /v1/chat/completions に curl からリクエストを送る。OpenAI 互換の形式を使うが、処理は本機にダウンロードしたモデルで行う | 項目 | Ollama | Splash | | :--- | :--- | :--- | | 既定の待ち受け先 | 127.0.0.1:11434 | 127.0.0.1:8000 | | 今回使う API | http://127.0.0.1:11434/v1/chat/completions | http://127.0.0.1:8000/v1/chat/completions | | 既定の認証 | ローカル API は認証不要 | API キー未設定では認証なし | | 認証を付ける場合 | 認証付きリバースプロキシなどを別途設ける | --api-key または SPLASH_API_KEY で設定できる | Ollama のローカル API は API キーを必要としない。Ollama のクラウド API 用キーは別の仕組みで、発行しても本機のローカル API に認証が追加されるわけではない。Ollama の認証仕様 Splash はキーを設定すると、API リクエストに Authorization: Bearer <キー> または x-api-key: <キー> が必要になる。ただしチャット画面・ヘルスチェックなど、一部の経路は認証の対象外となる。Splash の認証仕様 最初の同一 Mac 内での試験は既定のローカル待ち受けを使い、curl に認証ヘッダーを付けない。127.0.0.1 は同じ Mac から接続するアドレスで、既定では別端末から直接接続できない。ただし同じ Mac 内のプログラムからは呼び出せるため、ローカル待ち受け自体がユーザー認証になるわけではない。Ollama の待ち受け仕様 後半では待ち受けを Mac Mini の NIC IP アドレスの 192.168.129.6 に変更し、LAN 内からも応答確認する。Splash は API キー認証を有効にする。Ollama の直接接続は認証なしのため、PF で 192.168.129.0/24 に接続元を制限した検証用 LAN で短時間の疎通確認に使う。継続運用やインターネット経由の利用では、認証・接続制限・TLS または VPN による通信保護の別途設計が必要(今回は対象外) 試験モデル 初回は「API 提供の基準を作る構成」と「Apple Silicon 向けの高速化を試す構成」の2つに絞っている | 構成 | 選定理由・補足 | 配布サイズの目安 | | :--- | :--- | :--- | | Ollama + gpt-oss 20B / MXFP4 | 公式導入ガイドで16GB以上の VRAM またはユニファイドメモリでの利用が案内されており、32GB環境の初期候補とした。テキスト API の比較基準にし、今後は関数呼び出しや構造化出力も評価したい | 約14GB | | Splash + Qwen3.8-27B / Unsloth UD-IQ4_XS | Splash 1.3.2 の対応モデルで、技術的な質問への回答と投機的デコードを使った構成の使用感を確認するために選定。32GB環境でのメモリの余裕を重視し、UD-Q4_K_M(約16.5GB)より約2.2GB小さい UD-IQ4_XS を採用した | 対象 GGUF 約14.3GB。ドラフトモデル(先読み用)約3.85GBは別途必要で、合計約18.1GB | 配布サイズは実行時メモリとは異なり、小さい方が必ず高速とは限らない。 モデル選定は詳しくないので別途試験が必要で今回は試すこと優先で選んだだけとなっている 事前準備 購入直後の環境を整えるため、Ollama と Splash の導入に使う Homebrew を先に準備する 利用バージョンの方針 検証には実施環境の表に記載した安定版を使用した。Homebrew の配布版は更新されるため、後日同じコマンドを実行しても同じ版が入るとは限らない。導入時にバージョンを確認し、比較期間中は brew pin で更新を止める。具体的な確認・設定は各ツールのインストール手順に記載する モデルもタグの内容が更新される場合があるため、Ollama のモデル ID と Splash の対象・ドラフトのリビジョンを記録する。Splash の SSD キャッシュはディスク容量の見積もりに含める ディスク容量の見積もり 導入時の容量を下表のとおり見積もり、更新や一時ファイルの余裕を含めて100GB程度の空きを確保する | 用途 | ディスク容量の目安 | 補足 | | :--- | ---: | :--- | | Ollama の gpt-oss 20B | 約14GB | モデルの配布サイズ | | Splash の Qwen3.8 とドラフトなど | 約17〜22GB | 対象 GGUF 約14.3GBとドラフトなどの概算。保存済みの重みを再利用するため、モデルとダウンロードキャッシュを二重計上しない | | アプリ・Homebrew・依存関係など | 約5〜10GB | Command Line Tools などを含む概算 | | Splash の SSD キャッシュ | 上限32GiB(約34GB) | 今回の既定設定。空き容量に応じて自動調整され、常に全量を使うわけではない。今回の起動方法では終了時に解放される | | 合計 | 初期導入:約36〜46GB/キャッシュ込み:約70〜80GB | キャッシュ込みは上限の約34GBを加えた場合 | 上記は概算で、実測値や最低動作要件ではない。macOS 本体・他のアプリ・スワップ・追加モデルは含めず、実際の空き容量の変化は導入前後に測定する 初期起動からセットアップ 購入直後の Mac は、下記の設定を済ませておく | 設定項目 | 操作 | 補足 | | :--- | :--- | :--- | | 初回起動の設定 | 言語設定(日本語)、アカウント作成、Apple Account 認証などを進める | 画面の案内に従う | | OS アップデート | 「システム設定 → 一般 → ソフトウェアアップデート」で最新版に更新する | 更新後の OS バージョンを記録する | | 自動スリープの解除 | 「システム設定 → エネルギー」で「ディスプレイがオフの時に自動でスリープさせない」をオンにする | API サーバの利用中にスリープしないようにする | | ネットワーク設定 | 環境に合わせて固定 IP などを設定し、Wi-Fi が不要ならオフにする | 必要に応じて設定する | | 画面共有 | 「システム設定 → 一般 → 共有 → 画面共有」をオンにする | リモートで画面操作する場合に設定する | | SSH 接続 | 「システム設定 → 一般 → 共有 → リモートログイン」をオンにする | リモートでターミナル操作する場合に設定する | 導入前のディスク容量を記録する Homebrew やモデルのインストールを始める前に、Mac mini 側でデータボリュームの空き容量を保存する。最後に同じボリュームを測定し、空き容量の減少分を表示する df -h /System/Volumes/Data mkdir -p "$HOME/local-llm-measurements" LC_ALL=C df -kP /System/Volumes/Data > "$HOME/local-llm-measurements/disk-before.txt" cat "$HOME/local-llm-measurements/disk-before.txt" df -h は目視確認用、保存する df -kP は差分計算用となる。記録はファイルに残すため、途中でターミナルを閉じても比較できる。導入後にこの手順を再実行して、導入前の記録を上書きしないようにする <details><summary>導入前のディスク容量の測定ログ</summary><div> suzuyu@macmini-m6 ~ % df -h /System/Volumes/Data mkdir -p "$HOME/local-llm-measurements" LC_ALL=C df -kP /System/Volumes/Data > "$HOME/local-llm-measurements/disk-before.txt" cat "$HOME/local-llm-measurements/disk-before.txt" Filesystem Size Used Avail Capacity iused ifree %iused Mounted on /dev/disk3s5 460Gi 35Gi 400Gi 8% 343k 4.2G 0% /System/Volumes/Data Filesystem 1024-blocks Used Available Capacity Mounted on /dev/disk3s5 482746452 36382052 419935276 8% /System/Volumes/Data suzuyu@macmini-m6 ~ % </div></details> Homebrew のインストール Splash のインストールに使う Homebrew を導入する。macOS の標準ターミナル(zsh)で、公式サイト に掲載されているコマンドを実行する /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" <details><summary>Homebrew のインストールログ</summary><div> suzuyu@macmini-m6 ~ % /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" ==> Checking for `sudo` access (which may request your password)... Password: ==> This script will install: /opt/homebrew/bin/brew /opt/homebrew/share/doc/homebrew /opt/homebrew/share/man/man1/brew.1 /opt/homebrew/share/zsh/site-functions/_brew /opt/homebrew/etc/bash_completion.d/brew /opt/homebrew /etc/paths.d/homebrew ==> The following new directories will be created: /opt/homebrew/bin /opt/homebrew/etc /opt/homebrew/include /opt/homebrew/lib /opt/homebrew/sbin /opt/homebrew/share /opt/homebrew/var /opt/homebrew/opt /opt/homebrew/share/zsh /opt/homebrew/share/zsh/site-functions /opt/homebrew/var/homebrew /opt/homebrew/var/homebrew/linked /opt/homebrew/Cellar /opt/homebrew/Caskroom /opt/homebrew/Frameworks ==> The Xcode Command Line Tools will be installed. Press RETURN/ENTER to continue or any other key to abort: ==> /usr/bin/sudo /usr/bin/install -d -o suzuyu -g admin -m 0755 /opt/homebrew ==> Searching online for the Command Line Tools ==> Installing Command Line Tools for Xcode 27.0-27.0 Software Update Tool Finding available software Downloading Command Line Tools for Xcode 27.0 Downloaded Command Line Tools for Xcode 27.0 Installing Command Line Tools for Xcode 27.0 Done with Command Line Tools for Xcode 27.0 Done. ==> /usr/bin/sudo /usr/bin/xcode-select --switch /Library/Developer/CommandLineTools ==> Found Git: /Library/Developer/CommandLineTools/usr/bin/git ==> Downloading and installing Homebrew... remote: Enumerating objects: 367837, done. remote: Counting objects: 100% (997/997), done. remote: Compressing objects: 100% (219/219), done. remote: Total 367837 (delta 870), reused 823 (delta 778), pack-reused 366840 (from 2) remote: Enumerating objects: 55, done. remote: Counting objects: 100% (33/33), done. remote: Total 55 (delta 33), reused 33 (delta 33), pack-reused 22 (from 1) ==> /usr/bin/sudo /usr/bin/install -o root -g wheel -m 0644 /var/folders/vm/x0l5wqcx7njgdqpfvbpp20n00000gn/T/tmp.vXAXi5cpXX /etc/paths.d/homebrew ==> Updating Homebrew... ==> Downloading https://ghcr.io/v2/homebrew/core/portable-ruby/blobs/sha256:e0088dff5614b39387300136ec7a5f95bf1e07589547245c919524fc9e8b4197 ######################################################################################################################################################################################################################################################### 100.0% ==> Pouring portable-ruby-4.0.7.arm64_big_sur.bottle.tar.gz ==> Installation successful! ==> Homebrew has enabled anonymous aggregate formulae and cask analytics. Read the analytics documentation (and how to opt-out) here: https://docs.brew.sh/Analytics No analytics data has been sent yet (nor will any be during this install run). ==> Homebrew is run entirely by unpaid volunteers. Please consider donating: https://github.com/Homebrew/brew#donations ==> Next steps: - Run these commands in your terminal to add Homebrew to your PATH: echo >> /Users/suzuyu/.zprofile echo 'eval "$(/opt/homebrew/bin/brew shellenv zsh)"' >> /Users/suzuyu/.zprofile eval "$(/opt/homebrew/bin/brew shellenv zsh)" - Run brew help to get started - Further documentation: https://docs.brew.sh suzuyu@macmini-m6 ~ % </div></details> 表示されるインストール内容を確認し、案内に従って進める。パスワードを求められた場合は Mac のログインパスワードを入力する。入力中は文字が表示されない 今回の環境では、Homebrew インストーラが Command Line Tools for Xcode 27.0 を自動でインストールした。別途 xcode-select --install を実行する必要はなかった Homebrew の PATH 設定と確認 インストール完了時の Next steps に従って設定する。Apple Silicon の標準インストール先は /opt/homebrew となる。zsh では下記を一度実行し、今後開くターミナルと現在のターミナルの両方で brew を使えるようにする echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile eval "$(/opt/homebrew/bin/brew shellenv)" command -v brew brew --version brew --prefix <details><summary>Homebrew の PATH・バージョン確認ログ</summary><div> suzuyu@macmini-m6 ~ % echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile suzuyu@macmini-m6 ~ % eval "$(/opt/homebrew/bin/brew shellenv)" suzuyu@macmini-m6 ~ % command -v brew /opt/homebrew/bin/brew suzuyu@macmini-m6 ~ % brew --version Homebrew 7.0.9 suzuyu@macmini-m6 ~ % brew --prefix /opt/homebrew suzuyu@macmini-m6 ~ % </div></details> Homebrew インストール後の出力で出るものと同じのため、表示される Next steps の操作ですでに .zprofile へ同じ設定を追加した場合、上記の echo は省略する。 command -v brew が /opt/homebrew/bin/brew、brew --prefix が /opt/homebrew となり、バージョンが表示されることを確認する 今回の環境では、Homebrew 7.0.9、インストール先 /opt/homebrew を確認した macOS ファイアウォールと接続元の制限 LAN 向けにサーバを起動する前に、今回の API の2ポートを下記の範囲に制限する。アプリ単位の許可には macOS ファイアウォール、接続元の制限には PF を使う | 送信元 | 宛先 | プロトコル・ポート | 扱い | | :--- | :--- | :--- | :--- | | 192.168.129.0/24 | 192.168.129.6 | TCP 11434 / 8000 | 許可 | | 上記以外の IPv4 アドレス | 192.168.129.6 | TCP 11434 / 8000 | 拒否 | LAN 試験時は、サーバを 192.168.129.6 にバインドする。Ollama は許可範囲内で認証なしに利用できるため、信頼できる検証用 LAN を前提とする アプリケーションファイアウォール 「システム設定 → ネットワーク → ファイアウォール」をオンにし、「オプション」の「すべての受信接続をブロック」はオフにする。Ollama / Splash の導入・起動後、実際に待ち受けるサーバの実行ファイルを確認し、受信接続を許可する。Apple の設定手順 PF のルールを作成する 購入直後で、VPN などが独自の PF 設定を管理していない環境向けの例。メイン設定の再読込は既存ルールに影響する場合があるため、既存設定がある場合は先に確認する。操作は Mac 本体のターミナルで行う 設定をバックアップし、専用のルールファイルを作成する。cp -n は既存のバックアップを上書きしない sudo cp -p -n /etc/pf.conf /etc/pf.conf.before-local-llm sudo tee /etc/pf.anchors/local.llm >/dev/null <<'EOF' llm_host = "192.168.129.6" llm_net = "192.168.129.0/24" llm_ports = "{ 11434, 8000 }" pass in inet proto tcp from $llm_net to $llm_host port $llm_ports flags S/SA keep state block return in quick inet proto tcp from ! $llm_net to $llm_host port $llm_ports EOF <details><summary>PF ルールの作成ログ</summary><div> suzuyu@macmini-m6 ~ % sudo cp -p -n /etc/pf.conf /etc/pf.conf.before-local-llm Password: suzuyu@macmini-m6 ~ % sudo tee /etc/pf.anchors/local.llm >/dev/null <<'EOF' llm_host = "192.168.129.6" llm_net = "192.168.129.0/24" llm_ports = "{ 11434, 8000 }" pass in inet proto tcp from $llm_net to $llm_host port $llm_ports flags S/SA keep state block return in quick inet proto tcp from ! $llm_net to $llm_host port $llm_ports EOF suzuyu@macmini-m6 ~ % sudo nano /etc/pf.conf suzuyu@macmini-m6 ~ % cat /etc/pf.conf # # Default PF configuration file. # # This file contains the main ruleset, which gets automatically loaded # at startup. PF will not be automatically enabled, however. Instead, # each component which utilizes PF is responsible for enabling and disabling # PF via -E and -X as documented in pfctl(8). That will ensure that PF # is disabled only when the last enable reference is released. # # Care must be taken to ensure that the main ruleset does not get flushed, # as the nested anchors rely on the anchor point defined here. In addition, # to the anchors loaded by this file, some system services would dynamically # insert anchors into the main ruleset. These anchors will be added only when # the system service is used and would removed on termination of the service. # # See pf.conf(5) for syntax. # # # com.apple anchor point # scrub-anchor "com.apple/*" nat-anchor "com.apple/*" rdr-anchor "com.apple/*" dummynet-anchor "com.apple/*" anchor "com.apple/*" load anchor "com.apple" from "/etc/pf.anchors/com.apple" suzuyu@macmini-m6 ~ % cat /etc/pf.anchors/local.llm llm_host = "192.168.129.6" llm_net = "192.168.129.0/24" llm_ports = "{ 11434, 8000 }" pass in inet proto tcp from $llm_net to $llm_host port $llm_ports flags S/SA keep state block return in quick inet proto tcp from ! $llm_net to $llm_host port $llm_ports suzuyu@macmini-m6 ~ % </div></details> メイン設定から読み込む sudo nano /etc/pf.conf 既存の Apple 用設定を残し、下記の local.llm を含む2行だけを追加する。anchor "local.llm" はフィルタ用の最初の anchor 行の直前(scrub-anchor・nat-anchor・rdr-anchor より後)、load anchor "local.llm" ... はファイル末尾に置く 下記は標準構成の該当部分の抜粋。ファイル全体を置き換えず、再実行時も同じ行を重複させない anchor "local.llm" anchor "com.apple/*" load anchor "com.apple" from "/etc/pf.anchors/com.apple" load anchor "local.llm" from "/etc/pf.anchors/local.llm" <details><summary>PF メイン設定の確認ログ</summary><div> suzuyu@macmini-m6 ~ % cat /etc/pf.conf # # Default PF configuration file. # # This file contains the main ruleset, which gets automatically loaded # at startup. PF will not be automatically enabled, however. Instead, # each component which utilizes PF is responsible for enabling and disabling # PF via -E and -X as documented in pfctl(8). That will ensure that PF # is disabled only when the last enable reference is released. # # Care must be taken to ensure that the main ruleset does not get flushed, # as the nested anchors rely on the anchor point defined here. In addition, # to the anchors loaded by this file, some system services would dynamically # insert anchors into the main ruleset. These anchors will be added only when # the system service is used and would removed on termination of the service. # # See pf.conf(5) for syntax. # # # com.apple anchor point # scrub-anchor "com.apple/*" nat-anchor "com.apple/*" rdr-anchor "com.apple/*" dummynet-anchor "com.apple/*" anchor "local.llm" anchor "com.apple/*" load anchor "com.apple" from "/etc/pf.anchors/com.apple" load anchor "local.llm" from "/etc/pf.anchors/local.llm" suzuyu@macmini-m6 ~ % </div></details> 構文確認と適用 構文を確認し、直後の終了コードが 0 なら読み込みへ進む。エラーがあれば修正し、再確認する sudo pfctl -nf /etc/pf.conf echo $? sudo pfctl -f /etc/pf.conf echo $? 読込の終了コードも 0 なら、PF を有効化して状態とルールを確認する。-E は1回だけ実行し、出力される Token は設定を戻す際に使うため控える sudo pfctl -E sudo pfctl -s info sudo pfctl -sr sudo pfctl -a local.llm -sr | 確認対象 | 成功時の表示 | | :--- | :--- | | PF の状態 | Status: Enabled | | メインルール | anchor "local.llm" | | 専用 anchor | TCP 11434 / 8000 の許可2件・拒否2件 | 今回の環境では上記を確認できた。実通信の確認は、後半の「別PC・サーバからの API 応答確認」で新しい接続を使って行う <details><summary>pfctl の注意書きと ALTQ の通知について</summary><div> pfctl: Use of -f option, could result in flushing of rules present in the main ruleset added by the system at startup. See /etc/pf.conf for further details. -f でメインルールを読み込む際の一般的な注意書きで、この表示自体は構文エラーではない。-nf は構文確認のみで、実際のルールは変更しない No ALTQ support in kernel ALTQ related functions disabled ALTQ による帯域制御・キュー制御がカーネルで利用できないという通知。今回の IP・ポートによる制限では ALTQ を使わないため、この表示自体は読込失敗を意味しない。成否は終了コードと読み込まれたルールで確認する </div></details> <details><summary>PF の適用・状態確認ログ</summary><div> suzuyu@macmini-m6 ~ % sudo pfctl -f /etc/pf.conf pfctl: Use of -f option, could result in flushing of rules present in the main ruleset added by the system at startup. See /etc/pf.conf for further details. No ALTQ support in kernel ALTQ related functions disabled suzuyu@macmini-m6 ~ % echo $? 0 suzuyu@macmini-m6 ~ % sudo pfctl -E No ALTQ support in kernel ALTQ related functions disabled pf enabled Token : 923634586225060667 suzuyu@macmini-m6 ~ % sudo pfctl -s info No ALTQ support in kernel ALTQ related functions disabled Status: Enabled for 0 days 00:00:05 Debug: Urgent State Table Total Rate current entries 0 searches 441 88.2/s inserts 0 0.0/s removals 0 0.0/s Counters match 379 75.8/s bad-offset 0 0.0/s fragment 0 0.0/s short 0 0.0/s normalize 0 0.0/s memory 0 0.0/s bad-timestamp 0 0.0/s congestion 0 0.0/s ip-option 0 0.0/s proto-cksum 0 0.0/s state-mismatch 0 0.0/s state-insert 0 0.0/s state-limit 0 0.0/s src-limit 0 0.0/s synproxy 0 0.0/s dummynet 0 0.0/s invalid-port 0 0.0/s suzuyu@macmini-m6 ~ % sudo pfctl -sr No ALTQ support in kernel ALTQ related functions disabled scrub-anchor "com.apple/*" all fragment reassemble anchor "local.llm" all anchor "com.apple/*" all suzuyu@macmini-m6 ~ % sudo pfctl -a local.llm -sr No ALTQ support in kernel ALTQ related functions disabled pass in inet proto tcp from 192.168.129.0/24 to 192.168.129.6 port = 11434 flags S/SA keep state pass in inet proto tcp from 192.168.129.0/24 to 192.168.129.6 port = 8000 flags S/SA keep state block return in quick inet proto tcp from ! 192.168.129.0/24 to 192.168.129.6 port = 11434 block return in quick inet proto tcp from ! 192.168.129.0/24 to 192.168.129.6 port = 8000 suzuyu@macmini-m6 ~ % </div></details> 今回は再起動時の自動適用を設定しない。Mac を再起動した場合は、LAN 向けサーバの起動前に構文確認・読込・有効化と状態確認をやり直す。再起動していなければ -E は重ねず、状態確認のみ行う 設定を解除する場合は、後半の「利用後の停止と設定の切り戻し」を参照する Ollama のインストール・起動・応答確認 公式ドキュメントでは macOS Sonoma 14 以降が必要で、Apple M シリーズでは CPU と GPU がサポートされている。macOS の要件 Homebrew でインストールする 事前準備で導入した Homebrew を使い、CLI 版の ollama をインストールする。2026-10-18 時点の配布版は0.40.6となる。Homebrew の配布情報、Ollama 0.40.6 まず配布バージョンを確認する brew update brew info ollama <details><summary>Ollama の配布情報の確認ログ</summary><div> suzuyu@macmini-m6 ~ % brew update brew info ollama ==> Updating Homebrew... ==> Homebrew collects anonymous analytics. Read the analytics documentation (and how to opt-out) here: https://docs.brew.sh/Analytics No analytics have been recorded yet (nor will be during this `brew` run). ==> Homebrew is run entirely by unpaid volunteers. Please consider donating: https://github.com/Homebrew/brew#-donations Updated 1 tap (homebrew/core). ==> New Formulae bqlog: Cross-language logging library ==> ollama: stable 0.40.6 (bottled), HEAD Create, run, and share large language models (LLMs) https://ollama.com/ Not installed From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/o/ollama.rb License: MIT ==> Dependencies Required (1): mlx-c ==> Options --HEAD Install HEAD version ==> Caveats To start ollama now and restart at login: brew services start ollama Or, if you don't want/need a background service you can just run: OLLAMA_FLASH_ATTENTION="1" OLLAMA_KV_CACHE_TYPE="q8_0" /opt/homebrew/opt/ollama/bin/ollama serve ==> Downloading https://formulae.brew.sh/api/formula/ollama.json ==> Analytics install: 54,285 (30 days), 143,032 (90 days), 639,496 (365 days) install-on-request: 54,186 (30 days), 142,813 (90 days), 639,009 (365 days) build-error: 160 (30 days) suzuyu@macmini-m6 ~ % </div></details> 配布版が0.40.6であることを確認してインストールする。将来の実行時に別の版になっている場合、このコマンドでは0.40.6を指定できないため、導入版と検証条件を見直して記録する brew install ollama brew list --versions ollama command -v ollama <details><summary>Ollama のインストールログ</summary><div> suzuyu@macmini-m6 ~ % brew install ollama ==> Downloading bottle manifests ✔︎ Bottle Manifest ollama (0.40.6) Downloaded 50.4KB/ 50.4KB ==> Would install 1 formula: ollama 0.40.6 ==> Would install 11 dependencies for ollama: ca-certificates mpdecimal openssl@4 readline sqlite xz lz4 zstd python@3.15 mlx mlx-c ==> Do you want to proceed with the installation? [y/n] ==> Fetching downloads for: ollama ✔︎ Bottle Manifest ca-certificates (2026-09-25) Downloaded 8.1KB/ 8.1KB ✔︎ Bottle Manifest openssl@4 (4.0.3) Downloaded 19.5KB/ 19.5KB ✔︎ Bottle Manifest sqlite (3.54.1) Downloaded 22.6KB/ 22.6KB ✔︎ Bottle Manifest xz (5.8.4) Downloaded 14.6KB/ 14.6KB ✔︎ Bottle Manifest zstd (1.5.7_1) Downloaded 17.8KB/ 17.8KB ✔︎ Bottle Manifest lz4 (1.10.0) Downloaded 13.1KB/ 13.1KB ✔︎ Bottle Manifest mlx (0.32.4) Downloaded 36.4KB/ 36.4KB ✔︎ Bottle Manifest mpdecimal (4.0.1) Downloaded 16.6KB/ 16.6KB ✔︎ Bottle Manifest mlx-c (0.7.0_1) Downloaded 38.9KB/ 38.9KB ✔︎ Bottle Manifest readline (8.3.6) Downloaded 15.3KB/ 15.3KB ✔︎ Bottle Manifest python@3.15 (3.15.0) Downloaded 108.8KB/108.8KB ✔︎ Bottle ca-certificates (2026-09-25) Downloaded 112.2KB/112.2KB ✔︎ Bottle mpdecimal (4.0.1) Downloaded 185.8KB/185.8KB ✔︎ Bottle lz4 (1.10.0) Downloaded 286.9KB/286.9KB ✔︎ Bottle xz (5.8.4) Downloaded 788.8KB/788.8KB ✔︎ Bottle readline (8.3.6) Downloaded 757.0KB/757.0KB ✔︎ Bottle mlx-c (0.7.0_1) Downloaded 225.7KB/225.7KB ✔︎ Bottle zstd (1.5.7_1) Downloaded 795.2KB/795.2KB ✔︎ Bottle sqlite (3.54.1) Downloaded 2.5MB/ 2.5MB ✔︎ Bottle openssl@4 (4.0.3) Downloaded 11.2MB/ 11.2MB ✔︎ Bottle ollama (0.40.6) Downloaded 17.6MB/ 17.6MB ✔︎ Bottle python@3.15 (3.15.0) Downloaded 20.2MB/ 20.2MB ✔︎ Bottle mlx (0.32.4) Downloaded 76.9MB/ 76.9MB ==> Installing dependencies for ollama: ca-certificates, mpdecimal, openssl@4, readline, sqlite, xz, lz4, zstd, python@3.15, mlx and mlx-c ==> Installing ollama dependency: ca-certificates (2026-09-25) ==> Pouring ca-certificates--2026-09-25.all.bottle.tar.gz 🍺 /opt/homebrew/Cellar/ca-certificates/2026-09-25: 5 files, 204.4KB ==> Installing ollama dependency: mpdecimal (4.0.1) ==> Pouring mpdecimal--4.0.1.arm64_golden_gate.bottle.1.tar.gz 🍺 /opt/homebrew/Cellar/mpdecimal/4.0.1: 22 files, 627.4KB ==> Installing ollama dependency: openssl@4 (4.0.3) ==> Pouring openssl@4--4.0.3.arm64_golden_gate.bottle.tar.gz 🍺 /opt/homebrew/Cellar/openssl@4/4.0.3: 6,820 files, 23.8MB ==> Installing ollama dependency: readline (8.3.6) ==> Pouring readline--8.3.6.arm64_golden_gate.bottle.tar.gz 🍺 /opt/homebrew/Cellar/readline/8.3.6: 56 files, 2.6MB ==> Installing ollama dependency: sqlite (3.54.1) ==> Pouring sqlite--3.54.1.arm64_golden_gate.bottle.tar.gz 🍺 /opt/homebrew/Cellar/sqlite/3.54.1: 13 files, 5.4MB ==> Installing ollama dependency: xz (5.8.4) ==> Pouring xz--5.8.4.arm64_golden_gate.bottle.tar.gz 🍺 /opt/homebrew/Cellar/xz/5.8.4: 96 files, 2.7MB ==> Installing ollama dependency: lz4 (1.10.0) ==> Pouring lz4--1.10.0.arm64_golden_gate.bottle.3.tar.gz 🍺 /opt/homebrew/Cellar/lz4/1.10.0: 28 files, 774.1KB ==> Installing ollama dependency: zstd (1.5.7_1) ==> Pouring zstd--1.5.7_1.arm64_golden_gate.bottle.tar.gz 🍺 /opt/homebrew/Cellar/zstd/1.5.7_1: 32 files, 2.3MB ==> Installing ollama dependency: python@3.15 (3.15.0) ==> Pouring python@3.15--3.15.0.arm64_golden_gate.bottle.tar.gz 🍺 /opt/homebrew/Cellar/python@3.15/3.15.0: 3,983 files, 79MB ==> Installing ollama dependency: mlx (0.32.4) ==> Pouring mlx--0.32.4.arm64_golden_gate.bottle.tar.gz 🍺 /opt/homebrew/Cellar/mlx/0.32.4: 466 files, 240MB ==> Installing ollama dependency: mlx-c (0.7.0_1) ==> Pouring mlx-c--0.7.0_1.arm64_golden_gate.bottle.tar.gz 🍺 /opt/homebrew/Cellar/mlx-c/0.7.0_1: 40 files, 1MB ==> Installing ollama ==> Pouring ollama--0.40.6.arm64_golden_gate.bottle.tar.gz 🍺 /opt/homebrew/Cellar/ollama/0.40.6: 15 files, 54MB ==> Caveats ==> ollama To start ollama now and restart at login: brew services start ollama Or, if you don't want/need a background service you can just run: OLLAMA_FLASH_ATTENTION="1" OLLAMA_KV_CACHE_TYPE="q8_0" /opt/homebrew/opt/ollama/bin/ollama serve suzuyu@macmini-m6 ~ % brew list --versions ollama ollama 0.40.6 suzuyu@macmini-m6 ~ % command -v ollama /opt/homebrew/bin/ollama suzuyu@macmini-m6 ~ % </div></details> 導入版が0.40.6、コマンドのパスが /opt/homebrew/bin/ollama であることを確認し、比較期間中の更新を防ぐ brew pin ollama pin は導入済みの版を保持する操作で、特定の旧版を取得するものではない。比較を終えて更新する際は brew unpin ollama で解除する 起動確認 Homebrew のサービスとして起動する。手動の ollama serve が動いている場合は、そのターミナルで Ctrl+C を押して終了してから実行する brew services start ollama brew services list ollama が started となることを確認する。今回は sudo を付けず、ログインユーザーのサービスとして登録する。ターミナルを閉じても動作し、次回ログイン時にも起動する。OS 起動時からログイン前に動かす設定は今回の対象外となる。Homebrew services 続けて、クライアントとサーバのバージョン、待ち受け先を確認する。サービスの起動直後に接続できない場合は、起動を待って再確認する ollama --version curl --fail-with-body http://127.0.0.1:11434/api/version lsof -nP -iTCP:11434 -sTCP:LISTEN バージョンが0.40.6、待ち受け先が 127.0.0.1:11434 であることを確認する。サーバの再起動は brew services restart ollama、停止とログイン時の自動起動解除は brew services stop ollama で行う Homebrew のサービスには、今回の配布情報で OLLAMA_FLASH_ATTENTION=1 と OLLAMA_KV_CACHE_TYPE=q8_0 が設定されている。比較時にはサービス設定とサーバログを確認し、実際の適用状況を記録する % curl --fail-with-body http://127.0.0.1:11434/api/version {"version":"0.40.6"}% 続けてモデル一覧を確認する ollama ls 初回はモデルが未登録でも問題ない。サーバへの接続エラーが出ず、一覧を取得できることを確認する <details><summary>Ollama の起動確認ログ</summary><div> suzuyu@macmini-m6 ~ % command -v ollama /opt/homebrew/bin/ollama suzuyu@macmini-m6 ~ % suzuyu@macmini-m6 ~ % brew pin ollama suzuyu@macmini-m6 ~ % suzuyu@macmini-m6 ~ % ollama --version ollama version is 0.40.6 suzuyu@macmini-m6 ~ % curl --fail-with-body http://127.0.0.1:11434/api/version {"version":"0.40.6"}% suzuyu@macmini-m6 ~ % lsof -nP -iTCP:11434 -sTCP:LISTEN COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME ollama 6835 suzuyu 4u IPv4 0xa5d1408b43cabbe0 0t0 TCP 127.0.0.1:11434 (LISTEN) suzuyu@macmini-m6 ~ % ollama ls NAME ID SIZE MODIFIED suzuyu@macmini-m6 ~ % </div></details> gpt-oss 20B のダウンロードと起動 ダウンロード 下記でモデルをダウンロードする ollama pull gpt-oss:20b 完了後、一覧を確認する ollama ls % ollama ls NAME ID SIZE MODIFIED gpt-oss:20b f38aa0c53da5 13 GB 51 seconds ago モデル名に加えて ID とサイズも記録しておく。同じタグでも配布内容が更新されることがあるため、検証時点の情報を残す <details><summary>gpt-oss 20B のダウンロードログ</summary><div> suzuyu@macmini-m6 ~ % ollama pull gpt-oss:20b pulling manifest pulling 8ecb5d4ad617: 100% ▕███████████████████████████▏ 13 GB pulling d8ba2f9a17b3: 100% ▕███████████████████████████▏ 18 B pulling e103573196b6: 100% ▕███████████████████████████▏ 139 B verifying sha256 digest writing manifest success suzuyu@macmini-m6 ~ % ollama ls NAME ID SIZE MODIFIED gpt-oss:20b f38aa0c53da5 13 GB 51 seconds ago suzuyu@macmini-m6 ~ % </div></details> コンテキスト長を8Kに設定する 検証用の作業ディレクトリを作成する。以降の設定・リクエスト・結果ファイルはここに保存する mkdir -p ~/macmini-localllm/verification cd ~/macmini-localllm/verification OpenAI 互換 API ではコンテキスト長を直接指定できないため、8Kを設定したモデル名を作る。再学習ではなく、取得済みモデルに設定を付ける操作となる。設定方法 cat > Modelfile.gpt-oss-20b <<'EOF' FROM gpt-oss:20b PARAMETER num_ctx 8192 EOF ollama create gpt-oss-20b-8k -f Modelfile.gpt-oss-20b ollama show --modelfile gpt-oss-20b-8k 以下の実行では gpt-oss-20b-8k を使う <details><summary>モデル作成ログと Modelfile 全文</summary><div> suzuyu@macmini-m6 ~ % mkdir -p ~/macmini-localllm/verification cd ~/macmini-localllm/verification suzuyu@macmini-m6 verification % cat > Modelfile.gpt-oss-20b <<'EOF' FROM gpt-oss:20b PARAMETER num_ctx 8192 EOF suzuyu@macmini-m6 verification % ollama create gpt-oss-20b-8k -f Modelfile.gpt-oss-20b gathering model components using existing layer sha256:8ecb5d4ad6172c63f9cbf1a07a04e525985a8a11a6e516da02b674c4010de716 using existing layer sha256:fa6710a93d78da62641e192361344be7a8c0a1c3737f139cf89f20ce1626b99c using existing layer sha256:f60356777647e927149cbd4c0ec1314a90caba9400ad205ddc4ce47ed001c2d6 creating new layer sha256:e8fcfac0b1234eeb8c811092c47d0dfa6f9368b9171ba2abfcabed9e22ea9dc5 writing manifest success suzuyu@macmini-m6 verification % ollama show --modelfile gpt-oss-20b-8k Modelfile generated by "ollama show" To build a new Modelfile based on this, replace FROM with: FROM gpt-oss-20b-8k:latest FROM /Users/suzuyu/.ollama/models/blobs/sha256-8ecb5d4ad6172c63f9cbf1a07a04e525985a8a11a6e516da02b674c4010de716 TEMPLATE """<|start|>system<|message|>You are ChatGPT, a large language model trained by OpenAI. Knowledge cutoff: 2024-06 Current date: {{ currentDate }} {{- if and .IsThinkSet .Think (ne .ThinkLevel "") }} Reasoning: {{ .ThinkLevel }} {{- else if or (not .IsThinkSet) (and .IsThinkSet .Think) }} Reasoning: medium {{- end }} {{- $hasNonBuiltinTools := false }} {{- if .Tools -}} {{- $hasBrowserSearch := false }} {{- $hasBrowserOpen := false }} {{- $hasBrowserFind := false }} {{- $hasPython := false }} {{- range .Tools }} {{- if eq .Function.Name "browser.search" -}}{{- $hasBrowserSearch = true -}} {{- else if eq .Function.Name "browser.open" -}}{{- $hasBrowserOpen = true -}} {{- else if eq .Function.Name "browser.find" -}}{{- $hasBrowserFind = true -}} {{- else if eq .Function.Name "python" -}}{{- $hasPython = true -}} {{- else }}{{ $hasNonBuiltinTools = true -}} {{- end }} {{- end }} {{- if or $hasBrowserSearch $hasBrowserOpen $hasBrowserFind $hasPython }} Tools {{- if or $hasBrowserSearch $hasBrowserOpen $hasBrowserFind }} browser // Tool for browsing. // The cursor appears in brackets before each browsing display: [{cursor}]. // Cite information from the tool using the following format: // 【{cursor}†L{line_start}(-L{line_end})?】, for example: 【6†L9-L11】 or 【8†L3】. // Do not quote more than 10 words directly from the tool output. // sources=web (default: web) namespace browser { {{- if $hasBrowserSearch }} // Searches for information related to query and displays topn results. type search = (_: { query: string, topn?: number, // default: 10 source?: string, }) => any; {{- end }} {{- if $hasBrowserOpen }} // Opens the link id from the page indicated by cursor starting at line number loc, showing num_lines lines. // Valid link ids are displayed with the formatting: 【{id}†.*】. // If cursor is not provided, the most recent page is implied. // If id is a string, it is treated as a fully qualified URL associated with source. // If loc is not provided, the viewport will be positioned at the beginning of the document or centered on the most relevant passage, if available. // Use this function without id to scroll to a new location of an opened page. type open = (_: { id?: number | string, // default: -1 cursor?: number, // default: -1 loc?: number, // default: -1 num_lines?: number, // default: -1 view_source?: boolean, // default: false source?: string, }) => any; {{- end }} {{- if $hasBrowserFind }} // Finds exact matches of pattern in the current page, or the page given by cursor. type find = (_: { pattern: string, cursor?: number, // default: -1 }) => any; {{- end }} } // namespace browser {{- end }}{{/* end if has browser tools */}} {{- if $hasPython }} python Use this tool to execute Python code in your chain of thought. The code will not be shown to the user. This tool should be used for internal reasoning, but not for code that is intended to be visible to the user (e.g. when creating plots, tables, or files). When you send a message containing Python code to python, it will be executed in a stateful Jupyter notebook environment. python will respond with the output of the execution or time out after 120.0 seconds. The drive at '/mnt/data' can be used to save and persist user files. Internet access for this session is UNKNOWN. Depends on the cluster. {{- end }}{{/* end if hasPython */}} {{- end }}{{/* end if has any built-in tools */}} {{- end }}{{/* end if .Tools */}} Valid channels: analysis, commentary, final. Channel must be included for every message.{{ if $hasNonBuiltinTools }} Calls to these tools must go to the commentary channel: 'functions'. {{- end -}}<|end|>{{/* end of system */ -}} {{- if or $hasNonBuiltinTools .System -}} <|start|>developer<|message|>{{- if $hasNonBuiltinTools }}# Tools functions namespace functions { {{- range .Tools }} {{- if not (or (eq .Function.Name "browser.search") (eq .Function.Name "browser.open") (eq .Function.Name "browser.find") (eq .Function.Name "python")) }} {{if .Function.Description }} // {{ .Function.Description }} {{- end }} {{- if and .Function.Parameters.Properties (gt (len .Function.Parameters.Properties) 0) }} type {{ .Function.Name }} = (_: { {{- range $name, $prop := .Function.Parameters.Properties }} {{- if $prop.Description }} // {{ $prop.Description }} {{- end }} {{ $name }}: {{ $prop | toTypeScriptType }}, {{- end }} }) => any; {{- else }} type {{ .Function.Name }} = () => any; {{- end }} {{- end }}{{/* end if not browser tool */}} {{- end }}{{/* end of range .Tools */}} } // namespace functions {{- end }}{{/* end if hasNonBuiltinTools */}} {{- if .System}} Instructions {{ .System }} {{- end -}} <|end|> {{- end -}} {{- /* Find the index of the last user message */ -}} {{- $lastUserIdx := -1 }} {{- $prefillingContent := false }} {{- $prefillingThinkingOnly := false }} {{- range $i, $msg := .Messages }} {{- $last := eq (len (slice $.Messages $i)) 1 -}} {{- if eq $msg.Role "user" }} {{- $lastUserIdx = $i }} {{- end -}} {{- if and $last (eq $msg.Role "assistant") (gt (len $msg.Content) 0) }} {{- $prefillingContent = true }} {{- else if and $last (eq $msg.Role "assistant") (gt (len $msg.Thinking) 0) }} {{- $prefillingThinkingOnly = true }} {{- end }} {{- end -}} {{- /* Now render messages */ -}} {{- range $i, $msg := .Messages }} {{- $last := eq (len (slice $.Messages $i)) 1 -}} {{- if (ne $msg.Role "system") -}} {{- if eq $msg.Role "tool" -}} {{- if or (eq $msg.ToolName "python") (eq $msg.ToolName "browser.search") (eq $msg.ToolName "browser.open") (eq $msg.ToolName "browser.find") -}} <|start|>{{ $msg.ToolName }} to=assistant<|message|>{{ $msg.Content }}<|end|> {{- else -}} <|start|>functions.{{ $msg.ToolName }} to=assistant<|message|>{{ $msg.Content }}<|end|> {{- end -}} {{- else if eq $msg.Role "assistant" -}} {{- if and $msg.Thinking (gt $i $lastUserIdx) -}}{{- /* Show thinking only after last user message */ -}} <|start|>assistant<|channel|>analysis<|message|>{{ $msg.Thinking }}{{- if not $prefillingThinkingOnly -}}<|end|>{{- end -}} {{- end -}} {{- if gt (len $msg.Content) 0 -}} <|start|>assistant<|channel|>final<|message|>{{ $msg.Content }}{{- if not $prefillingContent -}}<|end|>{{- end -}} {{- end -}} {{- if gt (len $msg.ToolCalls) 0 -}} {{- range $j, $toolCall := $msg.ToolCalls -}} {{- $isBuiltin := or (eq $toolCall.Function.Name "python") (eq $toolCall.Function.Name "browser.search") (eq $toolCall.Function.Name "browser.open") (eq $toolCall.Function.Name "browser.find") -}} <|start|>assistant<|channel|>{{ if $isBuiltin }}analysis{{ else }}commentary{{ end }} to={{ if not $isBuiltin}}functions.{{end}}{{ $toolCall.Function.Name }} <|constrain|>json<|message|>{{ $toolCall.Function.Arguments }}<|call|> {{- end -}} {{- end -}} {{- else if eq $msg.Role "user" -}} <|start|>{{ $msg.Role }}<|message|>{{ $msg.Content }}<|end|> {{- end }} {{- else }} {{- end }} {{- end -}} {{- if not (or $prefillingContent $prefillingThinkingOnly) -}} <|start|>assistant {{- end -}}""" PARAMETER temperature 1 PARAMETER num_ctx 8192 LICENSE """ Apache License Version 2.0, January 2004 apache.org TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made,

  4. @sigma5736394841 わかります、僕もこれでエンジニアとして一段階上のステージに行けました brew#1535

    2 likes · 22 views

Latest stargazers

The last 10 distinct accounts we observed this year.

Most active participants

PR, issue, comment & review events in the past year.

Packages

1
PackageVersionDownloads · 30dTrend · 30d
———

README badges

Embed a badge in your README or website.

  • Homebrew/brew reached #2 in daily stars on gitwal · Feb 6, 2021
Live mentions · last 30 days
Homebrew/brew mentions in the last 30 days