Files
ABSDEV/Modules/AI/Services/AIManager.php
T
Mark Gregory 5dbb04e3ad AI provider management
* OpenAI-compatible provider adapter
* OpenAI support
* LM Studio support
* Ollama OpenAI-compatible support
* Encrypted API-key storage
* Default provider and model selection
* Provider connection testing
* Reusable prompt library
* Prompt variables and templates
* Prompt version field
* AI execution history
* Token-usage storage
* Duration and failure tracking
* Manual knowledge sources
* Knowledge-source chunking
* Knowledge indexing status
* Authenticated AI REST endpoint
* AI administration dashboard
* AI permissions
* Module registration and boot test
2026-07-24 13:01:56 +01:00

43 lines
2.8 KiB
PHP

<?php
namespace Modules\AI\Services;
use Illuminate\Support\Str;
use Modules\AI\Contracts\AIManager as AIManagerContract;
use Modules\AI\Contracts\AIProviderDriver;
use Modules\AI\Drivers\OpenAICompatibleDriver;
use Modules\AI\Models\AIExecution;
use Modules\AI\Models\AIPrompt;
use Modules\AI\Models\AIProvider;
final class AIManager implements AIManagerContract
{
public function execute(AIPrompt|string $prompt,array $variables=[],array $options=[]): AIExecution
{
$promptModel=is_string($prompt)?AIPrompt::where('slug',$prompt)->where('is_active',true)->firstOrFail():$prompt;
$provider=$this->provider($options['provider_id']??null); $input=$promptModel->render($variables);
$execution=AIExecution::create(['execution_id'=>(string)Str::uuid(),'ai_provider_id'=>$provider->id,'ai_prompt_id'=>$promptModel->id,'user_id'=>auth()->id(),'model'=>$options['model']??$provider->default_model,'purpose'=>$promptModel->purpose,'input'=>$input,'context'=>$variables,'status'=>'running']);
$started=microtime(true);
try {
$messages=[]; if (filled($promptModel->system_prompt)) $messages[]=['role'=>'system','content'=>$promptModel->system_prompt];
$messages[]=['role'=>'user','content'=>$input]; $result=$this->driver($provider)->generate($provider,$messages,$options);
$execution->update(['output'=>$result['text'],'usage'=>$result['usage'],'status'=>'completed','duration_ms'=>(int)((microtime(true)-$started)*1000),'completed_at'=>now()]);
} catch (\Throwable $e) {
report($e); $execution->update(['status'=>'failed','error'=>$e->getMessage(),'duration_ms'=>(int)((microtime(true)-$started)*1000),'completed_at'=>now()]);
}
return $execution->refresh();
}
public function testProvider(int $providerId): array
{
$provider=AIProvider::findOrFail($providerId);
try { $result=$this->driver($provider)->test($provider); $provider->update(['last_tested_at'=>now(),'last_test_status'=>'passed','last_test_message'=>$result['message']]); return $result; }
catch (\Throwable $e) { $provider->update(['last_tested_at'=>now(),'last_test_status'=>'failed','last_test_message'=>$e->getMessage()]); return ['ok'=>false,'message'=>$e->getMessage()]; }
}
private function provider(?int $id): AIProvider
{
return AIProvider::query()->when($id,fn($q)=>$q->whereKey($id))->when(!$id,fn($q)=>$q->where('is_default',true))->where('is_active',true)->first()
?? AIProvider::where('is_active',true)->firstOrFail();
}
private function driver(AIProvider $provider): AIProviderDriver
{
return match($provider->driver) { 'openai_compatible','openai','lm_studio','ollama'=>app(OpenAICompatibleDriver::class), default=>throw new \InvalidArgumentException("Unsupported AI driver [{$provider->driver}].") };
}
}