Increased timeout for local AI inferences, and made AI timeout configurable per provider
Some checks failed
Build assets artifact / Build assets artifact (push) Has been cancelled
Docker Image Build / build (linux/amd64, amd64, ubuntu-latest) (push) Has been cancelled
Docker Image Build / build (linux/arm/v7, armv7, ubuntu-24.04-arm) (push) Has been cancelled
Docker Image Build / build (linux/arm64, arm64, ubuntu-24.04-arm) (push) Has been cancelled
Docker Image Build (FrankenPHP) / build (linux/amd64, amd64, ubuntu-latest) (push) Has been cancelled
Docker Image Build (FrankenPHP) / build (linux/arm/v7, armv7, ubuntu-24.04-arm) (push) Has been cancelled
Docker Image Build (FrankenPHP) / build (linux/arm64, arm64, ubuntu-24.04-arm) (push) Has been cancelled
Static analysis / Static analysis (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.2, mysql) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.3, mysql) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.4, mysql) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.5, mysql) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.2, postgres) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.3, postgres) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.4, postgres) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.5, postgres) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.2, sqlite) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.3, sqlite) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.4, sqlite) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.5, sqlite) (push) Has been cancelled
Docker Image Build / merge (push) Has been cancelled
Docker Image Build (FrankenPHP) / merge (push) Has been cancelled

Fixes issue #1396
This commit is contained in:
Jan Böhmer 2026-06-22 22:06:30 +02:00
parent ec80115d0a
commit 3e725dd2ec
10 changed files with 73 additions and 1 deletions

View file

@ -2,3 +2,4 @@ ai:
platform:
lmstudio:
host_url: '%env(string:settings:ai_lmstudio:hostURL)%'
http_client: 'app.http_client.ai_lmstudio'

View file

@ -3,3 +3,4 @@ ai:
ollama:
endpoint: '%env(string:settings:ai_ollama:endpoint)%'
api_key: '%env(string:settings:ai_ollama:apiKey)%'
http_client: 'app.http_client.ai_ollama'

View file

@ -2,3 +2,4 @@ ai:
platform:
openrouter:
api_key: '%env(string:settings:ai_openrouter:apiKey)%'
http_client: 'app.http_client.ai_openrouter'

View file

@ -52,6 +52,28 @@ services:
alias: 'doctrine.migrations.dependency_factory'
####################################################################################################################
# AI provider HTTP clients (with configurable timeouts)
####################################################################################################################
app.http_client.ai_ollama:
class: Symfony\Contracts\HttpClient\HttpClientInterface
factory: ['@http_client', 'withOptions']
arguments:
- { timeout: '%env(int:settings:ai_ollama:timeout)%' }
app.http_client.ai_lmstudio:
class: Symfony\Contracts\HttpClient\HttpClientInterface
factory: ['@http_client', 'withOptions']
arguments:
- { timeout: '%env(int:settings:ai_lmstudio:timeout)%' }
app.http_client.ai_openrouter:
class: Symfony\Contracts\HttpClient\HttpClientInterface
factory: ['@http_client', 'withOptions']
arguments:
- { timeout: '%env(int:settings:ai_openrouter:timeout)%' }
####################################################################################################################
# Email
####################################################################################################################

View file

@ -282,6 +282,10 @@ final class AIWebProvider implements InfoProviderInterface
try {
$aiPlatform = $this->AIPlatformRegistry->getPlatform($this->settings->platform ?? throw new \RuntimeException('No AI platform selected') );
// AI inference can take much longer than PHP's default max_execution_time (typically 30s).
// The HTTP client timeout already enforces the configured limit; disable PHP's constraint here.
set_time_limit(0);
//'openai/gpt-5-mini'
$result = $aiPlatform->invoke($this->settings->model ?? throw new \RuntimeException('No model selected'), $input, [
'response_format' => [

View file

@ -35,6 +35,8 @@ class AISettings
{
use SettingsTrait;
public const TIMEOUT_LIMIT = 600;
#[EmbeddedSettings]
public ?OpenRouterSettings $openRouter = null;

View file

@ -23,16 +23,17 @@ declare(strict_types=1);
namespace App\Settings\AISettings;
use App\Form\Type\APIKeyType;
use App\Services\AI\AIPlatformSettingsInterface;
use App\Settings\SettingsIcon;
use Jbtronics\SettingsBundle\Metadata\EnvVarMode;
use Jbtronics\SettingsBundle\Settings\Settings;
use Jbtronics\SettingsBundle\Settings\SettingsParameter;
use Jbtronics\SettingsBundle\Settings\SettingsTrait;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\UrlType;
use Symfony\Component\Translation\StaticMessage;
use Symfony\Component\Translation\TranslatableMessage as TM;
use Symfony\Component\Validator\Constraints as Assert;
#[Settings(name: 'ai_lmstudio', label: new TM("settings.ai.lmstudio"))]
#[SettingsIcon("fa-robot")]
@ -46,6 +47,14 @@ class LMStudioSettings implements AIPlatformSettingsInterface
envVar: "AI_LMSTUDIO_HOSTURL", envVarMode: EnvVarMode::OVERWRITE)]
public ?string $hostURL = null;
#[SettingsParameter(label: new TM("settings.ai.timeout"),
description: new TM("settings.ai.timeout.help"),
formType: NumberType::class,
formOptions: ["scale" => 0, "attr" => ["min" => 1]],
)]
#[Assert\Range(min: 1, max: AISettings::TIMEOUT_LIMIT)]
public int $timeout = 180;
public function isAIPlatformEnabled(): bool
{
return $this->hostURL !== null && $this->hostURL !== "";

View file

@ -30,9 +30,11 @@ use Jbtronics\SettingsBundle\Metadata\EnvVarMode;
use Jbtronics\SettingsBundle\Settings\Settings;
use Jbtronics\SettingsBundle\Settings\SettingsParameter;
use Jbtronics\SettingsBundle\Settings\SettingsTrait;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\UrlType;
use Symfony\Component\Translation\StaticMessage;
use Symfony\Component\Translation\TranslatableMessage as TM;
use Symfony\Component\Validator\Constraints as Assert;
#[Settings(name: 'ai_ollama', label: new TM("settings.ai.ollama"))]
#[SettingsIcon("fa-robot")]
@ -51,6 +53,14 @@ class OllamaSettings implements AIPlatformSettingsInterface
envVar: "AI_OLLAMA_API_KEY", envVarMode: EnvVarMode::OVERWRITE)]
public ?string $apiKey = null;
#[SettingsParameter(label: new TM("settings.ai.timeout"),
description: new TM("settings.ai.timeout.help"),
formType: NumberType::class,
formOptions: ["scale" => 0, "attr" => ["min" => 1]]
)]
#[Assert\Range(min: 1, max: AISettings::TIMEOUT_LIMIT)]
public int $timeout = 180;
public function isAIPlatformEnabled(): bool
{
return $this->endpoint !== null && $this->endpoint !== "";

View file

@ -30,7 +30,9 @@ use Jbtronics\SettingsBundle\Metadata\EnvVarMode;
use Jbtronics\SettingsBundle\Settings\Settings;
use Jbtronics\SettingsBundle\Settings\SettingsParameter;
use Jbtronics\SettingsBundle\Settings\SettingsTrait;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Translation\TranslatableMessage as TM;
use Symfony\Component\Validator\Constraints as Assert;
#[Settings(name: 'ai_openrouter', label: new TM("settings.ai.openrouter"), description: "settings.ai.openrouter.help")]
#[SettingsIcon("fa-robot")]
@ -43,6 +45,14 @@ class OpenRouterSettings implements AIPlatformSettingsInterface
formOptions: ["help_html" => true], envVar: "AI_OPENROUTER_KEY", envVarMode: EnvVarMode::OVERWRITE)]
public ?string $apiKey = null;
#[SettingsParameter(label: new TM("settings.ai.timeout"),
description: new TM("settings.ai.timeout.help"),
formType: NumberType::class,
formOptions: ["scale" => 0, "attr" => ["min" => 1]],
envVar: "int:AI_OPENROUTER_TIMEOUT", envVarMode: EnvVarMode::OVERWRITE)]
#[Assert\Range(min: 1, max: AISettings::TIMEOUT_LIMIT)]
public int $timeout = 90;
public function isAIPlatformEnabled(): bool
{
return $this->apiKey !== null && $this->apiKey !== "";

View file

@ -13637,6 +13637,18 @@ Buerklin-API Authentication server:
<target>API Key</target>
</segment>
</unit>
<unit id="VxXEQUD" name="settings.ai.timeout">
<segment state="translated">
<source>settings.ai.timeout</source>
<target>Timeout</target>
</segment>
</unit>
<unit id="vRgtpoJ" name="settings.ai.timeout.help">
<segment state="translated">
<source>settings.ai.timeout.help</source>
<target>Maximum time in seconds to wait for a response. Local AI inference might take multiple minutes, cloud inference is normally faster.</target>
</segment>
</unit>
<unit id="kuDv.So" name="browser_plugin.recent_pages.title">
<segment state="translated">
<source>browser_plugin.recent_pages.title</source>