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

Cerebrum Laravel Package

yab/cerebrum

Cerebrum is a Laravel package for building and managing AI agents and assistants, with tool calling, prompt handling, and model integrations. It gives PHP developers a structured way to wire LLM-powered features into their applications without leaving the Laravel ecosystem.

View on GitHub
Deep Wiki
Context7

Getting Started

What you're actually installing. Read the source (src/) before running composer require. The package is archived, so your first job is not "how do I use it" but "does it resolve at all." Open composer.json first and check the php and illuminate/support constraints against your app. On a modern Laravel/PHP baseline expect a conflict; install it in a throwaway branch with --ignore-platform-reqs only to read and probe, never to ship.

Minimum steps for a local spike

  1. composer require yab/cerebrum in a scratch branch; if it fails, clone the repo and add it as a path repository so you can keep going.
  2. php artisan vendor:publish --provider="..." — the provider class name is in composer.json's extra.laravel.providers if auto-discovery is configured, otherwise look in src/ for the class extending ServiceProvider.
  3. Fill in the published config (config/cerebrum.php or similar) via .env keys. Identify the driver/credential keys and the default driver.
  4. On Lumen, register the provider and (if present) the facade alias manually in bootstrap/app.php — auto-discovery almost certainly predates the Lumen setup you're on.
  5. Verify with php artisan tinker and a single facade call.

Where to look first, in order

  • The service provider — tells you the container binding key, whether it's a singleton, and what it resolves from config. That binding is your mocking seam and your exit hatch.
  • The contracts/interface directory, if any — this determines whether the vendor client is abstracted or leaked into call sites. This is the single most important file in the repo for your decision.
  • The config file — driver list, credential keys, timeout settings (if any).
  • The driver implementations — what HTTP client, what error handling, what the return type actually is on failure.

Your first use case. Pick something with zero blast radius: an admin back-office action, a one-off internal report generator, or a hackathon demo. Call the facade from a controller, observe the response shape, and immediately wrap the call in a class of your own (see below). Do not put a facade call in a Blade view or a model accessor "just to try it" — that's the pattern that survives into production by accident.

Implementation Patterns

Always wrap it behind your own interface. The package's facade is designed for convenience, which means it invites Cerebrum::something(...) scattered across controllers. That is the anti-pattern. Define a one-method contract in your namespace (app/Contracts/...) and a concrete adapter that holds the package call:

interface TextAnalyzer {
    public function analyze(string $input, array $context = []): AnalysisResult;
}

Bind it once in your own service provider. Every call site depends on TextAnalyzer, never on the facade. When the package or its vendor dies — and given archived status, plan for it — you rewrite one class.

Constructor injection over the facade. The facade is fine for tinker; in application code inject the bound contract. This also makes your tests fake your interface rather than the package's, which insulates your suite from the package's return quirks.

Normalize failure at the boundary. Thin vendor wrappers commonly return null, false, or an empty array on upstream error rather than throwing. Do not propagate that. In your adapter, check the result shape, and on anything unexpected throw a domain exception (AnalysisFailed) you control. You want the choice between "fail loudly" and "degrade deliberately" to be yours, not the package's.

Keep prompts and context out of call sites. The package advertises centralized prompts. Use that, but keep the authoritative copies in your own config or a dedicated prompts class so they survive the removal of the package. Pass context through your adapter's $context argument and let the adapter shape it into whatever the vendor expects.

Queue anything on a request path. These calls are almost certainly synchronous HTTP. Put them in a job with $tries, backoff, and a timeout. Add a rate limiter if you'll exceed a handful per minute. On the request path, gate the feature behind a flag and render without the AI output if the job hasn't completed.

Cache idempotent inputs. Same text in, same analysis out — memoize in the cache with a hash key. This is the cheapest cost and latency control you'll get, and the package won't do it for you.

Testing pattern. Two layers:

  • Fake your contract in feature tests. That's the whole point of the wrapper.
  • Test the adapter itself against a mocked HTTP layer (Http::fake() if the driver uses Laravel's client, or a Guzzle MockHandler otherwise). Do not write tests that hit the live vendor; there's no maintainer to tell you when that breaks.

If the package ships no HTTP-layer tests of its own (likely), assume untested error paths and write contract tests covering: happy path, timeout, non-2xx, malformed body, and missing credentials.

Driver swapping. The pluggable-driver design is the package's best idea. Encode it in your own config (services.analysis.driver) and use it to run a fake driver in CI and a real one in staging. That gives you provider portability the package itself can't guarantee, because you own the resolution logic.

Forking as a pattern. If the spike justifies going further, fork rather than depend: bump constraints, swap in a maintained HTTP client, add explicit timeouts, add typed exceptions, and publish to a private registry pinned to an exact version. Budget for owning it permanently — the failure mode for an unowned fork is that it works, nobody touches it, and a Laravel major breaks it at the worst moment.

Gotchas and Tips

Dependency resolution is the first wall. Archived packages carry stale constraints. If composer require fails, don't reach for --ignore-platform-reqs in your real composer.json — that's a repo-wide footgun. Use a path repository or a fork with edited constraints, and keep it out of the main dependency graph.

Auto-discovery may not be wired up. Older packages targeting Lumen-era Laravel often predate extra.laravel.providers. If publishing doesn't work and the binding isn't there, register the provider manually. Check composer.json extra before assuming anything.

env() outside config files breaks under config:cache. A classic in older packages: calling env() in a service provider or driver constructor works in dev and returns null in production after php artisan config:cache. Grep the source for env( — if you find it outside config/, that's a bug you must patch or work around.

Facade alias collisions. If you alias the facade in config/app.php, verify it doesn't clash with an existing alias. A silent collision produces a Class not found or, worse, resolves the wrong binding.

Cached config after deploy. Config-driven packages fail confusingly when config:cache runs against stale values. Add a deploy-time check or a health endpoint that asserts the driver and credentials are present.

Silent degradation is the real hazard. Instrument error rate and latency from day one. If the wrapper swallows errors, your product will make decisions on default values with no signal. Log the raw failure and the normalized exception.

Credential rotation surfaces as an opaque error. Distinguish auth failures from timeouts from rate limits in your adapter's exception mapping. An expired key that looks like a transient failure will burn hours.

Check whether it's a singleton or a fresh instance. If the binding is a singleton holding a client, state (headers, last response) can leak between calls in long-running workers (Octane, queue workers). Verify; if it's stateful, resolve per-call or rebuild in handle().

Extension points to lean on. The contracts, if they exist, are your real integration surface: implement the package's interface with your own driver to reuse its resolution and facade ergonomics while owning the vendor call. If no contracts exist and the vendor client is referenced directly at call sites, stop — that's your signal to harvest the idea and reimplement it, because there's nothing to swap.

License vs. vendor terms. MIT covers the code; it does not cover the upstream API. Confirm data-handling, retention, and ToS before any text leaves your app, and check with security if the input can contain PII.

Pin exactly, and write the README you wish existed. No maintainer means no issue queue. At adoption time — not later — record: which vendor and contract, how credentials rotate, what happens on failure, and why this fork exists. That document, not the code, is the onboarding cost.

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.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource