Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Api Ai Php Laravel Package

iboldurev/api-ai-php

A PHP client for the API.AI (Dialogflow) natural language service, letting you add intent detection, entity recognition, and conversational agents to PHP or Laravel apps through a simple, expressive API.

Deep Wiki
Context7

Getting Started

This is a thin, unmaintained (last release Oct 2017) PHP wrapper around the Dialogflow/API.AI v1 HTTP API — not the v2 gRPC/REST API. Before anything else, confirm the endpoint it targets still works for your agent; if you are on Dialogflow ES v2 or CX, this package will not talk to your project.

Install and verify the source

composer require iboldurev/api-ai-php
ls vendor/iboldurev/api-ai-php/src

The repo is not linked from Packagist metadata you have, so read composer.json and src/ first. The classes you'll actually touch are the client (HTTP + auth), a query/message object (text, session, contexts, language), and a response wrapper around the JSON body.

Wire up the token

Store the agent's client access token (Settings → Agent → API keys in the legacy console) in Laravel config, never inline:

// config/services.php
'dialogflow' => [
    'token' => env('DIALOGFLOW_CLIENT_TOKEN'),
],

// .env
DIALOGFLOW_CLIENT_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxx

First call

use IBoldurev\ApiAi\Client;
use IBoldurev\ApiAi\Query;

$client = new Client(config('services.dialogflow.token'));

$query = new Query('Book a table for two tomorrow at 7pm');
$query->setSessionId('chat-'.auth()->id());   // stable per conversation
$query->setLang('en');

$response = $client->query($query);

// The wrapper exposes the decoded "result" object; treat it as an array
// so you are not coupled to the DTO's getters.
$result = $response->getResult();
$reply  = $result['fulfillment']['speech'] ?? '';
$intent = $result['metadata']['intentName'] ?? null;
$params = $result['parameters'] ?? [];

First real use case: a form/chat widget where free-text input is classified into intents and slot parameters (date, time, partySize) are extracted, then handed to your own domain code. Don't use it for the response text alone — the value is the structured parameters and contexts.

Implementation Patterns

Bind it as a singleton, not a per-request new. The client holds a Guzzle instance; recreating it per call wastes connections and makes middleware-based logging harder.

// AppServiceProvider
$this->app->singleton(Client::class, fn () =>
    new Client(config('services.dialogflow.token'))
);

Session identity is the whole state machine. Dialogflow keeps context and follow-up state per sessionId. Use one ID per conversation (user + channel + thread), persisted — not uniqid() per request, or every follow-up question starts from zero.

$sessionId = "web:{$userId}:{$conversationId}";

Treat contexts as your slot-filling loop. When a required parameter is missing, Dialogflow returns an outputContext (e.g. awaiting_size) plus a prompt. Your loop is: send text → read actionIncomplete → if incomplete, keep the same session and just send the next user message. Don't reimplement slot filling in PHP; let the agent drive it.

$result = $response->getResult();

if (! empty($result['actionIncomplete'])) {
    return $result['fulfillment']['speech']; // ask the next question
}

return $this->dispatch($result['metadata']['intentName'], $result['parameters']);

Route intents to handlers, not to if chains.

protected array $handlers = [
    'book.table'   => BookTableIntent::class,
    'order.status' => OrderStatusIntent::class,
    'smalltalk'    => SmalltalkIntent::class,
];

Each handler gets (array $parameters, array $contexts) and returns a reply string. This keeps the SDK at the edge and your domain logic testable without HTTP.

Clear contexts to reset. If a user abandons a booking flow, the next message will still be inside awaiting_size. Send an explicit reset — either a /reset style query or an empty/null contexts payload with a fresh sessionId. Rotating the session ID is the cheap, reliable reset.

Map SDK/Guzzle failures to user-facing behaviour. Wrap calls and distinguish transport from semantic failure:

try {
    $response = $client->query($query);
} catch (\GuzzleHttp\Exception\ConnectException $e) {
    // timeout / DNS — retry once, then fall back to "try again"
} catch (\Throwable $e) {
    Log::warning('dialogflow.query_failed', ['session' => $sessionId, 'e' => $e->getMessage()]);
    return $this->fallbackReply();
}

A chatbot must never 500 on the user; degrade to a canned reply and log.

Log the raw payload, always. Classification problems are almost never PHP bugs — they're the agent missing training phrases. Persist query text → intentName → confidence → parameters (a JSON column or a log channel) so you can hand real misclassifications back to whoever owns the agent.

Async where latency doesn't matter. For analytics-style enrichment (tagging a conversation, sentiment, intent auditing) push the query into a queued job. Keep synchronous calls only for the request/response path.

Testing without the network. Inject a Guzzle client with a MockHandler (or bind a fake Client) so intent-handler tests assert on fixture payloads:

$mock = new MockHandler([new Response(200, [], json_encode($fixture))]);
$client = new Client('test-token', new \GuzzleHttp\Client(['handler' => HandlerStack::create($mock)]));

Gotchas and Tips

  • The v1 API was decommissioned. api.api.ai/v1/query and the whole v1 surface were retired; Dialogflow v2 requires a service-account JSON, OAuth2 access tokens, and a projects/{project}/agent/sessions/{session} path. If your target is a current Dialogflow agent, this package is a reference implementation at best — plan on google/cloud-dialogflow or a small hand-rolled v2 client. Validate this first; it determines whether the rest of this assessment matters.
  • Two different token types. The client access token (query runtime) and the developer access token (agent/intent CRUD) are not interchangeable. A 401/403 on a query usually means a developer token was pasted into config.
  • Guzzle version lock. A 2017 SDK pins Guzzle 6.x. On PHP 8 with modern Guzzle 7 this can conflict; check composer why-not guzzlehttp/guzzle before blaming your own code. Isolating the dependency in a small internal service is cheaper than fighting it.
  • sessionId defaults can silently create fresh state. If the query object doesn't carry a session ID, every call may look like a new conversation — follow-ups never resolve, contexts never persist. Always set it explicitly.
  • Response shape is nested. result.fulfillment.speech, result.parameters, result.contexts, result.metadata.intentName, result.score, result.actionIncomplete. Reaching for a custom getter that doesn't exist wastes time; read the array and log one real payload early.
  • Missing parameters appear as null, not absent. $params['date'] ?? null will be null both when unset and when the agent returned an empty slot. Check actionIncomplete and your required-slot list rather than testing nulls.
  • Timezone and dates. Pass the user's timezone (timezone parameter) or relative phrases ("tomorrow") resolve against the agent's default timezone — a classic off-by-one-day bug. Same for lang: default it from the request, not the app config.
  • Rate limits and cold starts. Expect occasional 429/5xx; one short retry with jitter is enough. Set Guzzle connect/read timeouts (2–5s) — the default of "no timeout" will hang a web request behind a slow upstream.
  • Debugging at the transport layer. Attach a Middleware::tap or Middleware::log to the handler stack to see exact request/response bodies; the SDK's exceptions often don't include the response body, and Dialogflow's error JSON is where the actual message lives.
  • Extension points are thin. The realistic customisation is (a) wrap the client in your own DialogflowService and never expose SDK types above it, (b) add Guzzle middleware for auth refresh/logging, (c) fork the package — for a ~10-file 2017 SDK, vendoring the source into app/Services/Dialogflow and deleting the Composer dependency is often the lowest-risk move, since no upstream fixes are coming.
  • No maintenance = pin it. Commit the lockfile entry, and add a code comment linking the Dialogflow v2 migration as known debt. Do not build new features directly against this package's types.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource