Files
City10/config/ai.php
T
marcocosta10000 e7498e1566 feat(city): add airports, ATC agent, luminous signs, and AI model fallback chain
- Add two airports (north/south) with full geometry: runways, taxiways,
  aprons, terminals, hangars, stands, and helipads; walkable airport
  grounds extend beyond city bounds.
- Introduce ATC AI agent ("Torre de Controle City10") using AI_ATC_MODEL
  to generate takeoff clearances; includes dispatch endpoint with
  30-second cooldown and deterministic fallback on failure.
- Implement AI model fallback chain: preferred model → AI_FALLBACK_MODEL
  before failing, applied in ChatController and PersonTalkController.
- Add luminous sign labels (letreiros) for plazas, beach, food, hospital,
  school, city hall, and airports with day/night glow.
- Fix city presence cache: compute is_me per viewer instead of baking
  into shared per-second cache to prevent cross-user flag leakage.
- Add tests: AirportTest, CityPresenceTest, SignLabelsTest, and
  PersonTalkTest fallback model retry scenario.
2026-10-31 22:06:18 -03:00

170 lines
5.9 KiB
PHP
Executable File

<?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 [
/*
|--------------------------------------------------------------------------
| OpenRouter.ai Integration
|--------------------------------------------------------------------------
|
| Credentials and behaviour settings used by the AI agents to take
| strategic decisions through the OpenRouter.ai chat completions API.
| The API key must be provided via the OPENROUTER_API_KEY env variable
| and must never be committed to version control.
|
*/
'openrouter' => [
'api_key' => env('OPENROUTER_API_KEY'),
'base_url' => env('OPENROUTER_BASE_URL', 'https://openrouter.ai/api/v1'),
'timeout' => (int) env('OPENROUTER_TIMEOUT', 30),
'app_name' => env('APP_NAME', 'City10'),
'app_url' => env('APP_URL', 'http://localhost:16980'),
],
/*
|--------------------------------------------------------------------------
| Allow-listed Free Models
|--------------------------------------------------------------------------
|
| Only the free models listed below may be selected for AI agents.
| Administrators cannot register agents with models outside this list,
| which keeps operating costs at zero and enforces the platform policy.
|
*/
'allowed_models' => [
'poolside/laguna-m.1:free',
'cohere/north-mini-code:free',
'deepseek/deepseek-v4-flash-0731:free',
'qwen/qwen3.8-27b:free',
'nvidia/nemotron-3-ultra-550b-a55b:free',
'nvidia/nemotron-3-super-120b-a12b:free',
'google/gemma-4-31b-it:free',
'google/gemma-4-26b-a4b-it:free',
'openai/gpt-oss-20b:free',
'nvidia/nemotron-nano-12b-v2-vl:free',
'nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free',
'nvidia/nemotron-3-nano-30b-a3b:free',
'nvidia/nemotron-nano-9b-v2:free',
'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',
// Owner-approved exception: paid but near-zero cost
// (1.4e-7/1k tokens), verified live on 2026-11-01.
'xiaomi/mimo-v2.6-flash',
],
/*
|--------------------------------------------------------------------------
| 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', '')),
/*
|--------------------------------------------------------------------------
| Air Traffic Control Model (AI_ATC_MODEL)
|--------------------------------------------------------------------------
|
| Default model used by the airport control tower agent for movement
| clearances (takeoff authorizations). Must stay inside allowed_models.
|
*/
'atc_model' => env('AI_ATC_MODEL', 'poolside/laguna-xs-2.1:free'),
/*
|--------------------------------------------------------------------------
| Fallback Model (AI_FALLBACK_MODEL)
|--------------------------------------------------------------------------
|
| Tried whenever the preferred model of a call fails (chain fallback).
| Owner-approved paid model with near-zero cost; must stay inside
| allowed_models.
|
*/
'fallback_model' => env('AI_FALLBACK_MODEL', 'xiaomi/mimo-v2.6-flash'),
/*
|--------------------------------------------------------------------------
| Default Model
|--------------------------------------------------------------------------
|
| Model assigned to newly created agents when none is specified.
|
*/
'default_model' => env('AI_DEFAULT_MODEL', 'deepseek/deepseek-v4-flash-0731'),
/*
|--------------------------------------------------------------------------
| Agent Decision Cadence (seconds)
|--------------------------------------------------------------------------
|
| Minimum interval between two reasoning calls for the same agent,
| protecting the upstream API from excessive usage.
|
*/
'think_interval' => (int) env('AI_THINK_INTERVAL', 60),
];