Files
InvoiceShelf/app/Platform/Ai/Http/Company/ChatController.php
T
Darko Gjorgjijoski 5ef7804e60 refactor: adopt modular domain architecture (#747)
* refactor: stabilize model identities for domain migration

* refactor: extract module platform context

* refactor: assign models to domain contexts

* refactor: extract ai platform context

* refactor: extract storage platform context

* refactor: extract mail platform context

* refactor: extract pdf platform context

* refactor: extract operations platform context

* refactor: move installation into operations platform

* refactor: extract money domain context

* refactor: extract taxation domain context

* refactor: extract catalog domain context

* refactor: extract metadata domain context

* refactor: extract reporting domain context

* refactor: extract purchases domain context

* refactor: extract receivables domain context

* refactor: extract accounts domain context

* refactor: complete reporting statement boundary

* refactor: extract contacts domain context

* refactor: extract sales domain context

* refactor: remove legacy application layers

* fix: migrate legacy bouncer role identities
2026-08-05 17:40:03 +02:00

99 lines
3.0 KiB
PHP

<?php
namespace App\Platform\Ai\Http\Company;
use App\Platform\Ai\Application\AiAssistantService;
use App\Platform\Ai\Exceptions\AiException;
use App\Platform\Ai\Models\AiConversation;
use App\Platform\Http\Controller;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
class ChatController extends Controller
{
public function __construct(
private readonly AiAssistantService $assistant,
) {}
/**
* Send a message into a conversation and get the assistant's reply.
*
* If `conversation_id` is omitted (or belongs to a conversation the user
* doesn't own), we start a fresh conversation scoped to the current
* (company_id, user_id). This matches the "new chat" UX where the user
* opens the drawer and starts typing immediately.
*
* @throws ValidationException
*/
public function __invoke(Request $request): JsonResponse
{
$this->authorize('use ai');
$validated = $this->validate($request, [
'conversation_id' => 'nullable|integer',
'message' => 'required|string|max:10000',
]);
$companyId = (int) $request->header('company');
$userId = (int) $request->user()->id;
$conversation = $this->resolveConversation(
$validated['conversation_id'] ?? null,
$companyId,
$userId,
$validated['message'],
);
try {
$assistantMessage = $this->assistant->chat($conversation, $validated['message']);
} catch (AiException $e) {
return response()->json([
'error' => $e->errorKey,
'message' => $e->getMessage(),
], 422);
}
$conversation->refresh();
return response()->json([
'conversation' => [
'id' => $conversation->id,
'title' => $conversation->title,
'model' => $conversation->model,
'updated_at' => $conversation->updated_at,
],
'message' => [
'id' => $assistantMessage->id,
'role' => $assistantMessage->role,
'content' => $assistantMessage->content,
'created_at' => $assistantMessage->created_at,
],
]);
}
/**
* Pick an existing conversation the user owns, or create a new one.
*/
protected function resolveConversation(
?int $conversationId,
int $companyId,
int $userId,
string $firstMessage,
): AiConversation {
if ($conversationId !== null) {
$existing = AiConversation::query()
->where('id', $conversationId)
->where('company_id', $companyId)
->where('user_id', $userId)
->first();
if ($existing) {
return $existing;
}
}
return $this->assistant->startConversation($companyId, $userId, $firstMessage);
}
}