Files
nuqloud-ai/lib/Service/ToolIntentService.php
T
Pascal Kienast 0739d3da6a Initial open-source release of Talk AI
Talk AI is a multi-bot AI assistant manager for Nextcloud Talk:
per-bot prompts and models, agentic tool calling (MCP + built-in
tools), RAG over Nextcloud files, room-document search, vision and
speech-to-text attachments, persistent bot wikis, approval workflows,
rate limiting, and multi-provider LLM support (any OpenAI-compatible
endpoint).

Developed within EDUC - the European Digital UniverCity
(https://educalliance.eu), where it runs as the 'EDUC AI' assistant on
the alliance-wide Nextcloud portal. This public repository is the
upstream point of truth; deployment-specific tools plug in via the
tool-provider extension point (docs/TOOL_PROVIDERS.md).

License: AGPL-3.0-or-later.
2026-07-08 21:13:13 +02:00

96 lines
2.3 KiB
PHP

<?php
declare(strict_types=1);
namespace OCA\EducAI\Service;
use OCA\EducAI\Db\Bot;
/**
* Keeps user-message intent heuristics out of the message orchestration code.
*
* @psalm-import-type McpToolLoadoutEntry from \OCA\EducAI\TypeDefinitions
*/
class ToolIntentService {
private const FORCE_TOOL_KEYWORDS = [
'tool call',
'toolcall',
'nutze das tool',
'mach ein tool',
'mach einen tool',
'websuche',
'suche im web',
'suche im internet',
'such im internet',
'recherchiere',
'search the web',
'search online',
'use the web',
'googele',
'im internet',
'im web',
'internet recherchieren',
];
/**
* @param array<int,McpToolLoadoutEntry> $toolLoadout
*/
public function shouldForceToolCall(Bot $bot, string $cleanMessage, ?string $originalMessage, array $toolLoadout): bool {
$combinedText = strtolower(trim($originalMessage ?? '')) . ' ' . strtolower(trim($cleanMessage));
$combinedText = trim($combinedText);
foreach ($toolLoadout as $entry) {
$config = $entry['config'] ?? [];
if (($config['force'] ?? false) === true) {
return true;
}
}
$keywords = self::FORCE_TOOL_KEYWORDS;
if ($this->mentionIndicatesSearch(strtolower($bot->getMentionName())) || $this->toolListIndicatesSearch($toolLoadout)) {
$keywords[] = 'was ist';
$keywords[] = 'aktuelles';
$keywords[] = 'heute';
}
foreach ($keywords as $needle) {
if ($needle !== '' && $combinedText !== '' && str_contains($combinedText, $needle)) {
return true;
}
}
return false;
}
public function mentionIndicatesSearch(string $mention): bool {
if ($mention === '') {
return false;
}
return str_contains($mention, 'web')
|| str_contains($mention, 'search')
|| str_contains($mention, 'browser');
}
/**
* @param array<int,McpToolLoadoutEntry> $toolLoadout
*/
public function toolListIndicatesSearch(array $toolLoadout): bool {
foreach ($toolLoadout as $entry) {
$tool = $entry['tool'];
$name = strtolower($tool->getName());
$description = strtolower($tool->getDescription() ?? '');
if (
str_contains($name, 'web')
|| str_contains($name, 'search')
|| str_contains($name, 'browser')
|| str_contains($description, 'websuche')
|| str_contains($description, 'search')
|| str_contains($description, 'internet')
) {
return true;
}
}
return false;
}
}