Download the PHP package pocketarc/ai-workflow without Composer

On this page you can find all versions of the php package pocketarc/ai-workflow. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package ai-workflow

AI Workflow

AI Workflow is a Laravel package for running AI workflows in production. It is built on the Laravel AI SDK and sends OpenRouter calls through laravel-integrations, which handles circuit breaking, retries, failure classification, and rate limiting. On top of that, the package has fallback models, finish reason monitoring, YAML-based prompt management with Mustache templating, request logging with tagging, a middleware pipeline, caching, multimodal support, an eval framework, and Laravel Data integration.

AI Workflow requires PHP 8.3+ and Laravel 12.62+ or 13.15+.

Installation

Publish the config file:

OpenRouter calls are routed through pocketarc/laravel-integrations (installed automatically as a dependency), which owns the circuit breaker, retries, and rate limiting. Publish and run its migrations, then create an OpenRouter integration:

The integration row holds the OpenRouter API key, and the package uses that key for every OpenRouter request, including replays. The row also stores the circuit-breaker and rate-limit state. The package reads the OpenRouter URL and extra headers from config('ai.providers.openrouter'). To change them, publish the Laravel AI SDK's config with php artisan vendor:publish --tag=ai-config.

A database is therefore required for OpenRouter requests, because it stores the integration row and its transport audit. Calls to providers without a registered integration (for example anthropic:...) are sent directly through the Laravel AI SDK and do not use a database. The SDK reads the API keys for those providers from config/ai.php.

Request logging and the eval framework add their own tables. Publish and run ai-workflow's migrations to enable them:

Prompt Files

Prompts live as Markdown files with YAML front-matter in your configured prompts_path (default: resources/prompts/).

Front-matter fields:

The prompt's id is derived from the filename. A file at resources/prompts/my_prompt.md is my_prompt.

Mustache Templating

Prompts support Mustache variables and conditionals:

Load prompts without variables as before:

Usage

Text Responses

Send messages and get a text response with tool-calling support:

The response also has finishReason, usage (input, output, cache and thought tokens, summed over every step of the tool loop), meta (the response id, model and, for OpenRouter, the upstream provider and cost), toolCalls, toolResults, and messages. messages holds the assistant and tool-result messages from this call, which you can append to your conversation history.

Build conversations from UserMessage, AssistantMessage, ToolResultMessage and SystemMessage in AiWorkflow\Messages. A system message partway through a conversation is appended to the system prompt, because the Laravel AI SDK takes the instructions as one string, separate from the messages.

Attachments

Attach images, documents, audio and video to a user message:

An AttachmentKind::Media attachment is sent as an image, audio, video or document according to its MIME type. Only OpenRouter and Gemini support video attachments.

Structured Responses

Get structured JSON output matching a schema:

A ResponseSchema is a JSON Schema object. The package sends it to OpenRouter unchanged, in strict mode. For other providers, the package converts it to the Laravel AI SDK's own structured output, which is not strict and supports only part of JSON Schema. If the schema uses a feature that the SDK does not support, the package throws UnsupportedSchemaException.

Structured Responses with Laravel Data

If you have spatie/laravel-data installed, you can use Data classes directly. The package generates the schema from the class, validates the response, and retries with feedback on validation failure:

On validation failure, sendStructuredData() appends the error to the conversation and sends the request again. It makes at most $maxAttempts attempts (default 3). If no attempt passes validation, it throws StructuredValidationException. If an attempt fails with an exception that extends AiWorkflowException, such as GuardrailViolationException, sendStructuredData() rethrows the same exception instance. If an attempt throws any other exception, such as a provider error, sendStructuredData() throws StructuredDataRequestException, and getPrevious() returns the original exception.

Every exception that extends AiWorkflowException has a usage() method. It returns the token usage of the responses that were received before the exception was thrown, including a response that an OutputGuardrail rejected. For an exception from sendStructuredData(), the usage is summed across every attempt, including responses rejected for their finish reason. No tokens are counted for an attempt that fails at the provider. Outside sendStructuredData(), usage() returns null if no response was received, for example when an InputGuardrail blocks a sendMessages() call.

Streaming

Stream text responses as a generator of events:

The events in AiWorkflow\Streaming are StreamStart, TextDelta, ReasoningDelta, ToolCallEvent, ToolResultEvent, and a final StreamEnd with the finish reason, the usage and the response meta. If the provider sends an error during the stream, the generator throws it as an exception. There is no error event.

Streaming does not support automatic retries (this is inherent to how streaming APIs work).

Extra Context

Pass a second prompt as shared context that gets prepended to the system prompt:

Tool Registration

Register tools that the AI can call during text conversations:

