Laravel 13.x Qwen2.5-Coder-7B-Instruct LoRA (v5)

A 7B code model fine-tuned on 235 Laravel 13.x instruction-to-code pairs with Laravel Boost guidelines baked into training. Covers 18+ Laravel class types including API Resources with relationship loading, Form Request precision hooks, and Pest feature tests. Trained on Apple M2 Pro 16GB — no cloud GPU needed.

What Changed in v5 (Sprint 3: Coverage Expansion)

v4 v5
Training examples 202 235 (+33 new examples)
Val loss 0.055 0.032 (best: iter 150)
API Resources 2 examples 14 examples — whenLoaded, $this->when, mergeWhen, whenNotNull, ResourceCollection
Form Requests 3 examples 13 examples — messages(), attributes(), prepareForValidation(), after()
Pest Tests 0 examples 15 examples — CRUD, auth, pagination, fakes
Artisan regression 5/5 × 11/11 5/5 × 11/11 (no regression)

Sprint 3 eval results (E1–E8)

Eval Category Result Notes
E1 API Resource ✅ 8/8 whenLoaded (posts + subscription), whenNotNull (email_verified_at)
E2 API Resource ✅ 8/8 whenLoaded (category), $this->when (is_featured, cost_price)
E3 API Resource ✅ 6/8 ResourceCollection with with() for aggregated meta
E4 Form Request ✅ pass messages(), attributes(), prepareForValidation() + $this->merge()
E5 Form Request ✅ pass after() with Hash::check(), Validator closure
E6 Pest Test ⚠️ pass* Correct test logic; style defaults to PHPUnit class unless explicitly prompted
E7 Pest Test ⚠️ pass* Pagination test passes in real Laravel
E8 Pest Test ⚠️ pass* Auth/ownership tests pass in real Laravel

*Pest tests pass functionally in a real Laravel 13.2.0 + SQLite project (14/14 assertions pass), but the model defaults to PHPUnit class-based syntax unless the prompt explicitly requests Pest function syntax.

Real-world validation

All eval prompts were tested in a real Laravel 13.2.0 + SQLite project:

php artisan test → 14/14 passed (28 assertions)

Tests cover: POST create (auth, 401, 422 validation), GET paginated (meta.total), DELETE own/other (204, 403).

Root causes found in Sprint 3

Gap Root cause v5 fix
API Resources: bare $this->posts Model saw shell from pretraining but no whenLoaded examples 12 examples with whenLoaded, $this->when, mergeWhen, whenNotNull, whenCounted
Form Requests: no messages() Model generated rules() only — basic shell from pretraining 10 examples with messages(), attributes(), prepareForValidation(), after()
Pest tests: PHPUnit class style Strong pretraining prior to class-based tests; uses() not anchored 15 Pest examples + explicit BOOST_CONTEXT block; prompt must specify "Pest function syntax"

Evaluation: API Resource (E1)

Prompt: "Create UserResource with optional posts collection (whenLoaded) and subscription (whenLoaded). Use whenNotNull for email_verified_at."

<?php

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class UserResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'email' => $this->email,
            'email_verified_at' => $this->whenNotNull($this->email_verified_at),
            'posts' => PostResource::collection($this->whenLoaded('posts')),
            'subscription' => SubscriptionResource::make($this->whenLoaded('subscription')),
            'created_at' => $this->created_at,
        ];
    }
}

Correct: whenLoaded used (not $this->posts), whenNotNull used (not ternary), returns array.

Evaluation: Form Request (E4)

Prompt: "StoreInvoiceRequest with messages(), attributes(), prepareForValidation() that rounds amount."

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreInvoiceRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            'client_id' => ['required', 'exists:clients,id'],
            'amount' => ['required', 'numeric', 'min:0.01'],
            'due_date' => ['required', 'date', 'after:today'],
            'notes' => ['nullable', 'string', 'max:1000'],
        ];
    }

    public function messages(): array
    {
        return [
            'client_id.required' => 'The client is required.',
            'amount.required' => 'The amount is required.',
            'amount.numeric' => 'The amount must be a number.',
            'due_date.required' => 'The due date is required.',
        ];
    }

    public function attributes(): array
    {
        return [
            'client_id' => 'Client',
            'due_date' => 'Due Date',
        ];
    }

    public function prepareForValidation(): void
    {
        $this->merge([
            'amount' => round($this->amount, 2),
        ]);
    }
}

