RT-Extension-AI
view release on metacpan or search on metacpan
html/Helpers/AISearch/GenerateTicketSQL view on Meta::CPAN
# Add Format grammar if available
if ($format_grammar) {
$full_prompt .= <<"END_FORMAT_GRAMMAR";
# Format Grammar Reference
$format_grammar
END_FORMAT_GRAMMAR
}
# The prompt for the expected response format is inline below because the
# code expects a specific JSON structure. If set in the configuration file,
# users might unintentionally change the response format and break the code
# parsing the response.
$full_prompt .= <<"END_REQUEST";
# User Request
$naturalLanguageQuery
# Response Format
You MUST respond with valid JSON in this exact format (no markdown, no explanation):
{"query": "YOUR_TICKETSQL_QUERY", "format": "YOUR_FORMAT_STRING", "message": "Brief explanation of what the search does"}
The "query" field contains the TicketSQL query.
The "format" field contains the Format string for displaying results. Use the Format Grammar Reference above to construct an appropriate format based on what the user wants to see. If the user doesn't specify display preferences, use a sensible defau...
The "message" field contains a brief (1-2 sentence) explanation of what tickets will be found and what columns will be displayed.
END_REQUEST
# Initialize the AI provider
my $provider_class = "RT::Extension::AI::Provider::" . $config->{name};
my $provider_instance = $provider_class->new(config => $config);
# Process the request
my $response = $provider_instance->process_request(
prompt => $full_prompt,
raw_text => '', # The query is in the prompt
model_config => $config->{default_model},
);
if ($response->{success}) {
my $ai_response = $response->{result};
# Try to parse as JSON
my $parsed;
eval {
# Remove markdown code blocks if present
$ai_response =~ s/```json\s*//g;
$ai_response =~ s/```\s*//g;
$ai_response =~ s/^\s+|\s+$//g;
RT->Logger->debug("AI response after cleanup: $ai_response");
$parsed = JSON::decode_json($ai_response);
};
# Use default format when AI doesn't provide one
my $user_prefs = $session{'CurrentUser'}->UserObj->Preferences("SearchDisplay") || {};
my $default_search_format = $user_prefs->{'Format'} || RT->Config->Get('DefaultSearchResultFormat');
if ($@ || !$parsed) {
RT->Logger->warning("AI response was not valid JSON, attempting to extract TicketSQL. Error: $@");
RT->Logger->debug("Failed to parse as JSON, raw response was: $ai_response");
# Fallback: try to extract TicketSQL the old way
my $ticketsql = $ai_response;
$ticketsql =~ s/```ticketsql\s*//g;
$ticketsql =~ s/```\s*//g;
$ticketsql =~ s/^\s+|\s+$//g;
# If multiline, find the query line
if ($ticketsql =~ /\n/) {
my @lines = split /\n/, $ticketsql;
for my $line (@lines) {
next if $line =~ /^\s*$/;
next if $line =~ /^(here|this|the|note|explanation|search)/i;
if ($line =~ /(Queue|Status|Owner|Priority|Subject|Created|Due|Requestor|Content)\s*[=!<>]|AND|OR/i) {
$ticketsql = $line;
last;
}
}
}
$result->{success} = 1;
$result->{query} = $ticketsql;
$result->{format} = $default_search_format;
$result->{message} = "Search generated (using default display format)";
} else {
$result->{success} = 1;
$result->{query} = $parsed->{query} // '';
$result->{format} = $parsed->{format} // $default_search_format;
$result->{message} = $parsed->{message} // '';
}
} else {
RT->Logger->error("AI request failed: " . $response->{error});
$result->{message} = $response->{error} || "Unknown error";
}
}
$output = JSON::encode_json($result);
</%INIT>
( run in 0.446 second using v1.01-cache-2.11-cpan-8f98c5d2c55 )