Fluent API & Execution

v1.0.0

Learn how to assemble decision pipelines using DecisionRequest, state context data, and handle typed DecisionResponse outputs in Laravel.

Fluent API & Execution

The Obelaw SystemOne fluent API provides an expressive, chainable interface for composing AI decision requests. Rather than orchestrating multiple HTTP calls or crafting fragile prompt strings, you define application state once and query multiple primitives simultaneously.


The SystemOne Facade

The primary entry point is the SystemOne facade (Obelaw\SystemOne\Facades\SystemOne):

use Obelaw\SystemOne\Facades\SystemOne;

// Initialize request with state
$request = SystemOne::state($context);

// Or initialize an empty request builder
$request = SystemOne::request();

Behind the facade is the SystemOneManager, which handles driver resolution, extensions, and lifecycle management.


Assembling a DecisionRequest

A DecisionRequest contains three core components:

  1. State: The context data evaluated by the decision engine.
  2. Questions: One or more registered decision primitives.
  3. Execution Options: Optional driver and model overrides.
flowchart TD
    subgraph Request["DecisionRequest Builder"]
        State["1. State Context<br/>String | Array | Eloquent Model (Arrayable)"]
        Questions["2. Questions Registry<br/>-&gt;ask('key', Noul | Choice | Score)"]
        Options["3. Driver &amp; Model Overrides<br/>-&gt;driver('...') | -&gt;model('...')"]
    end

    Request --> Run["-&gt;run() / -&gt;decide()"]
    Run --> Manager["SystemOneManager<br/>Driver Resolution"]
    Manager --> Response["DecisionResponse<br/>Helper methods: value(), confidence(), isPositive()"]

1. Setting State Context

State can be provided as a string, an associative array, or any object implementing Laravel’s Arrayable contract (including Eloquent models):

String Context

SystemOne::state('User IP changed from US to DE within 4 minutes. Failed password twice.')

Array Context

SystemOne::state([
    'user_id' => 8421,
    'account_age_days' => 2,
    'order_total' => 1450.00,
    'shipping_country' => 'NG',
    'billing_country' => 'US',
    'card_country' => 'US',
    'ip_proxy_detected' => true,
])

Eloquent Model Context

$ticket = SupportTicket::with('messages', 'customer')->findOrFail($id);

// Automatically serialized via Arrayable::toArray()
SystemOne::state($ticket)

2. Adding Decision Questions

You can chain questions sequentially with ask() or provide them in bulk:

use Obelaw\SystemOne\Primitives\Noul;
use Obelaw\SystemOne\Primitives\Choice;
use Obelaw\SystemOne\Primitives\Score;

$request = SystemOne::state($order)
    ->ask('is_fraudulent', Noul::make('Is this order fraudulent?'))
    ->ask('risk_tier', Choice::make('Assign risk category', [
        'low' => 'Normal consumer order',
        'medium' => 'Suspicious mismatch requiring review',
        'high' => 'High certainty fraud, block payment',
    ]))
    ->ask('chargeback_risk', Score::range('Estimated chargeback probability 0-100', 0, 100));

Bulk Questions Registration

$request->questions([
    'is_urgent' => Noul::make('Is this ticket urgent?'),
    'topic' => Choice::make('Topic', ['billing', 'shipping', 'returns']),
]);

3. Model & Driver Selection

By default, SystemOne uses the driver configured in config/clef.php. You can override the driver or model dynamically per request:

// Use faster flash model for low-latency triage
$response = SystemOne::state($alert)
    ->model('clef-flash')
    ->ask('ping_ok', Noul::make('Is service alive?'))
    ->run();

// Explicitly target a specific driver
$response = SystemOne::state($payload)
    ->driver('cloudflare')
    ->ask('result', Choice::make('Action', ['retry', 'abort']))
    ->run();

Working with DecisionResponse

Calling ->run() (or its alias ->decide()) executes the request and returns an implementation of Obelaw\SystemOne\Contracts\DecisionResponseInterface.

sequenceDiagram
    autonumber
    actor App as Laravel Application
    participant Builder as DecisionRequest
    participant Manager as SystemOneManager
    participant Driver as ClefDriver / CustomDriver
    participant Edge as Workers AI (Edge)
    participant Response as DecisionResponse

    App->>Builder: SystemOne::state($context)->ask(...)->run()
    Builder->>Manager: Resolve configured driver
    Manager->>Driver: decide(DecisionRequest)
    Driver->>Edge: HTTPS POST with serialized payload
    Edge-->>Driver: JSON response with evaluated answers
    Driver->>Response: instantiate DecisionResponse(answers, raw)
    Response-->>App: DecisionResponse instance
    App->>Response: ->value(), ->confidence(), ->isPositive()

Key Response Methods