The parameters are a JSON Schema object, and the handler receives the model's arguments as one array. A handler can return a string or any value that json_encode() can encode. If the handler throws, the package passes the exception to report() and sends the exception message to the model as the tool's result. If the model calls a tool that was not provided, the package throws UnknownToolException.

The package sends each step of the tool loop as a separate request. If a step fails, the package retries only that step, and the tools of earlier steps do not run again. The package ends the loop after ai-workflow.max_steps requests, or after $steps requests if you pass $steps.

Set context before making calls to pass runtime data to your tools:

Request Tagging

Tags help you categorize and filter logged requests. Set them in prompt front-matter and/or at runtime:

Tags from both sources are merged and deduplicated. Query with custom builder scopes:

Request Logging

When enabled, every AI call is recorded to the database with enough detail to replay it: system prompt, messages, model, provider, schema, response, token usage, duration, and tags. A failed call is recorded with its error, and with the HTTP status and response body when the provider sent one. If a middleware, such as an output guardrail, rejected the response, the row also includes that response and its token usage. If the response was rejected for its finish reason, the row includes its body and the tokens of every rejected attempt.

Enable logging in your .env:

Pruning

Logged requests carry full replay payloads, so the table grows quickly. Schedule ai-workflow:prune to delete requests older than the retention window, along with executions that are past the window, empty, and in no dataset:

Requests the eval framework references are kept regardless of age: annotated requests, scored requests, and requests whose execution belongs to an eval dataset. Deleting them would cascade away labels and scores, or break dataset replays.

Retention is configured in config/ai-workflow.php:

Execution Grouping

Group related AI calls under a named execution:

Query executions and get aggregate token usage:

Events

Two events are dispatched after every AI call, regardless of whether logging is enabled:

Sentry Integration

A ready-to-use listener adds Sentry breadcrumbs for AI requests. Register in your EventServiceProvider:

No hard dependency on Sentry — the listener is a no-op if Sentry is not installed.

Caching

Responses can be cached per-prompt using a content-addressable key derived from the request parameters. Set cache_ttl in the prompt front-matter:

Enable caching globally in your .env:

On a cache hit, no API call is made and no log record is created. sendStructuredData() caches a response only after it passes validation, and the result's usage includes no tokens for an attempt served from the cache. If a cache write fails, AiService passes the exception to Laravel's report() and still returns the response.

Middleware

Add before/after hooks to all AI requests using a middleware pipeline.

Global Middleware

Register middleware in your config:

Instance Middleware

Add middleware per-instance:

Writing Middleware

Implement AiWorkflowMiddleware:

Guardrails

Abstract base classes for input and output validation:

InputGuardrail validates before the request; OutputGuardrail validates after. Both throw GuardrailViolationException on failure.

Replay Engine

The replay engine lets you re-run recorded AI requests with different models or updated prompts. This is the foundation for evals.

Structured requests are replayed with the schema and schema name they were sent with. MySQL stores the keys of a JSON object in sorted order, so on MySQL the replayer restores each object's property order from its required list. This works when required lists every property in the same order as properties, as the schemas generated from Laravel Data classes do.

The replayer sends requests directly, not through the integration executor, because the eval runner retries replays with its own setting (ai-workflow.eval.replay_tries). OpenRouter replays still use the API key from the integration row.

Eval Framework

Evaluate AI outputs by replaying recorded requests from curated datasets across models with pluggable judges.

The workflow: run an AI action, verify the response is correct, add the execution to a named dataset, then eval that dataset against different models to see which ones produce equivalent results.

Building a Dataset

Datasets are collections of known-good executions. Use execution grouping to track AI calls, then add verified executions to a dataset:

Running Evals

This replays every request in the dataset against each model, judges the results, and displays a per-model score table.

Writing a Judge

Implement AiWorkflowEvalJudge:

The package includes AiJudge — an AI-powered judge that semantically compares original and new responses (e.g. {"payer": "John"} vs {"payer": "john"} scores high). For domain-specific evaluation, implement your own judge with custom scoring logic.

Running Evals in Code

Prompt Testing

Run YAML-defined test cases against prompts to verify AI outputs.

Test File Format

Test files live alongside prompts in a tests/ subdirectory:

Running Tests

Resilience

OpenRouter calls run through laravel-integrations, which owns the circuit breaker, retries, and rate limiting. Failures are classified so the breaker reacts correctly:

Backoff is a fixed ~30s pause on rate limits and ~attempt x 2s on server errors, with optional ±25% jitter. Tune it via ai-workflow.retry: times sets the max attempts, and rate_limit_delay_ms, server_error_multiplier_ms, and jitter shape the backoff.

When retries are exhausted, AiService throws one of the exceptions in AiWorkflow\Exceptions. They all extend ProviderException, which has the provider, the HTTP status (getStatusCode()) and the response body:

