1. Go to this page and download the library: Download pocketarc/ai-workflow library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
use AiWorkflow\AiService;
use AiWorkflow\Facades\Prompt;
use Prism\Prism\ValueObjects\Messages\UserMessage;
$aiService = app(AiService::class);
$response = $aiService->sendMessages(
collect([new UserMessage('What is the weather like?')]),
Prompt::load('chat'),
);
echo $response->text;
use Prism\Prism\Schema\ObjectSchema;
use Prism\Prism\Schema\StringSchema;
use Prism\Prism\Schema\NumberSchema;
$schema = new ObjectSchema(
name: 'analysis',
description: 'Ticket analysis',
properties: [
new StringSchema('summary', 'A brief summary'),
new NumberSchema('priority', 'Priority from 1-5'),
],
use AiWorkflow\Attributes\Description;
use Spatie\LaravelData\Data;
class SentimentAnalysis extends Data
{
public function __construct(
#[Description('The detected sentiment: positive, negative, or neutral')]
public readonly string $sentiment,
#[Description('Confidence score from 0.0 to 1.0')]
public readonly float $confidence,
) {}
}
$result = $aiService->sendStructuredData(
collect([new UserMessage('Analyze the sentiment of: "I love this product!"')]),
Prompt::load('sentiment'),
SentimentAnalysis::class,
);
// $result is a validated SentimentAnalysis instance
echo $result->sentiment; // "positive"
echo $result->confidence; // 0.95
$stream = $aiService->streamMessages(
collect([new UserMessage('Tell me a story')]),
Prompt::load('chat'),
);
foreach ($stream as $event) {
if ($event instanceof \Prism\Prism\Streaming\Events\TextDeltaEvent) {
echo $event->delta;
}
}
// In your AppServiceProvider::boot()
use AiWorkflow\AiService;
use Prism\Prism\Facades\Tool;
$aiService = app(AiService::class);
$aiService->resolveToolsUsing(fn (array $context) => [
Tool::as('get_weather')
->for('Get current weather conditions.')
->withStringParameter('city', 'The city name')
->using(fn (string $city): string => "Weather in {$city}: sunny, 20°C"),
]);
use AiWorkflow\Models\AiWorkflowRequest;
AiWorkflowRequest::query()->withTag('classification')->get();
AiWorkflowRequest::query()->withAnyTag(['classification', 'intent'])->get();
AiWorkflowRequest::query()->byModel('claude-4')->successful()->get();
AiWorkflowRequest::query()->errors()->get();
$aiService->startExecution('work_ticket', ['ticket_id' => $ticket->id]);
$aiService->sendMessages($messages, Prompt::load('decide_action'));
$aiService->sendMessages($messages, Prompt::load('generate_response'));
$aiService->sendStructuredMessages($messages, Prompt::load('judge_response'), $schema);
$execution = $aiService->endExecution();
// All three calls are linked to this execution.
use AiWorkflow\Models\AiWorkflowExecution;
$execution = AiWorkflowExecution::query()->byName('work_ticket')->recent()->first();
$execution->totalInputTokens();
$execution->totalOutputTokens();
$execution->totalTokens();
$execution->totalDurationMs();
$execution->requestCount();
use AiWorkflow\Events\AiWorkflowRequestCompleted;
use AiWorkflow\Events\AiWorkflowRequestFailed;
use AiWorkflow\Listeners\SentrySpanListener;
protected $listen = [
AiWorkflowRequestCompleted::class => [SentrySpanListener::class . '@handleCompleted'],
AiWorkflowRequestFailed::class => [SentrySpanListener::class . '@handleFailed'],
];
use AiWorkflow\Middleware\AiWorkflowContext;
use AiWorkflow\Middleware\AiWorkflowMiddleware;
class LogRequestMetrics implements AiWorkflowMiddleware
{
public function handle(AiWorkflowContext $context, Closure $next): AiWorkflowContext
{
// Before the AI request
$start = microtime(true);
$context = $next($context);
// After the AI request
logger()->info('AI request took ' . (microtime(true) - $start) . 's');
return $context;
}
}
use AiWorkflow\Middleware\InputGuardrail;
use AiWorkflow\Middleware\AiWorkflowContext;
class PiiDetectionGuardrail extends InputGuardrail
{
protected function validate(AiWorkflowContext $context): void
{
// Throw GuardrailViolationException if PII is detected in messages
}
}
use AiWorkflow\AiWorkflowReplayer;
$replayer = app(AiWorkflowReplayer::class);
// Replay exactly as recorded
$result = $replayer->replay($request);
// Replay with a different model
$result = $replayer->replay($request, model: 'anthropic:claude-4');
// Replay with the latest prompt from disk (uses the stored prompt_id to load)
$result = $replayer->replay($request, useCurrentPrompts: true);
// Both: latest prompt + different model
$result = $replayer->replay($request, useCurrentPrompts: true, model: 'anthropic:claude-4');
// Compare one request across multiple models
$results = $replayer->replayAcrossModels($request, [
'openrouter:google/gemini-3-pro',
'anthropic:claude-4',
'openrouter:openai/gpt-5.2',
]);
// Returns array keyed by model name.
// Replay an entire execution — each request loads its own prompt via prompt_id
$results = $replayer->replayExecution($execution, useCurrentPrompts: true);
// In your action, group AI calls under an execution:
$aiService->startExecution('decide_action #42', ['ticket_id' => 42]);
$response = $aiService->sendStructuredMessages($messages, $prompt, $schema);
$execution = $aiService->endExecution();
// $execution->id is the UUID you'll reference
use AiWorkflow\Eval\AiWorkflowEvalJudge;
use AiWorkflow\Eval\AiWorkflowEvalResult;
use AiWorkflow\Models\AiWorkflowRequest;
use Prism\Prism\Text\Response;
use Prism\Prism\Structured\Response as StructuredResponse;
class MyJudge implements AiWorkflowEvalJudge
{
public function judge(AiWorkflowRequest $originalRequest, Response|StructuredResponse $response): AiWorkflowEvalResult
{
// Compare the new response against the original recorded response
// Return a score from 0.0 to 1.0
return new AiWorkflowEvalResult(score: 0.9, details: ['reasoning' => '...']);
}
}
use AiWorkflow\Eval\AiWorkflowEvalRunner;
use AiWorkflow\Models\AiWorkflowEvalDataset;
$runner = app(AiWorkflowEvalRunner::class);
$dataset = AiWorkflowEvalDataset::query()->where('name', 'decide-actions')->firstOrFail();
$evalRun = $runner->run(
name: 'Decision eval',
requests: $dataset->requests(),
models: ['openrouter:anthropic/claude-4', 'openrouter:google/gemini-3-pro'],
judge: app(MyJudge::class),
);
$evalRun->averageScore(); // overall
$evalRun->averageScoreForModel('openrouter:anthropic/claude-4'); // per model
use Prism\Prism\Enums\FinishReason;
use Prism\Prism\Facades\Prism;
use Prism\Prism\Testing\TextResponseFake;
Prism::fake([
TextResponseFake::make()
->withText('Mocked response')
->withFinishReason(FinishReason::Stop),
]);
// Your code that calls AiService will receive the fake response.