Custom Drivers & Extension
v1.0.0Extend Obelaw SystemOne with custom decision drivers for local LLMs, OpenAI, Anthropic, or on-premise inference engines.
Custom Drivers & Extension
Obelaw SystemOne is engineered from the ground up to be driver-agnostic. While obelaw/systemone-clef provides turnkey edge inference via Cloudflare Workers AI, you can easily integrate any AI provider, local inference runtime (e.g. Ollama, vLLM), or custom rule engine.
The Driver Contract
Every SystemOne driver implements Obelaw\SystemOne\Contracts\DriverInterface:
flowchart TD
App["Laravel Application Code"] --> Facade["SystemOne Facade"]
Facade --> Manager["SystemOneManager"]
Manager -->|"driver('clef')"| Clef["ClefDriver (Official)<br/>Cloudflare Workers AI"]
Manager -->|"driver('ollama')"| Ollama["OllamaDriver (Custom)<br/>Local Daemon :11434"]
Manager -->|"driver('vllm')"| VLLM["vLLM / Custom Driver<br/>On-Premise GPU Cluster"]
subgraph DriverContract["DriverInterface Contract"]
direction TB
M1["decide(DecisionRequest): DecisionResponseInterface"]
M2["run(DecisionRequest): DecisionResponseInterface"]
end
Clef -.-> DriverContract
Ollama -.-> DriverContract
VLLM -.-> DriverContract
namespace Obelaw\SystemOne\Contracts;
use Obelaw\SystemOne\DecisionRequest;
interface DriverInterface
{
/**
* Execute the decision request and return a response.
*/
public function decide(DecisionRequest $request): DecisionResponseInterface;
/**
* Alias for decide().
*/
public function run(DecisionRequest $request): DecisionResponseInterface;
}
Registering Custom Drivers
You can register custom drivers using two convenient approaches:
Approach A: Using SystemOne::extend()
Ideal for registering custom drivers in your AppServiceProvider or dedicated package service provider:
namespace App\Providers;
use App\Services\SystemOne\OllamaDriver;
use Illuminate\Support\ServiceProvider;
use Obelaw\SystemOne\Facades\SystemOne;
use Obelaw\SystemOne\SystemOneManager;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
$this->app->resolving('systemone', function (SystemOneManager $manager, $app) {
$manager->extend('ollama', function ($app) {
return new OllamaDriver(
baseUrl: config('services.ollama.url', 'http://127.0.0.1:11434'),
model: config('services.ollama.model', 'llama3')
);
});
});
}
}
Approach B: Service Container Binding
The SystemOneManager will automatically look for container bindings with the prefix systemone.driver.{name}:
$this->app->singleton('systemone.driver.ollama', function ($app) {
return new OllamaDriver();
});
Step-by-Step: Building an Ollama Driver
Here is a complete, working example of an on-premise driver connecting SystemOne to a local Ollama daemon:
flowchart LR
Req["DecisionRequest<br/>(State + Primitives)"] --> Payload["Extract payload & targetModel<br/>Enforce strict JSON schema in prompt"]
Payload --> HTTP["HTTP POST /api/generate<br/>format: 'json', stream: false"]
HTTP --> Daemon["Ollama Local Daemon<br/>(e.g. llama3.2:3b)"]
Daemon --> Res["Raw Response JSON<br/>answers: { ... }"]
Res --> Resp["new DecisionResponse(answers, raw)"]
namespace App\Services\SystemOne;
use Illuminate\Support\Facades\Http;
use InvalidArgumentException;
use Obelaw\SystemOne\Contracts\DecisionResponseInterface;
use Obelaw\SystemOne\Contracts\DriverInterface;
use Obelaw\SystemOne\DecisionRequest;
use Obelaw\SystemOne\DecisionResponse;
class OllamaDriver implements DriverInterface
{
public function __construct(
protected string $baseUrl = 'http://127.0.0.1:11434',
protected string $model = 'llama3.2:3b'
) {
}
public function decide(DecisionRequest $request): DecisionResponseInterface
{
$payload = $request->toPayload();
$targetModel = $request->getModel() ?? $this->model;
// Build prompt enforcing strict JSON output
$systemPrompt = "You are a deterministic decision engine. "
. "Evaluate the given state and answer each registered question strictly matching the requested primitive type. "
. "Output JSON with key 'answers' mapping each question key to: "
. "{ value: <float|string|number>, confidence: <float 0-1>, probabilities?: <object> }.";
$httpResponse = Http::timeout(60)
->post("{$this->baseUrl}/api/generate", [
'model' => $targetModel,
'system' => $systemPrompt,
'prompt' => json_encode($payload, JSON_PRETTY_PRINT),
'stream' => false,
'format' => 'json',
]);
if ($httpResponse->failed()) {
throw new InvalidArgumentException("Ollama request failed: " . $httpResponse->body());
}
$decoded = json_decode($httpResponse->json('response', '{}'), true);
$answers = $decoded['answers'] ?? [];
return new DecisionResponse($answers, $httpResponse->json());
}
public function run(DecisionRequest $request): DecisionResponseInterface
{
return $this->decide($request);
}
}
Using Your Custom Driver
Once registered, you can invoke your driver by name:
use Obelaw\SystemOne\Facades\SystemOne;
use Obelaw\SystemOne\Primitives\Noul;
$response = SystemOne::state('Disk space utilization is 96% on primary database node.')
->driver('ollama')
->ask('escalate', Noul::make('Should an on-call engineer be paged immediately?'))
->run();
if ($response->isPositive('escalate')) {
PagerDuty::triggerIncident();
}
Or pass a direct driver instance directly into run():
$driverInstance = new OllamaDriver(baseUrl: 'http://internal-ai.lan:11434');
$response = SystemOne::state($serverState)
->ask('is_healthy', Noul::make('Is database operational?'))
->run($driverInstance);
Next Steps
- Test your custom driver offline using Testing & Mocking.
- Review all methods of the Fluent API & Builder.