v1.3.0
- Snipe-IT asset inventory integration (Admin > Integracje), optional and off by default: connect by API address + personal token (+ SSL-verification bypass). Three independent toggles: client can pick which of their own Snipe-IT assets a ticket concerns (scoped to admin-selected subcategories, empty = never shows), operator sees the requester's assets in a ticket-view sidebar, operator can search the whole inventory from that same sidebar (not a separate page) for shared equipment. Assets shown as "numer środka - numer seryjny - producent model" + category; a linked asset's live status is fetched fresh on the ticket page, and unlinking stays available to an operator even with both view/search toggles off. - AI summary: a "Wygeneruj teraz" button for an immediate on-demand refresh, plus a new admin toggle to regenerate right after every new reply/note instead of only on the next scheduled sweep. The transcript sent to the model now also includes the ticket's own opening body, fixing summaries missing the original request on long threads. - Fixed: the status dropdown in the operator ticket view could keep showing the pre-change status after a status-changing quick action until the next page load (Livewire/Alpine-morph quirk for wire:change-bound selects). - Docs: README/ARCHITECTURE/CHANGELOG/install/wiki updated for all of the above, including correcting the AI-summary refresh description left over from the 1.2.1 release notes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,8 @@ class TicketAiSummaryService
|
||||
|
||||
protected const MESSAGE_EXCERPT_CHARS = 1500;
|
||||
|
||||
protected const BODY_EXCERPT_CHARS = 4000;
|
||||
|
||||
protected const TRANSCRIPT_MESSAGE_LIMIT = 30;
|
||||
|
||||
public function __construct(protected AiClient $ai) {}
|
||||
@@ -70,6 +72,20 @@ class TicketAiSummaryService
|
||||
})->orderBy('id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Regenerates the summary for a single ticket right now, bypassing the
|
||||
* staleness check — used by the manual "regenerate" button and the
|
||||
* on-new-message hook, as opposed to run()'s scheduled batch sweep.
|
||||
*/
|
||||
public function generateFor(Ticket $ticket): bool
|
||||
{
|
||||
if (! $this->ai->enabled() || ! Settings::bool('ai_summary_enabled')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->summarizeOne($ticket);
|
||||
}
|
||||
|
||||
protected function summarizeOne(Ticket $ticket): bool
|
||||
{
|
||||
$raw = $this->ai->chat([
|
||||
@@ -95,9 +111,20 @@ class TicketAiSummaryService
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Includes tickets.body explicitly (the opening description, separate
|
||||
* from ticket_messages) rather than relying on it showing up as the
|
||||
* thread's first message — that row falls outside the last-N transcript
|
||||
* window on any ticket with more than TRANSCRIPT_MESSAGE_LIMIT messages,
|
||||
* which would otherwise silently drop the original request from long
|
||||
* threads. Mirrors TicketAiTriageService's own subject+body framing.
|
||||
*/
|
||||
protected function buildTranscript(Ticket $ticket): string
|
||||
{
|
||||
$lines = ["Temat: {$ticket->subject}"];
|
||||
$lines = [
|
||||
"Temat: {$ticket->subject}",
|
||||
"Treść:\n".Str::limit(strip_tags($ticket->body), self::BODY_EXCERPT_CHARS),
|
||||
];
|
||||
|
||||
$ticket->messages()->latest('created_at')->limit(self::TRANSCRIPT_MESSAGE_LIMIT)->get()
|
||||
->sortBy('created_at')
|
||||
|
||||
Reference in New Issue
Block a user