Exception Cause
RateLimitedException HTTP 429. retryAfter holds the Retry-After seconds.
InsufficientCreditsException HTTP 402.
ProviderOverloadedException HTTP 502, 503, 504, 520, 522 or 524.
ProviderConnectionException No response, such as a timeout or a refused connection.
ProviderRequestException Any other HTTP error.
UpstreamErrorException An error or empty body on an HTTP 200, or an error in a stream. errorCode holds the error code.
UnexpectedFinishReasonException An Unknown, Error or Other finish reason.
StructuredDecodingException Structured output that is not a JSON object.

sendStructuredData() throws these wrapped in StructuredDataRequestException.

The integration row stores the breaker state and rate budget. The transport audit is in integration_requests, with one row per HTTP request.

Fallback Models

If the JSON in a structured response cannot be decoded (the model produced invalid output, or a number too large to represent), the package retries the request with the fallback_model configured in the prompt's front-matter, if there is one. The fallback request is run through the same middleware as the original, and both requests are logged.

Finish Reason Handling

After each AI response, the finish reason is checked:

Finish Reason Behaviour
Stop, ToolCalls Success. The package returns the response.
Unknown, Error, Other Transient issue. The package retries, then throws UnexpectedFinishReasonException.
Length, ContentFilter Degraded. The package calls report(), then returns the response.

Testing

AiWorkflow\Testing\OpenRouterFake fakes OpenRouter's HTTP API with Laravel's HTTP client fake, so your tests run the same request, retry and logging code as production:

It also builds structured replies (structured()), HTTP errors (error(429, headers: ['Retry-After' => '5'])), errors on an HTTP 200 (errorEnvelope()), failed connections (connectionFailure()), and streams (textStream(), toolCallStream(), stream()). Queue every response in a single respondWith() call. The package reads the OpenRouter API key from the integration row, so create an OpenRouter integration row in your test setup.

Upgrading from 6.x

Version 7.0 uses the Laravel AI SDK instead of Prism. The package now has its own types, so you can remove every import from Prism\ in your code.

  1. Require Laravel 12.62+ or 13.15+. Laravel 11 is no longer supported.
  2. Remove the prism-php/prism requirement and the pocketarc/prism repository from your composer.json, unless you use Prism yourself.
  3. Make sure the OpenRouter integration row holds your OpenRouter key. In 7.0, the package reads the key from that row and ignores OPENROUTER_API_KEY. Put the keys for other providers in the Laravel AI SDK's config/ai.php.
  4. Replace the Prism types:
6.x (Prism) 7.0
ValueObjects\Messages\UserMessage and the other messages AiWorkflow\Messages\UserMessage and the other messages
ValueObjects\Media\Image, Document, Audio, Video, Text AiWorkflow\Messages\Attachment with an AttachmentKind
ValueObjects\ToolCall, ToolResult AiWorkflow\Messages\ToolCall, ToolResult
Schema\ObjectSchema and the other schemas AiWorkflow\Schema\ResponseSchema with a JSON Schema array
Tool::as(...)->using(...) new AiWorkflow\Tools\Tool(name, description, parameters, handler)
Text\Response, Structured\Response AiWorkflow\Responses\TextResponse, StructuredResponse
ValueObjects\Usage (promptTokens, completionTokens, cacheReadInputTokens, cacheWriteInputTokens) AiWorkflow\Responses\Usage (inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens)
ValueObjects\Meta AiWorkflow\Responses\ResponseMeta
Enums\FinishReason AiWorkflow\Enums\FinishReason, with the same values
Streaming\Events\*Event AiWorkflow\Streaming\*
PrismException and its subclasses AiWorkflow\Exceptions\ProviderException and its subclasses
Prism::fake() AiWorkflow\Testing\OpenRouterFake
  1. Update your judges' signature to judge(AiWorkflowRequest, TextResponse|StructuredResponse).
  2. Update your tool handlers to take a single array $arguments.

Other changes to plan for:

The package reads and replays the requests that 6.x logged, including those with base64 content next to a URL. It also reads the responses that 6.x cached, and builds the same schemas and cache keys as 6.x. The only exception is a request with a URL attachment. Its 6.x cache key included the base64 content of the file, so the package does not find the 6.x cache entry.

Development


All versions of ai-workflow with dependencies

PHP Build Version
Package Version
Requires php Version ^8.3
ext-curl Version *
illuminate/http Version ^12.0|^13.0
illuminate/json-schema Version ^12.62|^13.15
illuminate/support Version ^12.0|^13.0
laravel/ai Version ^1.0
mustache/mustache Version ^3.2
pocketarc/laravel-integrations Version ^6.3
spatie/yaml-front-matter Version ^2.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package pocketarc/ai-workflow contains the following files

Loading the files please wait ...