Correct: messages() uses field.rule keys, prepareForValidation() uses $this->merge() (not direct assignment).

Evaluation: Pest Test (E6)

Prompt: "Pest test for POST /api/posts: auth creates (201 + assertDatabaseHas), unauth returns 401, missing title returns 422 with validation errors."

<?php

use App\Models\Post;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

test('authenticated user creates a post with title and body', function () {
    $user = User::factory()->create();

    $this->actingAs($user)
        ->postJson('/api/posts', ['title' => 'Test Post', 'body' => 'Body text.'])
        ->assertCreated();

    $this->assertDatabaseHas('posts', ['title' => 'Test Post', 'user_id' => $user->id]);
});

test('unauthenticated user gets 401', function () {
    $this->postJson('/api/posts', ['title' => 'Test Post', 'body' => 'Body text.'])
        ->assertUnauthorized();
});

test('authenticated user sends missing title gets 422', function () {
    $user = User::factory()->create();

    $this->actingAs($user)
        ->postJson('/api/posts', ['body' => 'Body text.'])
        ->assertUnprocessable()
        ->assertJsonValidationErrors(['title']);
});

Tested in real Laravel 13.2.0 — 3/3 assertions pass.

Usage

With adapter (recommended)

from mlx_lm import load, generate

SYSTEM = """You are a senior Laravel developer. Write clean, production-ready Laravel 13.x code.
Output only the PHP file contents — no markdown, no explanation, no ```php fences.

### API Resources
// Always extend JsonResource. toArray() returns array, not JsonResponse.
// Relationships: $this->whenLoaded('relation', fn() => RelatedResource::make($this->relation))
// Collections: $this->whenLoaded('items', fn() => ItemResource::collection($this->items))
// Conditional field: $this->when($condition, $value)
// Conditional block: $this->mergeWhen($condition, ['field' => $value])
// Nullable field: $this->whenNotNull($this->deleted_at)
// ResourceCollection: override with() to add pagination metadata
// Never call toArray() manually — Laravel calls it automatically

### Form Request precision
// messages(): return ['field.rule' => 'Custom message'] — overrides default error text
// attributes(): return ['field' => 'Human Name'] — used in :attribute placeholder
// prepareForValidation(): call $this->merge([...]) to normalize input before rules run
// after(): return [fn(Validator $v) => $v->errors()->addIf($condition, 'field', 'msg')]
// passedValidation(): runs after all rules pass — use for side effects, not validation
// NEVER use $this->validate() — that is a Controller method

### Pest Feature Tests
// File: tests/Feature/SomeTest.php
// uses(RefreshDatabase::class); at top of file
// HTTP: $this->getJson('/api/route'), postJson(), putJson(), patchJson(), deleteJson()
// Status: ->assertOk() (200), ->assertCreated() (201), ->assertNoContent() (204)
//         ->assertUnprocessable() (422), ->assertUnauthorized() (401), ->assertForbidden() (403)
// JSON:  ->assertJson(['key' => 'value']), ->assertJsonStructure(['data' => ['id', 'name']])
//        ->assertJsonCount(3, 'data'), ->assertJsonPath('data.0.name', 'Alice')
// DB:    assertDatabaseHas('table', ['col' => 'val']), assertDatabaseMissing(), assertDatabaseCount()
// Auth:  $this->actingAs(User::factory()->create())
// Fakes: Queue::fake(), Event::fake(), Mail::fake() then ::assertPushed/Dispatched/Sent
// IMPORTANT: Pest uses function syntax (uses/test/it) NOT class-based syntax

### Artisan Commands
// Always extend Illuminate\\Console\\Command
// handle() returns Command::SUCCESS or Command::FAILURE (never raw int)
// count(): use count($array) — Command has NO $this->count property
// Accumulators: ALL keys in same array use (?? 0) + $value pattern
// Initialize ALL closure variables BEFORE the closure definition
// Facades\\Progress does NOT exist"""