MethodDescriptionReturn Type
value(string $question)Gets the resolved primitive value (boolean probability, selected choice key, or score number).mixed
confidence(string $question)Returns the driver’s confidence score for the selected answer.?float
probabilities(string $question)Returns the complete probability distribution across all categories for Choice primitives.array<string, float>
isPositive(string $question, float $threshold = 0.5)Checks if a binary or likelihood value strictly exceeds the given threshold.bool
isNegative(string $question, float $threshold = 0.5)Checks if a binary or likelihood value is below or equal to the given threshold.bool
answer(string $question)Returns the raw structured array representation for a single question.mixed
answers()Returns all parsed answers keyed by question identifier.array<string, mixed>
raw()Returns the complete unmodified response payload from the underlying AI driver.array<string, mixed>
toArray()Converts the response into an array for logging or JSON serialization.array

Code Example: Consuming Response

$response = SystemOne::state($customerFeedback)
    ->ask('sentiment', Choice::make('Customer sentiment', ['positive', 'neutral', 'negative']))
    ->ask('churn_risk', Noul::make('Is customer expressing cancellation intent?'))
    ->ask('satisfaction_score', Score::range('Satisfaction rating 1 to 10', 1, 10))
    ->run();

// 1. Reading Choice
$sentiment = $response->value('sentiment'); // "negative"
$sentimentConfidence = $response->confidence('sentiment'); // 0.94
$allSentimentProbs = $response->probabilities('sentiment');
// ['positive' => 0.02, 'neutral' => 0.04, 'negative' => 0.94]

// 2. Reading Noul
if ($response->isPositive('churn_risk', threshold: 0.70)) {
    Notification::route('slack', '#customer-success')
        ->notify(new ChurnAlert($customer, $response->value('churn_risk')));
}

// 3. Reading Score
$score = $response->value('satisfaction_score'); // 2.3

Real-World Case Study: Automated RMA Authorization

Here is a full example illustrating an automated warranty and return evaluation in an e-commerce store:

flowchart TD
    RMA["ReturnRequest (RMA)<br/>Product info, delivery age, customer return history"] --> Context["Construct State Array Context"]
    Context --> Engine["SystemOne Multi-Question Request<br/>1. eligible_policy (Noul)<br/>2. decision (Choice: auto_approve | manual_review | auto_reject)<br/>3. fraud_score (Score: 1-100)"]
    Engine --> Run["-&gt;run() Edge Evaluation"]
    Run --> Check{"eligible_policy &gt; 0.5 &amp;&amp;<br/>decision == 'auto_approve'?"}
    Check -- Yes --> Approve["rma-&gt;approve()<br/>Generate Prepaid Return Shipping Label"]
    Check -- No --> Review["rma-&gt;escalateToAgent()<br/>Route to Human Specialist with Decision &amp; Fraud Score"]
namespace App\Services;

use App\Models\ReturnRequest;
use Obelaw\SystemOne\Facades\SystemOne;
use Obelaw\SystemOne\Primitives\Choice;
use Obelaw\SystemOne\Primitives\Noul;
use Obelaw\SystemOne\Primitives\Score;

class ReturnAuthorizationService
{
    public function evaluate(ReturnRequest $rma): void
    {
        $context = [
            'item_title' => $rma->orderItem->product->title,
            'days_since_delivery' => $rma->order->delivered_at->diffInDays(now()),
            'customer_return_count_past_year' => $rma->customer->returns()->where('created_at', '>=', now()->subYear())->count(),
            'customer_reason' => $rma->reason_description,
            'image_labels' => $rma->photo_analysis_labels, // from vision analyzer
        ];

        $decision = SystemOne::state($context)
            ->ask('eligible_policy', Noul::make('Does this item qualify within standard 30-day return policy?'))
            ->ask('decision', Choice::make('RMA disposition action', [
                'auto_approve' => 'Approve instantly and issue return shipping label',
                'manual_review' => 'Flag for warehouse technician manual review',
                'auto_reject' => 'Decline request based on policy violation',
            ]))
            ->ask('fraud_score', Score::range('Abuse and warranty fraud risk 1-100', 1, 100))
            ->run();

        $rma->update([
            'ai_evaluation' => $decision->toArray(),
            'fraud_risk_score' => $decision->value('fraud_score'),
        ]);

        if ($decision->isPositive('eligible_policy') && $decision->value('decision') === 'auto_approve') {
            $rma->approve();
            $rma->generatePrepaidLabel();
        } else {
            $rma->escalateToAgent(reason: "SystemOne Disposition: {$decision->value('decision')}");
        }
    }
}

Next Steps

Our Premium Sponsors

Obelaw is proudly open-source. Continued development, bug fixes, and community support are made possible by the generosity of our sponsors.

Sponsor Obelaw