16: AI

Phlo bundelt een aantal AI-providers (OpenAI, Claude, Gemini, DeepSeek, Grok) achter een enkele façade. Jij kiest een model, Phlo kiest de juiste engine. Streaming naar de DOM gebruikt dezelfde apply() mechanismen als de rest van Phlo: geen aparte client-side bibliotheek, geen aparte event bus.

16.1: Hulpmiddelen

Voeg toe aan data/app.json:

{
    "resources": [..., "AI/AI", "AI/OpenAI"]
}

Één bestand per provider. AI/AI is de facade, die naar de juiste engine routeert op basis van het model:

Model bevat Engine
gpt-*, o1-*, o3-*, o4-*, chatgpt-* OpenAI
claude-* Claude
deepseek-* DeepSeek
gemini-* Gemini
grok-* Grok

Of expliciet via het via: argument: %AI->chat(via: 'claude', model: ...).

Het model argument is optioneel. Zonder een model valt de facade terug op %app->model en vervolgens op zijn eigen standaard, gpt-5.4-mini (dat naar OpenAI routeert), zodat een app met alleen een OpenAI sleutel in creds.ini direct werkt. Stel %app->model in om de standaard per app te wijzigen, of overschrijf %AI.model met een build mod om het systeemwijd te veranderen, inclusief de standaard engine.

De facade exposeert dezelfde methoden voor elke engine, maar niet elke provider ondersteunt elke methode:

Engine chat stream tools vision embeddings transcribe
OpenAI ja ja ja ja native ja
Claude ja ja ja ja OpenAI nee
Gemini ja ja ja ja native nee
DeepSeek ja ja ja nee OpenAI nee
Grok ja ja ja ja OpenAI nee

OpenAI in de embeddings kolom betekent dat de engine geen eigen embedding model heeft en delegeert naar OpenAI, dus het heeft ook een OpenAI sleutel nodig. DeepSeek en Grok zijn dunne lagen bovenop OpenAI (zelfde protocol, andere endpoint en sleutel), dus ze delen dezelfde methodenset; een nee cel betekent dat de provider geen model of endpoint achter die oproep heeft en het zal een fout geven. De matrix is de bron van waarheid: roep alleen een mogelijkheid aan die gemarkeerd is voor de engine die je target.

Inloggegevens gaan in data/creds.ini:

OpenAI = sk-...
Claude = sk-ant-...
Grok = xai-...

Phlo's security/creds laadt ze automatisch in %creds->OpenAI enz. Zie Configuratie voor het volledige inloggegevensformaat, omgevingsvariabelen en prioriteit.

16.2: Een enkel antwoord

Korte vraag, één antwoord:

$answer = %AI->chat(
    model: 'gpt-4o-mini',
    user: 'Vat dit artikel samen: '.$article->text,
)
echo $answer->answer

Of nog korter, via de answer helper:

$verdict = answer('Is "wortel" een groente?', 'ja', 'nee', 'misschien')

answer() is ingebouwd in AI/answer. Het doet één oproep met een lage temperatuur en retourneert alleen het puurste antwoord. Met opties wordt het een keuze uit de gegeven mogelijkheden.

16.3: Streamen naar de DOM

Dit is waar Phlo's apply() protocol echt tot zijn recht komt. Een async route die token voor token in een element schrijft:

route async POST chat::ask {
    %res->streaming = true
    foreach (%AI->stream(user: %payload->question) AS $chunk){
        if (isset($chunk->text)) apply(append: arr('#answer' => $chunk->text))
    }
}

Stel %res->streaming = true in en elke apply() wordt onmiddellijk naar de client geflusht op het moment dat je het aanroept, in plaats van gebufferd te worden totdat de respons eindigt. Elk token wordt aan #answer toegevoegd via hetzelfde apply() protocol dat je overal elders gebruikt: geen SSE plumbing, geen handmatige flush(), geen JS om te schrijven en geen status om te beheren, een streaming UI direct beschikbaar.

16.4: Hulpmiddelen (functieaanroep)

$tool = obj(
    name: 'get_weather',
    desc: 'Get the current weather for a location',
    args: arr(
        location: arr(type: 'string', desc: 'City and country, e.g. "Paris, FR"'),
    ),
)

$res = %AI->chat(
    model: 'gpt-4o-mini',
    user: 'What is the weather in Amsterdam?',
    tools: [OpenAI::tool($tool)],
)

foreach ($res->tools ?? [] AS $call){
    if ($call->name === 'get_weather') weather::fetch($call->args['location'])
}

Toolaanroepen komen terug onder $res->tools als een array van {name, args}. Phlo's facade normaliseert de verschillen tussen de providers.

16.5: Visie

Werkt met OpenAI, Claude, Gemini en Grok.

16.6: Insluitingen

$vector = %AI->embedding('Phlo is a compile-to-PHP framework', model: 'text-embedding-3-small')
%vectors->store(id: 'doc-1', vector: $vector, meta: ['source' => 'about'])

Het standaardmodel is specifiek voor de provider. Voor OpenAI is het text-embedding-3-small.

16.7: Transcribe

$file = %files->save(%payload->file('audio'))
$res = %AI->transcribe($file, model: 'whisper-1', language: 'nl')
echo $res->text

16.8: Veiligheid

We gebruiken essentiële cookies om deze site te laten werken. Met uw toestemming gebruiken we ook analytics om de site te verbeteren.