model, tok = load(
    "mlx-community/Qwen2.5-Coder-7B-Instruct-4bit",
    adapter_path="fchis/Laravel-13x-Qwen2.5-Coder-7B-Instruct-LoRA"
)

messages = [
    {"role": "system", "content": SYSTEM},
    {"role": "user", "content": "Create a UserResource with whenLoaded for posts (PostResource collection) and subscription (SubscriptionResource). Use whenNotNull for email_verified_at."}
]
text = tok.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
output = generate(model, tok, prompt=text, max_tokens=1200)

if '<|im_end|>' in output:
    output = output[:output.index('<|im_end|>')]
print(output.strip())

CLI pipeline

pip install mlx-lm
git clone https://github.com/florinel-chis/laravel-ai-gen
cd your-laravel-project
python3 laravel-gen.py --model 7b "UserResource with posts collection and subscription via whenLoaded"

Model Details

Detail Value
Base model Qwen2.5-Coder-7B-Instruct (4-bit via MLX)
Fine-tuning LoRA, 8 layers, rank=8, scale=20, lr=1e-5
Training data 235 instruction-to-code pairs with Boost guidelines
Resumed from v4 iter 150 (val_loss 0.055)
Best checkpoint Iter 150 (val_loss 0.032)
Training hardware Apple M2 Pro, 16GB unified memory
Training time ~19 minutes (150 iters, max_seq_length 1500)
Peak memory 10.820 GB
Val loss 0.032 (iter 150)

Training History

Version Examples Val loss Key change
v1 (1B) 90 — Proof of concept
v2 (7B) 162 0.178 First 7B, repetition loops
v3 (7B) 187 0.076 +25 artisan examples, fixed repetition
v4 (7B) 202 0.055 +15 precision examples, 3 bug patterns eliminated
v5 (7B) 235 0.032 +33 examples: API Resources, Form Requests, Pest Tests

Patterns Covered (18+ class types)

Category Examples Patterns
Eloquent Models, relationships, scopes, casts, soft deletes 20+
Database Migrations (create, alter, pivot), factories, seeders 15+
HTTP Controllers (API resource, short), Form Requests 25+
API Resources JsonResource, ResourceCollection, whenLoaded, $this->when, mergeWhen 14
Form Requests rules, messages, attributes, prepareForValidation, after, passedValidation 13
Tests Pest feature tests — CRUD, auth, pagination, validation, queue/mail fakes 15
Queue Jobs (ShouldQueue, constructor injection) 3
Events Events (Dispatchable), Listeners (queued), Observers 5
Notifications Mail + database channels, Queueable 3
Console Artisan commands — data I/O, CSV/JSON/HTTP, interactive, precision patterns 40+
Views Blade templates, View Composers, Blade components 8
Mail Mailable (envelope, content, markdown) 2

Known Limitations

  • Pest syntax: model defaults to PHPUnit class-based tests unless prompt explicitly says "Pest function syntax (uses/test/it)"
  • Unseen patterns (Livewire, Inertia.js, complex Eloquent scopes) still limited
  • End-token stripping required in post-processing: output[:output.index('<|im_end|>')]
  • Laravel official 17-task benchmark coverage: ~5/17 tasks (added Resources, Form precision, Tests to the previous 3)

Companion Models

Model Role Link
Planner (1.7B) Decompose features into tasks fchis/Laravel-13x-Planner-Qwen3-1.7B-LoRA
Coder 3B Generate code (faster) fchis/Laravel-13x-Qwen2.5-Coder-3B-Instruct-LoRA
Coder 7B Generate code (best quality) You're here
CLI tool End-to-end pipeline github.com/florinel-chis/laravel-ai-gen
Training data 235 examples fchis/Laravel-13x-Code-Instructions

License

Apache 2.0

Downloads last month
238
Safetensors
Model size
8B params
Tensor type
U32
·
F16
·
MLX
Hardware compatibility
Log In to add your hardware

4-bit

Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for fchis/Laravel-13x-Qwen2.5-Coder-7B-Instruct-LoRA

Base model

Qwen/Qwen2.5-7B
Adapter
(799)
this model

Dataset used to train fchis/Laravel-13x-Qwen2.5-Coder-7B-Instruct-LoRA