Skip to content

Latest commit

 

History

History
248 lines (185 loc) · 7.62 KB

File metadata and controls

248 lines (185 loc) · 7.62 KB

How to use the Apple Foundation Model from PHP

Call Apple's on-device Foundation Model from PHP using openai-php/client, pointed at a local apfel --serve. 100% on-device, zero API cost.

Runnable scripts + tests: Arthur-Ficial/apfel-guides-lab/scripts/php.

Prerequisites

  • macOS 26+ Tahoe, Apple Silicon, Apple Intelligence enabled
  • brew install apfel
  • apfel --serve running (port 11434)
  • PHP 8.1+ and Composer (brew install php composer)
  • composer require openai-php/client guzzlehttp/guzzle

openai-php/client needs a PSR-18 HTTP client; Guzzle is the usual pick.

1. One-shot

<?php
require __DIR__ . "/vendor/autoload.php";

$client = OpenAI::factory()
    ->withBaseUri("http://localhost:11434/v1")
    ->withApiKey("not-needed")
    ->make();

$response = $client->chat()->create([
    "model" => "apple-foundationmodel",
    "messages" => [
        ["role" => "user", "content" => "In one sentence, what is the Swift programming language?"],
    ],
    "max_tokens" => 80,
]);

echo trim($response->choices[0]->message->content ?? "") . "\n";

Real output:

Swift is a modern, open-source programming language developed by Apple for developing software on platforms like iOS, macOS, watchOS, and tvOS, known for its safety, performance, and simplicity.

Lab script: 01_oneshot.php.

2. Streaming

Use createStreamed and foreach:

<?php
require __DIR__ . "/vendor/autoload.php";

$client = OpenAI::factory()->withBaseUri("http://localhost:11434/v1")->withApiKey("not-needed")->make();

$stream = $client->chat()->createStreamed([
    "model" => "apple-foundationmodel",
    "messages" => [["role" => "user", "content" => "List three Apple silicon chips, one per line."]],
    "max_tokens" => 80,
]);

foreach ($stream as $response) {
    if (empty($response->choices)) continue;
    echo $response->choices[0]->delta->content ?? "";
    flush();
}
echo "\n";

Real output:

Apple M1
Apple M2
Apple M2 Pro

Lab script: 02_stream.php.

3. JSON mode

<?php
require __DIR__ . "/vendor/autoload.php";

$client = OpenAI::factory()->withBaseUri("http://localhost:11434/v1")->withApiKey("not-needed")->make();

$response = $client->chat()->create([
    "model" => "apple-foundationmodel",
    "messages" => [["role" => "user", "content" => "Return JSON with fields 'chip', 'year', 'cores'. Describe the Apple M1 chip. Return ONLY JSON."]],
    "response_format" => ["type" => "json_object"],
    "max_tokens" => 120,
]);

$raw = trim($response->choices[0]->message->content ?? "");
$raw = preg_replace('/\A```(?:json)?\s*|\s*```\z/m', "", $raw);
$data = json_decode(trim($raw), true, flags: JSON_THROW_ON_ERROR);
echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n";

Real output:

{
    "chip": "Apple M1",
    "year": 2020,
    "cores": {
        "cpu": 8,
        "gpu": 8
    }
}

Lab script: 03_json.php.

4. Error handling

<?php
require __DIR__ . "/vendor/autoload.php";
use OpenAI\Exceptions\ErrorException;

$client = OpenAI::factory()->withBaseUri("http://localhost:11434/v1")->withApiKey("not-needed")->make();

try {
    $client->embeddings()->create([
        "model" => "apple-foundationmodel",
        "input" => "apfel runs 100% on-device.",
    ]);
} catch (ErrorException $e) {
    echo "Got expected error (HTTP 501): {$e->getMessage()}\n";
}

Real output:

Got expected error (HTTP 501): Embeddings not supported by Apple's on-device model.

Lab script: 04_errors.php.

5. Tool calling

<?php
require __DIR__ . "/vendor/autoload.php";

$client = OpenAI::factory()->withBaseUri("http://localhost:11434/v1")->withApiKey("not-needed")->make();

$tools = [[
    "type" => "function",
    "function" => [
        "name" => "get_weather",
        "description" => "Get the current temperature in Celsius for a city.",
        "parameters" => [
            "type" => "object",
            "properties" => ["city" => ["type" => "string"]],
            "required" => ["city"],
        ],
    ],
]];

function get_weather(array $args): string {
    $fake = ["Vienna" => 14, "Cupertino" => 19, "Tokyo" => 11];
    $city = $args["city"] ?? "";
    return json_encode(["city" => $city, "temp_c" => $fake[$city] ?? 15]);
}

$messages = [["role" => "user", "content" => "What is the temperature in Vienna right now?"]];

$first = $client->chat()->create([
    "model" => "apple-foundationmodel", "messages" => $messages, "tools" => $tools, "max_tokens" => 256,
]);
$msg = $first->choices[0]->message;
$messages[] = $msg->toArray();

if (!empty($msg->toolCalls)) {
    foreach ($msg->toolCalls as $call) {
        $args = json_decode($call->function->arguments, true) ?? [];
        $messages[] = ["role" => "tool", "tool_call_id" => $call->id, "content" => get_weather($args)];
    }
    $final = $client->chat()->create([
        "model" => "apple-foundationmodel", "messages" => $messages, "max_tokens" => 120,
    ]);
    echo trim($final->choices[0]->message->content ?? "") . "\n";
}

Real output:

The current temperature in Vienna is 15°C.

Lab script: 05_tools.php.

6. Real example - summarize stdin

<?php
require __DIR__ . "/vendor/autoload.php";

$text = trim(stream_get_contents(STDIN));
if ($text === "") {
    fwrite(STDERR, "usage: cat file.txt | php 06_example.php\n");
    exit(1);
}

$client = OpenAI::factory()->withBaseUri("http://localhost:11434/v1")->withApiKey("not-needed")->make();

$response = $client->chat()->create([
    "model" => "apple-foundationmodel",
    "messages" => [
        ["role" => "system", "content" => "You are a concise summarizer. Reply with one short paragraph."],
        ["role" => "user", "content" => "Summarize:\n\n$text"],
    ],
    "max_tokens" => 150,
]);
echo trim($response->choices[0]->message->content ?? "") . "\n";

Real output:

Apple's M1 chip, released in November 2020, was Apple's first ARM-based system-on-a-chip for Mac computers. It uses an 8-core CPU with four performance and four efficiency cores, plus an integrated GPU with up to 8 cores. The chip unified CPU, GPU, memory, and neural engine on a single die, delivering significant performance-per-watt improvements over the Intel chips it replaced.

Lab script: 06_example.php.

Troubleshooting

  • No PSR-18 clients found - composer require guzzlehttp/guzzle.
  • TLS / SSL errors - make sure your baseUri starts with http://, not https://.
  • Laravel / Symfony - works inside any container; register OpenAI::factory()->make() as a singleton pointed at APFEL_BASE_URL.

Tested with

  • apfel v1.0.3 / macOS 26.3.1 Apple Silicon (original capture; the CLI and HTTP surfaces used here are release-gated by apfel's test suite on every version)
  • PHP 8.5.5 / openai-php/client 0.10.3 / Guzzle
  • Date: 2026-04-16

Runnable tests: tests/test_php.py.

See also

python.md, nodejs.md, ruby.md, bash-curl.md, apfel-guides-lab