| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>BuildSpec Pipeline β Data Flow Diagram</title> |
| <style> |
| * { margin: 0; padding: 0; box-sizing: border-box; } |
| |
| body { |
| font-family: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', sans-serif; |
| background: #0f1117; |
| color: #e2e8f0; |
| min-height: 100vh; |
| padding: 40px 20px; |
| } |
| |
| .container { |
| max-width: 1100px; |
| margin: 0 auto; |
| } |
| |
| h1 { |
| text-align: center; |
| font-size: 1.8em; |
| font-weight: 700; |
| color: #f1f5f9; |
| margin-bottom: 8px; |
| letter-spacing: -0.02em; |
| } |
| |
| .subtitle { |
| text-align: center; |
| color: #94a3b8; |
| font-size: 0.95em; |
| margin-bottom: 50px; |
| } |
| |
| |
| |
| .pipeline { |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| gap: 0; |
| } |
| |
| .stage { |
| width: 100%; |
| max-width: 680px; |
| position: relative; |
| } |
| |
| .stage-box { |
| border-radius: 12px; |
| padding: 24px 28px; |
| position: relative; |
| transition: transform 0.2s, box-shadow 0.2s; |
| } |
| |
| .stage-box:hover { |
| transform: translateY(-2px); |
| box-shadow: 0 8px 30px rgba(0,0,0,0.3); |
| } |
| |
| .stage-number { |
| position: absolute; |
| top: -12px; |
| left: 24px; |
| background: #0f1117; |
| padding: 0 8px; |
| font-size: 0.7em; |
| font-weight: 700; |
| text-transform: uppercase; |
| letter-spacing: 0.1em; |
| color: #64748b; |
| } |
| |
| .stage-title { |
| font-size: 1.15em; |
| font-weight: 700; |
| margin-bottom: 6px; |
| display: flex; |
| align-items: center; |
| gap: 10px; |
| } |
| |
| .stage-desc { |
| font-size: 0.85em; |
| color: #94a3b8; |
| line-height: 1.5; |
| } |
| |
| .stage-meta { |
| display: flex; |
| gap: 16px; |
| margin-top: 10px; |
| flex-wrap: wrap; |
| } |
| |
| .meta-tag { |
| font-size: 0.75em; |
| padding: 3px 10px; |
| border-radius: 20px; |
| font-weight: 600; |
| } |
| |
| |
| |
| .s-input .stage-box { |
| background: linear-gradient(135deg, #1e293b, #1a2332); |
| border: 1px solid #334155; |
| } |
| .s-input .stage-title { color: #e2e8f0; } |
| .s-input .meta-tag { background: #334155; color: #94a3b8; } |
| |
| .s-planner .stage-box { |
| background: linear-gradient(135deg, #172554, #1e3a5f); |
| border: 1px solid #1d4ed8; |
| } |
| .s-planner .stage-title { color: #93c5fd; } |
| .s-planner .stage-number { color: #3b82f6; } |
| .s-planner .meta-tag { background: #1e3a8a; color: #93c5fd; } |
| |
| .s-compiler .stage-box { |
| background: linear-gradient(135deg, #3b1764, #4c1d95); |
| border: 1px solid #7c3aed; |
| } |
| .s-compiler .stage-title { color: #c4b5fd; } |
| .s-compiler .stage-number { color: #8b5cf6; } |
| .s-compiler .meta-tag { background: #5b21b6; color: #c4b5fd; } |
| |
| .s-generator .stage-box { |
| background: linear-gradient(135deg, #064e3b, #065f46); |
| border: 1px solid #10b981; |
| } |
| .s-generator .stage-title { color: #6ee7b7; } |
| .s-generator .stage-number { color: #34d399; } |
| .s-generator .meta-tag { background: #047857; color: #6ee7b7; } |
| |
| .s-check .stage-box { |
| background: linear-gradient(135deg, #422006, #713f12); |
| border: 1px solid #f59e0b; |
| } |
| .s-check .stage-title { color: #fcd34d; } |
| .s-check .stage-number { color: #fbbf24; } |
| .s-check .meta-tag { background: #92400e; color: #fcd34d; } |
| |
| .s-output .stage-box { |
| background: linear-gradient(135deg, #1c1917, #292524); |
| border: 1px solid #57534e; |
| } |
| .s-output .stage-title { color: #e7e5e4; } |
| .s-output .meta-tag { background: #44403c; color: #a8a29e; } |
| |
| |
| |
| .arrow { |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| padding: 6px 0; |
| position: relative; |
| } |
| |
| .arrow-line { |
| width: 2px; |
| height: 28px; |
| background: linear-gradient(to bottom, #475569, #64748b); |
| } |
| |
| .arrow-head { |
| width: 0; |
| height: 0; |
| border-left: 7px solid transparent; |
| border-right: 7px solid transparent; |
| border-top: 8px solid #64748b; |
| } |
| |
| .arrow-label { |
| position: absolute; |
| right: calc(50% - 340px); |
| top: 50%; |
| transform: translateY(-50%); |
| font-size: 0.72em; |
| color: #64748b; |
| font-weight: 600; |
| text-align: right; |
| white-space: nowrap; |
| } |
| |
| @media (max-width: 800px) { |
| .arrow-label { |
| position: static; |
| transform: none; |
| text-align: center; |
| margin-top: 4px; |
| } |
| } |
| |
| |
| |
| .data-sample { |
| margin-top: 12px; |
| background: rgba(0,0,0,0.3); |
| border-radius: 8px; |
| padding: 12px 16px; |
| font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace; |
| font-size: 0.78em; |
| line-height: 1.5; |
| color: #cbd5e1; |
| overflow-x: auto; |
| border: 1px solid rgba(255,255,255,0.06); |
| } |
| |
| .data-sample .key { color: #93c5fd; } |
| .data-sample .str { color: #86efac; } |
| .data-sample .num { color: #fcd34d; } |
| .data-sample .bool { color: #f9a8d4; } |
| .data-sample .comment { color: #64748b; font-style: italic; } |
| .data-sample .php-tag { color: #c4b5fd; } |
| .data-sample .php-kw { color: #93c5fd; } |
| .data-sample .php-str { color: #86efac; } |
| .data-sample .php-cls { color: #fcd34d; } |
| .data-sample .err { color: #fca5a5; } |
| |
| |
| |
| .error-branch { |
| display: flex; |
| align-items: center; |
| gap: 12px; |
| margin-top: 12px; |
| } |
| |
| .error-box { |
| background: rgba(239, 68, 68, 0.1); |
| border: 1px solid #dc2626; |
| border-radius: 8px; |
| padding: 10px 16px; |
| font-size: 0.8em; |
| color: #fca5a5; |
| flex: 1; |
| } |
| |
| .error-arrow { |
| color: #dc2626; |
| font-size: 1.2em; |
| font-weight: bold; |
| } |
| |
| .fix-box { |
| background: rgba(16, 185, 129, 0.1); |
| border: 1px solid #10b981; |
| border-radius: 8px; |
| padding: 10px 16px; |
| font-size: 0.8em; |
| color: #6ee7b7; |
| flex: 1; |
| } |
| |
| |
| |
| .ontology-section { |
| max-width: 900px; |
| margin: 60px auto 0; |
| } |
| |
| .ontology-section h2 { |
| text-align: center; |
| font-size: 1.3em; |
| color: #f1f5f9; |
| margin-bottom: 24px; |
| } |
| |
| .ontology-compare { |
| display: grid; |
| grid-template-columns: 1fr 60px 1fr; |
| gap: 0; |
| align-items: start; |
| } |
| |
| @media (max-width: 700px) { |
| .ontology-compare { |
| grid-template-columns: 1fr; |
| gap: 16px; |
| } |
| .ontology-vs { display: none; } |
| } |
| |
| .ontology-card { |
| border-radius: 12px; |
| padding: 24px; |
| } |
| |
| .ontology-card h3 { |
| font-size: 1em; |
| margin-bottom: 12px; |
| display: flex; |
| align-items: center; |
| gap: 8px; |
| } |
| |
| .ontology-card ul { |
| list-style: none; |
| padding: 0; |
| } |
| |
| .ontology-card li { |
| font-size: 0.82em; |
| padding: 6px 0; |
| border-bottom: 1px solid rgba(255,255,255,0.05); |
| line-height: 1.4; |
| } |
| |
| .ontology-card li:last-child { border-bottom: none; } |
| |
| .card-model { |
| background: linear-gradient(135deg, #1e1b4b, #312e81); |
| border: 1px solid #4338ca; |
| } |
| .card-model h3 { color: #a5b4fc; } |
| .card-model li { color: #c7d2fe; } |
| |
| .card-dev { |
| background: linear-gradient(135deg, #022c22, #064e3b); |
| border: 1px solid #059669; |
| } |
| .card-dev h3 { color: #6ee7b7; } |
| .card-dev li { color: #a7f3d0; } |
| |
| .ontology-vs { |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| font-size: 1.5em; |
| font-weight: 800; |
| color: #475569; |
| padding-top: 60px; |
| } |
| |
| .gap-label { |
| text-align: center; |
| margin-top: 16px; |
| font-size: 0.85em; |
| font-weight: 700; |
| color: #ef4444; |
| letter-spacing: 0.05em; |
| } |
| |
| .bridge-label { |
| text-align: center; |
| margin-top: 8px; |
| font-size: 0.85em; |
| color: #10b981; |
| font-weight: 600; |
| } |
| |
| |
| |
| .file-grid { |
| display: grid; |
| grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); |
| gap: 8px; |
| margin-top: 12px; |
| } |
| |
| .file-item { |
| display: flex; |
| align-items: center; |
| gap: 8px; |
| font-size: 0.78em; |
| padding: 6px 10px; |
| background: rgba(255,255,255,0.04); |
| border-radius: 6px; |
| font-family: 'SF Mono', 'Fira Code', monospace; |
| color: #94a3b8; |
| } |
| |
| .file-icon { font-size: 1em; } |
| |
| |
| |
| .footer { |
| text-align: center; |
| margin-top: 50px; |
| color: #475569; |
| font-size: 0.8em; |
| } |
| |
| .footer a { color: #6366f1; text-decoration: none; } |
| .footer a:hover { text-decoration: underline; } |
| |
| |
| |
| .stats-bar { |
| display: flex; |
| justify-content: center; |
| gap: 32px; |
| margin: 40px 0 10px; |
| flex-wrap: wrap; |
| } |
| |
| .stat { |
| text-align: center; |
| } |
| |
| .stat-value { |
| font-size: 1.8em; |
| font-weight: 800; |
| background: linear-gradient(135deg, #6366f1, #10b981); |
| -webkit-background-clip: text; |
| -webkit-text-fill-color: transparent; |
| background-clip: text; |
| } |
| |
| .stat-label { |
| font-size: 0.75em; |
| color: #64748b; |
| text-transform: uppercase; |
| letter-spacing: 0.08em; |
| margin-top: 2px; |
| } |
| </style> |
| </head> |
| <body> |
|
|
| <div class="container"> |
|
|
| <h1>BuildSpec Pipeline</h1> |
| <p class="subtitle">Closing the Ontological Gap in Domain-Specific Code Generation</p> |
|
|
| |
| <div class="stats-bar"> |
| <div class="stat"> |
| <div class="stat-value">49</div> |
| <div class="stat-label">Training Examples</div> |
| </div> |
| <div class="stat"> |
| <div class="stat-value">26/26</div> |
| <div class="stat-label">PHP Valid</div> |
| </div> |
| <div class="stat"> |
| <div class="stat-value">20/20</div> |
| <div class="stat-label">Pest Tests Pass</div> |
| </div> |
| <div class="stat"> |
| <div class="stat-value">0</div> |
| <div class="stat-label">Semantic Hallucinations</div> |
| </div> |
| <div class="stat"> |
| <div class="stat-value"><1ms</div> |
| <div class="stat-label">Spec Validation</div> |
| </div> |
| </div> |
|
|
| |
|
|
| <div class="pipeline"> |
|
|
| |
| <div class="stage s-input"> |
| <div class="stage-box"> |
| <div class="stage-title"> |
| <span>💬</span> Natural Language Description |
| </div> |
| <div class="data-sample"> |
| <span class="str">"Create a REST API for a book library. Books belong to authors.<br> |
| Each book has title, isbn (unique), year, pages, status.<br> |
| Books can be filtered by status. Soft deletes on books."</span> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="arrow"> |
| <div class="arrow-line"></div> |
| <div class="arrow-head"></div> |
| <div class="arrow-label">Developer's intent (ambiguous)</div> |
| </div> |
|
|
| |
| <div class="stage s-planner"> |
| <div class="stage-box"> |
| <div class="stage-number">Stage 1</div> |
| <div class="stage-title"> |
| <span>🧠</span> Planner — Ontology Population |
| </div> |
| <div class="stage-desc"> |
| Same Qwen2.5-Coder-7B model, few-shot prompted with 2 in-context examples. |
| Converts NL into structured BuildSpec JSON — populating the domain ontology. |
| </div> |
| <div class="stage-meta"> |
| <span class="meta-tag">Qwen2.5-Coder-7B</span> |
| <span class="meta-tag">Few-shot (2 examples)</span> |
| <span class="meta-tag">max_tokens: 6000</span> |
| </div> |
| <div class="data-sample"> |
| <span class="comment">// Output: BuildSpec JSON array (explicit domain ontology)</span><br> |
| [<br> |
| {<br> |
| <span class="key">"artifact"</span>: <span class="str">"migration"</span>, |
| <span class="key">"table"</span>: <span class="str">"authors"</span>, |
| <span class="key">"columns"</span>: [...]<br> |
| },<br> |
| {<br> |
| <span class="key">"artifact"</span>: <span class="str">"model"</span>, |
| <span class="key">"class"</span>: <span class="str">"Book"</span>,<br> |
| <span class="key">"fillable"</span>: [<span class="str">"title"</span>, <span class="str">"isbn"</span>, <span class="str">"year"</span>],<br> |
| <span class="key">"relationships"</span>: [{<span class="key">"type"</span>: <span class="str">"BelongsTo"</span>, <span class="key">"model"</span>: <span class="str">"Author"</span>}],<br> |
| <span class="key">"has_factory"</span>: <span class="bool">true</span>, |
| <span class="key">"soft_deletes"</span>: <span class="bool">true</span><br> |
| },<br> |
| {<span class="key">"artifact"</span>: <span class="str">"controller"</span>, <span class="key">"class"</span>: <span class="str">"BookController"</span>, ...},<br> |
| {<span class="key">"artifact"</span>: <span class="str">"resource"</span>, <span class="key">"class"</span>: <span class="str">"BookResource"</span>, ...},<br> |
| {<span class="key">"artifact"</span>: <span class="str">"form_request"</span>, <span class="key">"class"</span>: <span class="str">"StoreBookRequest"</span>, ...},<br> |
| ...<br> |
| ] |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="arrow"> |
| <div class="arrow-line"></div> |
| <div class="arrow-head"></div> |
| <div class="arrow-label">BuildSpec JSON (explicit ontology)</div> |
| </div> |
|
|
| |
| <div class="stage s-compiler"> |
| <div class="stage-box"> |
| <div class="stage-number">Stage 2</div> |
| <div class="stage-title"> |
| <span>🛡</span> Spec Compiler — Ontological Reasoning |
| </div> |
| <div class="stage-desc"> |
| Deterministic validation: checks concept completeness, constraint satisfaction, |
| cross-reference integrity. Catches errors before the expensive generation step. |
| </div> |
| <div class="stage-meta"> |
| <span class="meta-tag">Deterministic</span> |
| <span class="meta-tag">< 1ms per spec</span> |
| <span class="meta-tag">spec_compiler.py</span> |
| </div> |
| <div class="error-branch"> |
| <div class="error-box"> |
| <span class="err">✘</span> <code>rules['venue_id']</code> contains <code>'required_on_post'</code>.<br> |
| Use <code>conditional_rules</code> dict instead. |
| </div> |
| <div class="error-arrow">→</div> |
| <div class="fix-box"> |
| <span style="color:#10b981;">✔</span> Fix the spec, not the model.<br> |
| Deterministic fix. Retry in <1ms. |
| </div> |
| </div> |
| <div class="stage-meta" style="margin-top:12px;"> |
| <span class="meta-tag">✓ Required fields</span> |
| <span class="meta-tag">✓ Conditional tokens</span> |
| <span class="meta-tag">✓ Schema validation</span> |
| <span class="meta-tag">✓ Defaults + paths</span> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="arrow"> |
| <div class="arrow-line"></div> |
| <div class="arrow-head"></div> |
| <div class="arrow-label">Validated specs (ontology-verified)</div> |
| </div> |
|
|
| |
| <div class="stage s-generator"> |
| <div class="stage-box"> |
| <div class="stage-number">Stage 3</div> |
| <div class="stage-title"> |
| <span>⚙</span> Code Generator — Ontology-to-Code |
| </div> |
| <div class="stage-desc"> |
| LoRA-adapted model transforms each validated spec into a complete PHP file. |
| The model only decides <em>how</em> to generate — <em>what</em> to generate is fixed by the ontology. |
| </div> |
| <div class="stage-meta"> |
| <span class="meta-tag">Qwen2.5-Coder-7B + LoRA</span> |
| <span class="meta-tag">adapters_spec_v4</span> |
| <span class="meta-tag">~30s per file</span> |
| <span class="meta-tag">49 training examples</span> |
| </div> |
| <div class="data-sample"> |
| <span class="comment">// Input: one BuildSpec β Output: one PHP file</span><br><br> |
| <span class="php-tag"><?php</span><br> |
| <span class="php-kw">namespace</span> <span class="php-cls">App\Models</span>;<br><br> |
| <span class="php-kw">use</span> Illuminate\Database\Eloquent\Factories\<span class="php-cls">HasFactory</span>;<br> |
| <span class="php-kw">use</span> Illuminate\Database\Eloquent\<span class="php-cls">Model</span>;<br> |
| <span class="php-kw">use</span> Illuminate\Database\Eloquent\<span class="php-cls">SoftDeletes</span>;<br> |
| <span class="php-kw">use</span> Illuminate\Database\Eloquent\Relations\<span class="php-cls">BelongsTo</span>;<br><br> |
| <span class="php-kw">class</span> <span class="php-cls">Book</span> <span class="php-kw">extends</span> <span class="php-cls">Model</span><br> |
| {<br> |
| <span class="php-kw">use</span> <span class="php-cls">HasFactory</span>, <span class="php-cls">SoftDeletes</span>;<br><br> |
| <span class="php-kw">protected</span> $fillable = [<span class="php-str">'title'</span>, <span class="php-str">'isbn'</span>, <span class="php-str">'year'</span>, ...];<br><br> |
| <span class="php-kw">public function</span> <span class="php-cls">author</span>(): <span class="php-cls">BelongsTo</span><br> |
| {<br> |
| <span class="php-kw">return</span> $this-><span class="php-cls">belongsTo</span>(<span class="php-cls">Author</span>::class);<br> |
| }<br> |
| } |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="arrow"> |
| <div class="arrow-line"></div> |
| <div class="arrow-head"></div> |
| <div class="arrow-label">Raw PHP files</div> |
| </div> |
|
|
| |
| <div class="stage s-check"> |
| <div class="stage-box"> |
| <div class="stage-number">Stage 4</div> |
| <div class="stage-title"> |
| <span>✅</span> Syntax Validation |
| </div> |
| <div class="stage-desc"> |
| <code>php -l</code> on every generated file. Catches syntax errors before the files |
| reach the Laravel project. |
| </div> |
| <div class="stage-meta"> |
| <span class="meta-tag">php -l</span> |
| <span class="meta-tag">26/26 valid (100%)</span> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="arrow"> |
| <div class="arrow-line"></div> |
| <div class="arrow-head"></div> |
| <div class="arrow-label">Syntax-validated PHP</div> |
| </div> |
|
|
| |
| <div class="stage s-output"> |
| <div class="stage-box"> |
| <div class="stage-title"> |
| <span>📦</span> Laravel Application Files |
| </div> |
| <div class="file-grid"> |
| <div class="file-item"><span class="file-icon" style="color:#f59e0b;">◆</span> create_books_table.php</div> |
| <div class="file-item"><span class="file-icon" style="color:#f59e0b;">◆</span> create_authors_table.php</div> |
| <div class="file-item"><span class="file-icon" style="color:#6366f1;">◆</span> Book.php</div> |
| <div class="file-item"><span class="file-icon" style="color:#6366f1;">◆</span> Author.php</div> |
| <div class="file-item"><span class="file-icon" style="color:#10b981;">◆</span> BookController.php</div> |
| <div class="file-item"><span class="file-icon" style="color:#10b981;">◆</span> AuthorController.php</div> |
| <div class="file-item"><span class="file-icon" style="color:#8b5cf6;">◆</span> BookResource.php</div> |
| <div class="file-item"><span class="file-icon" style="color:#8b5cf6;">◆</span> AuthorResource.php</div> |
| <div class="file-item"><span class="file-icon" style="color:#ec4899;">◆</span> StoreBookRequest.php</div> |
| <div class="file-item"><span class="file-icon" style="color:#ef4444;">◆</span> BookApiTest.php</div> |
| </div> |
| <div class="stage-meta" style="margin-top:12px;"> |
| <span class="meta-tag">20/20 Pest tests pass</span> |
| <span class="meta-tag">Ready for Laravel 13.x</span> |
| </div> |
| </div> |
| </div> |
|
|
| </div> |
|
|
| |
|
|
| <div class="ontology-section"> |
| <h2>The Ontological Gap</h2> |
| <p style="text-align:center; color:#94a3b8; font-size:0.9em; margin-bottom:24px;"> |
| Semantic hallucinations occur when the model fills prompt gaps from its own pretraining ontology |
| </p> |
|
|
| <div class="ontology-compare"> |
|
|
| |
| <div class="ontology-card card-model"> |
| <h3><span>🤖</span> Model's Implicit Ontology</h3> |
| <p style="font-size:0.78em; color:#a5b4fc; margin-bottom:10px;">Learned from pretraining on millions of PHP files</p> |
| <ul> |
| <li>❌ "Models usually have a <code>user()</code> relationship"</li> |
| <li>❌ "Validation includes <code>'optional'</code>"</li> |
| <li>❌ "Controllers use closure-based eager loading"</li> |
| <li>❌ "Use <code>$request->user()->create()</code>"</li> |
| <li>❌ "Resources need <code>->withHttpStatus()</code>"</li> |
| </ul> |
| </div> |
|
|
| <div class="ontology-vs">vs</div> |
|
|
| |
| <div class="ontology-card card-dev"> |
| <h3><span>👨‍💻</span> Developer's Intended Ontology</h3> |
| <p style="font-size:0.78em; color:#6ee7b7; margin-bottom:10px;">Application-specific, made explicit via BuildSpec</p> |
| <ul> |
| <li>✔ "Book <code>belongsTo</code> Author (not User)"</li> |
| <li>✔ "Validation uses <code>'nullable'</code>"</li> |
| <li>✔ "Simple array eager loading: <code>['author']</code>"</li> |
| <li>✔ "<code>Book::create($request->validated())</code>"</li> |
| <li>✔ "<code>response()->json(new BookResource(...))</code>"</li> |
| </ul> |
| </div> |
|
|
| </div> |
|
|
| <div class="gap-label">↑ Without BuildSpec: the model fills gaps from its own ontology → hallucinations</div> |
| <div class="bridge-label">↓ With BuildSpec: developer's ontology is explicit and complete → no gaps to fill</div> |
| </div> |
|
|
| |
|
|
| <div class="footer"> |
| <p style="margin-bottom:8px;"> |
| <a href="https://huggingface.co/fchis/Laravel-13x-Qwen2.5-Coder-7B-Instruct-LoRA-Spec">Model</a> · |
| <a href="https://huggingface.co/datasets/fchis/laravel-buildspec-training">Dataset</a> · |
| <a href="https://github.com/florinel-chis/laravel-ai-gen">GitHub</a> |
| </p> |
| <p>Florinel Chis · 2026 · Runs entirely on Apple Silicon (M2 Pro 16GB)</p> |
| </div> |
|
|
| </div> |
|
|
| </body> |
| </html> |
|
|