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

Laravel Ai Bridge Laravel Package

tetrixdev/laravel-ai-bridge

laravel-ai-bridge is a lightweight Laravel package for connecting your application to AI services through a simple, unified bridge. It helps you organize AI provider configuration and keep prompt/response integrations clean and maintainable.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install and wire up the bridge. Require the package via Composer, publish its config, and point it at your ai-bridge instance. Credentials depend on mode: managed mode uses the package's configured key, BYOK passes the user's own key through per request. Get the config correct first — almost every "why is this empty" issue traces back to mode/credentials.
  2. Sort out CSRF before you build any custom UI. As of v0.21.0 the browser-facing state-changing routes (POST /ai-bridge/connections/{id}/mcp, POST/DELETE .../upstreams/{server}/sign-in) require a valid CSRF token whenever the request is authenticated by session or cookie. The bundled <x-ai-bridge::chat /> sends it for you; a hand-rolled fetch/axios call needs X-CSRF-TOKEN, _token, or X-XSRF-TOKEN. Bearer/Sanctum token requests are exempt.
  3. Look here first: AGENTS.md and the docs map (following Engram documentation standard v1). The architecture page explains the conversation → turn → bridge request flow, and the decision records explain why things like per-turn model resolution and the new CSRF policy (Decision 0009) work the way they do. Read those before reading source.
  4. Probe the connection before building UI. Hit the connection status route and inspect effort_levels. Before ai-bridge 0.32.0 this is an empty array, so treat it as the capability manifest for the thinking-level selector rather than hardcoding options.
  5. First use case: create a conversation, send a single turn with an explicit model and (for Claude) an effort, and echo back the assistant message. That one request exercises model resolution, option serialisation (ai_request.options.effort), and validation — the three things most likely to bite you. Then exercise an MCP route with a tool_call_id to confirm your CSRF wiring works.

Implementation Patterns

  • Conversation-scoped defaults + per-turn overrides. Store the model on the conversation when it's created, and pass model on the turn only when the user explicitly changes it. As of v0.20.0 the per-turn value wins over the stored one, so you can implement a "this message only" model switcher without mutating the conversation, or persist the change separately if you want it to stick.
  • Effort as an opt-in, Claude-only knob. Effort belongs to Claude conversations. Send it inside the turn payload so it lands as ai_request.options.effort. Build the selector from effort_levels on the connection status response, and omit the key entirely rather than sending null or '' when the user hasn't picked one.
  • Always relay tool_call_id on MCP calls. Since v0.21.0 the mcp route rejects requests without it (422 tool_call_id_required). Treat it as mandatory in your browser/client code: thread the id of the card/message that triggered the tool through to the request. The PHP API still leaves it optional for trusted server-side code, so internal callers don't need to change, but any request originating from the UI does.
  • Ship the CSRF token with every state-changing bridge call. Use @csrf/X-XSRF-TOKEN in your layout, or keep using the bundled chat component which handles it. If you're on Inertia/axios, the default X-XSRF-TOKEN header already satisfies the check — just confirm the cookie is being issued before the request.
  • Opt in early to ai-bridge.csrf.routes. Setting AI_BRIDGE_CSRF_ROUTES=true extends the same protection to the package's other state-changing browser routes; it becomes the default in the next major, so enable it now in staging to flush out any non-token clients you've forgotten about.
  • Validate before dispatch. The conversation route enforces ^[a-z]{1,16} and returns 422 otherwise. Mirror that rule in a FormRequest so your users get a field-level error instead of a raw 422 from the bridge call; keep the two in sync if you expose custom effort labels.
  • Capability-gated UI. Wrap the effort control in a check on effort_levels: empty means the bridge is older than 0.32.0 (or the conversation isn't Claude), so hide the control rather than rendering a dead dropdown.
  • Keep BYOK and managed paths explicit. Effort is never sent for BYOK or managed mode, so don't build logic that assumes the option round-trips. If your UI shows effort and the account is BYOK, label it as unavailable rather than silently dropping it.
  • Test against a real bridge version. Feature-test the turn endpoint with Http::fake() for the happy path, but keep one integration test pinned to a known ai-bridge version so effort-level and model-precedence regressions surface before deploy. Add a CSRF test (session-authenticated request without a token → 419) and a bearer-token test (no token → passes).

Gotchas and Tips

  • 419 csrf_token_mismatch is the new failure mode. It appears on the MCP/upstream routes for session- or cookie-authenticated requests only. Note the check deliberately ignores your app's allowSameSite() setting and CSRF except list, and it also fires when a user is signed in by cookie without a started session — so a route you excluded elsewhere is still protected here. Only genuinely token-authenticated requests (bearer, Sanctum personal access tokens) skip it. Don't "fix" it by disabling CSRF globally; fix the client.
  • Disabling is a last resort. ai-bridge.csrf.upstreams / AI_BRIDGE_CSRF_UPSTREAMS=false turns the check off — only acceptable if your own middleware already guards those routes. Anything else reopens the same-site subdomain attack the release closes.
  • Breaking — tool_call_id is now required on the mcp route. Any client, queued job, or test that fabricated an MCP call without it now gets a 422 tool_call_id_required. Grep for the route before upgrading. Trusted server-side PHP callers are unaffected (parameter stays optional in the API).
  • Behaviour change — per-turn model now wins. Any code that assumed the conversation's stored model always took effect will change behaviour on upgrade. Audit spots where you pass a model on a turn "just in case" (defaults, job payloads, retries) — those now override the conversation and can silently switch models mid-thread.
  • Effort is silently inert on old bridges. Without ai-bridge 0.32.0 the option is accepted but does nothing, and effort_levels comes back empty. Don't treat "no error" as "it applied"; verify by inspecting the outbound ai_request.options.effort in logs.
  • 422s are validation, not failures. A rejected effort means the string didn't match ^[a-z]{1,16} — lowercase letters only, no digits, hyphens, or underscores. Log the offending value; the route error alone won't tell you which turn caused it.
  • Claude-only, mode-gated. Effort sent on a non-Claude conversation or under BYOK/managed is a no-op. Don't branch your whole UX on it being present.
  • Version-floor check. The new CSRF middleware requires a supported laravel/framework; if you're pinned to an older Laravel, the routes won't behave as documented. Confirm your framework version before enabling csrf.routes.
  • Debugging order: (1) mode and credentials in config, (2) ai-bridge version and effort_levels, (3) whether the request is session- or token-authenticated (CSRF applies to the former), (4) the actual request payload — confirm model, effort, and tool_call_id sit where you expect before blaming the bridge.
  • Extension point: the decision records are the right place to check before extending model resolution or option handling — Decision 0009 documents the CSRF scope and exemptions, and the earlier records document model/option precedence so your override doesn't fight the package's.
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.
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
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core