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 exampleswhenLoaded, $this->when, mergeWhen, whenNotNull, ResourceCollection
Form Requests 3 examples 13 examplesmessages(), 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
945
Safetensors
Model size
1B params
Tensor type
F16
·
U32
·
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
(571)
this model

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