|
4 | 4 |
|
5 | 5 | use App\Enums\AiModelCategoryEnum; |
6 | 6 | use App\Models\AiModel; |
| 7 | +use Database\Seeders\Concerns\ReadsCsv; |
7 | 8 | use Illuminate\Database\Seeder; |
8 | | -use Illuminate\Support\Arr; |
9 | 9 |
|
10 | 10 | class AiModelsTableSeeder extends Seeder |
11 | 11 | { |
12 | | - public function run(): void |
13 | | - { |
14 | | - $this->active(); |
15 | | - $this->archived(); |
16 | | - } |
| 12 | + use ReadsCsv; |
17 | 13 |
|
18 | | - private function active(): void |
| 14 | + public function run(): void |
19 | 15 | { |
20 | | - $models = [ |
21 | | - // Reasoning & Coding |
22 | | - [ |
23 | | - 'category' => AiModelCategoryEnum::REASONING_CODING, |
24 | | - 'order' => 1, |
25 | | - 'name' => 'deepseek-v4:flash', |
26 | | - 'provider' => 'DeepSeek AI (CN)', |
27 | | - 'ram' => '~102 GB RAM', |
28 | | - 'license' => 'MIT', |
29 | | - 'role' => [ |
30 | | - 'de' => 'Flaggschiff: Analysen, Buchungslogik, komplexes Coding', |
31 | | - 'en' => 'Flagship: analysis, booking logic, complex coding', |
32 | | - ], |
33 | | - 'link_label' => 'Hugging Face', |
34 | | - 'link_url' => 'https://huggingface.co/unsloth', |
35 | | - ], |
36 | | - [ |
37 | | - 'category' => AiModelCategoryEnum::REASONING_CODING, |
38 | | - 'order' => 2, |
39 | | - 'name' => 'kimi-linear:48b', |
40 | | - 'provider' => 'Moonshot AI (CN)', |
41 | | - 'ram' => '30 GB RAM', |
42 | | - 'license' => 'MIT', |
43 | | - 'role' => [ |
44 | | - 'de' => 'Alltags-Sprinter, lange Dokumente & Kontextverständnis', |
45 | | - 'en' => 'Everyday sprinter, long documents & context understanding', |
46 | | - ], |
47 | | - 'link_label' => 'Hugging Face', |
48 | | - 'link_url' => 'https://huggingface.co/moonshotai', |
49 | | - ], |
50 | | - [ |
51 | | - 'category' => AiModelCategoryEnum::REASONING_CODING, |
52 | | - 'order' => 3, |
53 | | - 'name' => 'qwen3-coder:30b', |
54 | | - 'provider' => 'Alibaba / Qwen (CN)', |
55 | | - 'ram' => '18 GB RAM', |
56 | | - 'license' => 'Apache-2.0', |
57 | | - 'role' => [ |
58 | | - 'de' => 'Coding-Spezialist für schnelle Iterationen & Agenten-Tasks', |
59 | | - 'en' => 'Coding specialist for fast iterations & agent tasks', |
60 | | - ], |
61 | | - 'link_label' => 'Ollama', |
62 | | - 'link_url' => 'https://ollama.com/library/qwen3-coder', |
63 | | - ], |
64 | | - [ |
65 | | - 'category' => AiModelCategoryEnum::REASONING_CODING, |
66 | | - 'order' => 4, |
67 | | - 'name' => 'qwen3.6:35b', |
68 | | - 'provider' => 'Alibaba / Qwen (CN)', |
69 | | - 'ram' => '23 GB RAM', |
70 | | - 'license' => 'Apache-2.0', |
71 | | - 'role' => [ |
72 | | - 'de' => 'Reserve-Mittelklasse', |
73 | | - 'en' => 'Mid-range reserve', |
74 | | - ], |
75 | | - 'link_label' => 'Ollama', |
76 | | - 'link_url' => 'https://ollama.com/library/qwen3.6', |
77 | | - ], |
78 | | - |
79 | | - // Vision & Dokumente |
80 | | - [ |
81 | | - 'category' => AiModelCategoryEnum::VISION_DOCUMENTS, |
82 | | - 'order' => 1, |
83 | | - 'name' => 'qwen3-vl:32b', |
84 | | - 'provider' => 'Alibaba / Qwen (CN)', |
85 | | - 'ram' => '20 GB RAM', |
86 | | - 'license' => 'Apache-2.0', |
87 | | - 'role' => [ |
88 | | - 'de' => 'Detailliertes Bildverständnis: Screenshots, Diagramme, Belege', |
89 | | - 'en' => 'Detailed image understanding: screenshots, diagrams, receipts', |
90 | | - ], |
91 | | - 'link_label' => 'Ollama', |
92 | | - 'link_url' => 'https://ollama.com/library/qwen3-vl', |
93 | | - ], |
94 | | - [ |
95 | | - 'category' => AiModelCategoryEnum::VISION_DOCUMENTS, |
96 | | - 'order' => 2, |
97 | | - 'name' => 'qwen3-vl:8b', |
98 | | - 'provider' => 'Alibaba / Qwen (CN)', |
99 | | - 'ram' => '6,1 GB RAM', |
100 | | - 'license' => 'Apache-2.0', |
101 | | - 'role' => [ |
102 | | - 'de' => 'Schnelle Vision-Variante für einfache Bildaufgaben', |
103 | | - 'en' => 'Fast vision variant for simple image tasks', |
104 | | - ], |
105 | | - 'link_label' => 'Ollama', |
106 | | - 'link_url' => 'https://ollama.com/library/qwen3-vl', |
107 | | - ], |
108 | | - [ |
109 | | - 'category' => AiModelCategoryEnum::VISION_DOCUMENTS, |
110 | | - 'order' => 3, |
111 | | - 'name' => 'gemma4:31b', |
112 | | - 'provider' => 'Google', |
113 | | - 'ram' => '19 GB RAM', |
114 | | - 'license' => 'Gemma', |
115 | | - 'role' => [ |
116 | | - 'de' => 'Bild-Input + stilsichere Texte', |
117 | | - 'en' => 'Image input + polished writing', |
118 | | - ], |
119 | | - 'link_label' => 'Ollama', |
120 | | - 'link_url' => 'https://ollama.com/library/gemma4', |
121 | | - ], |
122 | | - [ |
123 | | - 'category' => AiModelCategoryEnum::VISION_DOCUMENTS, |
124 | | - 'order' => 4, |
125 | | - 'name' => 'deepseek-ocr', |
126 | | - 'provider' => 'DeepSeek AI (CN)', |
127 | | - 'ram' => '6,7 GB RAM', |
128 | | - 'license' => 'MIT', |
129 | | - 'role' => [ |
130 | | - 'de' => 'PDF/Scan → Markdown', |
131 | | - 'en' => 'PDF/scan → Markdown', |
132 | | - ], |
133 | | - 'link_label' => 'Ollama', |
134 | | - 'link_url' => 'https://ollama.com/library/deepseek-ocr', |
135 | | - ], |
136 | | - |
137 | | - // Retrieval & Suche |
138 | | - [ |
139 | | - 'category' => AiModelCategoryEnum::RETRIEVAL_SEARCH, |
140 | | - 'order' => 1, |
141 | | - 'name' => 'qwen3-embedding:8b', |
142 | | - 'provider' => 'Alibaba / Qwen (CN)', |
143 | | - 'ram' => '4,7 GB RAM', |
144 | | - 'license' => 'Apache-2.0', |
145 | | - 'role' => [ |
146 | | - 'de' => 'Vektoren für Ähnlichkeitssuche', |
147 | | - 'en' => 'Vectors for similarity search', |
148 | | - ], |
149 | | - 'link_label' => 'Ollama', |
150 | | - 'link_url' => 'https://ollama.com/library/qwen3-embedding', |
151 | | - ], |
152 | | - ]; |
153 | | - |
154 | | - foreach ($models as $model) { |
155 | | - AiModel::updateOrCreate( |
156 | | - ['name' => $model['name']], |
157 | | - array_merge(['archived_at' => null, 'replaced_by_id' => null], $model) |
| 16 | + $rows = collect($this->readCsv('ai_models.csv')); |
| 17 | + |
| 18 | + $models = $rows->map(function (array $row) { |
| 19 | + return AiModel::updateOrCreate( |
| 20 | + ['name' => $row['name']], |
| 21 | + [ |
| 22 | + 'category' => AiModelCategoryEnum::from($row['category']), |
| 23 | + 'order' => (int) $row['order'], |
| 24 | + 'provider' => $row['provider'] !== '' ? $row['provider'] : null, |
| 25 | + 'ram' => $row['ram'] !== '' ? $row['ram'] : null, |
| 26 | + 'license' => $row['license'] !== '' ? $row['license'] : null, |
| 27 | + 'role' => $this->decodeJson($row['role']), |
| 28 | + 'link_label' => $row['link_label'] !== '' ? $row['link_label'] : null, |
| 29 | + 'link_url' => $row['link_url'] !== '' ? $row['link_url'] : null, |
| 30 | + 'archived_at' => $row['archived_at'] !== '' ? $row['archived_at'] : null, |
| 31 | + ] |
158 | 32 | ); |
159 | | - } |
160 | | - } |
| 33 | + }); |
161 | 34 |
|
162 | | - private function archived(): void |
163 | | - { |
164 | | - $archivedAt = '2026-07-01'; |
| 35 | + $idsByCsvId = $rows->pluck('id')->combine($models->pluck('id')); |
165 | 36 |
|
166 | | - $models = [ |
167 | | - [ |
168 | | - 'category' => AiModelCategoryEnum::REASONING_CODING, |
169 | | - 'order' => 1, |
170 | | - 'name' => 'qwen3.5:122b', |
171 | | - 'replaced_by' => 'deepseek-v4:flash', |
172 | | - ], |
173 | | - [ |
174 | | - 'category' => AiModelCategoryEnum::REASONING_CODING, |
175 | | - 'order' => 2, |
176 | | - 'name' => 'qwen3.6:35b-a3b', |
177 | | - 'replaced_by' => 'kimi-linear:48b', |
178 | | - ], |
179 | | - [ |
180 | | - 'category' => AiModelCategoryEnum::REASONING_CODING, |
181 | | - 'order' => 3, |
182 | | - 'name' => 'qwen3.6:27b', |
183 | | - 'replaced_by' => 'qwen3.6:35b', |
184 | | - ], |
185 | | - [ |
186 | | - 'category' => AiModelCategoryEnum::REASONING_CODING, |
187 | | - 'order' => 4, |
188 | | - 'name' => 'qwen3.5:9b', |
189 | | - 'replaced_by' => 'kimi-linear:48b', |
190 | | - ], |
191 | | - [ |
192 | | - 'category' => AiModelCategoryEnum::REASONING_CODING, |
193 | | - 'order' => 5, |
194 | | - 'name' => 'qwen3.5:4b', |
195 | | - 'replaced_by' => 'kimi-linear:48b', |
196 | | - ], |
197 | | - [ |
198 | | - 'category' => AiModelCategoryEnum::VISION_DOCUMENTS, |
199 | | - 'order' => 1, |
200 | | - 'name' => 'gemma4:12b', |
201 | | - 'replaced_by' => 'qwen3-vl:8b', |
202 | | - ], |
203 | | - [ |
204 | | - 'category' => AiModelCategoryEnum::VISION_DOCUMENTS, |
205 | | - 'order' => 2, |
206 | | - 'name' => 'gemma4:e4b', |
207 | | - 'replaced_by' => 'qwen3-vl:8b', |
208 | | - ], |
209 | | - [ |
210 | | - 'category' => AiModelCategoryEnum::RETRIEVAL_SEARCH, |
211 | | - 'order' => 1, |
212 | | - 'name' => 'qwen3-embedding:4b', |
213 | | - 'replaced_by' => 'qwen3-embedding:8b', |
214 | | - ], |
215 | | - [ |
216 | | - 'category' => AiModelCategoryEnum::RETRIEVAL_SEARCH, |
217 | | - 'order' => 2, |
218 | | - 'name' => 'qwen3-reranker:8b', |
219 | | - 'replaced_by' => null, |
220 | | - ], |
221 | | - ]; |
| 37 | + $rows->each(function (array $row) use ($idsByCsvId, $models) { |
| 38 | + if ($row['replaced_by_id'] === '') { |
| 39 | + return; |
| 40 | + } |
222 | 41 |
|
223 | | - foreach ($models as $model) { |
224 | | - $replacedByName = Arr::pull($model, 'replaced_by'); |
225 | | - $replacedById = $replacedByName ? AiModel::where('name', $replacedByName)->value('id') : null; |
226 | | - |
227 | | - AiModel::updateOrCreate( |
228 | | - ['name' => $model['name']], |
229 | | - array_merge(['archived_at' => $archivedAt, 'replaced_by_id' => $replacedById], $model) |
230 | | - ); |
231 | | - } |
| 42 | + $models->firstWhere('id', $idsByCsvId->get($row['id'])) |
| 43 | + ->update(['replaced_by_id' => $idsByCsvId->get($row['replaced_by_id'])]); |
| 44 | + }); |
232 | 45 | } |
233 | 46 | } |
0 commit comments