feat(ai): add per-character AI model assignment and identity profiles

Implements deterministic model resolution (column → env map → pool →
default) and adds AI identity fields to characters. Migration backfills
existing rows with deterministic defaults. Controller uses resolved model
and identity block in system prompts for contextual conversations.
This commit is contained in:
2026-10-31 18:35:57 -03:00
parent 59963de995
commit 81be63b4c3
7 changed files with 354 additions and 13 deletions
+63
View File
@@ -1,5 +1,33 @@
<?php
/*
|--------------------------------------------------------------------------
| Character model map parser
|--------------------------------------------------------------------------
|
| Turns AI_CHARACTER_MODELS entries such as "Maria=qwen/qwen3.7-flash:free"
| or "3=deepseek/deepseek-v4-flash-0731" into a lookup array keyed by
| lowercased character id or name. Invalid pairs are ignored.
|
*/
$parseCharacterModels = static function (string $raw): array {
$map = [];
foreach (explode(',', $raw) as $pair) {
$pair = trim($pair);
if ($pair === '' || ! str_contains($pair, '=')) {
continue;
}
[$key, $model] = array_map('trim', explode('=', $pair, 2));
if ($key !== '' && $model !== '') {
$map[strtolower($key)] = $model;
}
}
return $map;
};
return [
/*
@@ -50,8 +78,43 @@ return [
'poolside/laguna-xs-2.1:free',
'inclusionai/ling-3.0-flash-vl:free',
'qwen/qwen3.7-flash:free',
// Verified working free chat models (live probe of the OpenRouter
// catalogue). Safe defaults for per-character assignment.
'deepseek/deepseek-v4-flash-0731',
'nvidia/nemotron-3.5-lightning:free',
'liquid/lfm-2.5-2.6b:free',
'openrouter/free',
],
/*
|--------------------------------------------------------------------------
| Per-Character Model Pool (AI_MODELS)
|--------------------------------------------------------------------------
|
| Comma-separated allow-listed models distributed deterministically
| across characters (each character keeps a stable own model). Falls
| back to AI_DEFAULT_MODEL when empty.
|
*/
'models' => array_values(array_filter(array_map(
'trim',
explode(',', (string) env('AI_MODELS', ''))
))),
/*
|--------------------------------------------------------------------------
| Per-Character Model Map (AI_CHARACTER_MODELS)
|--------------------------------------------------------------------------
|
| Explicit "id or name = model" overrides from the environment, e.g.
| "Maria=liquid/lfm-2.5-2.6b:free,7=openrouter/free". Takes priority
| over the pool; entries outside the allow-list are ignored.
|
*/
'character_models' => $parseCharacterModels((string) env('AI_CHARACTER_MODELS', '')),
/*
|--------------------------------------------------------------------------
| Default Model