Backup #1130
| ID | 1130 |
| Dateipfad | /var/www/dev.campus.systemische-tools.de/src/UseCases/Chat/SendChatMessageUseCase.php |
| Version | 14 |
| Typ |
modified |
| Größe | 9.6 KB |
| Hash | f8e81f45dd29502e06fa96eb79ce777292dd402f96f0ca537733be32063db4b5 |
| Datum | 2025-12-25 09:41:01 |
| Geändert von | claude-code-hook |
| Grund | Claude Code Pre-Hook Backup vor Edit-Operation |
| Datei existiert |
Ja
|
Dateiinhalt
<?php
declare(strict_types=1);
namespace UseCases\Chat;
// @responsibility: Orchestriert Chat-Nachrichten (Validierung, AI-Antwort, Speicherung)
use Domain\Repository\ChatMessageRepositoryInterface;
use Domain\Repository\ChatSessionRepositoryInterface;
use Infrastructure\AI\ChatService;
use Infrastructure\AI\ContentQualityValidator;
use Infrastructure\Persistence\ContentConfigRepository;
class SendChatMessageUseCase
{
public function __construct(
private ChatService $chatService,
private ChatSessionRepositoryInterface $sessionRepo,
private ChatMessageRepositoryInterface $messageRepo,
private ContentConfigRepository $configRepo,
private ContentQualityValidator $qualityValidator
) {
}
/**
* Execute the use case
*
* @param string $sessionUuid Session UUID
* @param string $message User message text
* @param string $model AI model identifier
* @param array<string> $collections Qdrant collections to search
* @param int $contextLimit Number of context chunks
* @param int $authorProfileId Author profile ID (0 = none)
* @param int $systemPromptId System prompt config ID
* @param float $temperature AI temperature setting
* @param int $maxTokens Max tokens for response
* @param int $structureId Output structure ID (0 = frei)
* @param bool $qualityCheck Run quality validation on response
* @return ChatResponse
*/
public function execute(
string $sessionUuid,
string $message,
string $model,
array $collections = ['documents'],
int $contextLimit = 5,
int $authorProfileId = 0,
int $systemPromptId = 1,
float $temperature = 0.7,
int $maxTokens = 4096,
int $structureId = 0,
bool $qualityCheck = false
): ChatResponse {
// 1. Validate session
$session = $this->sessionRepo->findByUuid($sessionUuid);
if ($session === null) {
return ChatResponse::error('Session nicht gefunden.');
}
// 2. Validate message
$message = trim($message);
if ($message === '') {
return ChatResponse::error('Bitte gib eine Frage ein.');
}
// 3. Save user message
$this->messageRepo->save(
sessionId: $session['id'],
role: 'user',
content: $message,
model: $model
);
// 4. Auto-set title from first message
if ($session['title'] === null) {
$title = mb_substr($message, 0, 50) . (mb_strlen($message) > 50 ? '...' : '');
$this->sessionRepo->updateTitle($session['id'], $title);
}
// 5. Get style prompt from author profile
$stylePrompt = $this->getStylePromptFromProfile($authorProfileId);
// 6. Get system prompt
$systemPrompt = $this->getSystemPromptById($systemPromptId);
// 6b. Add structure formatting if selected
$structurePrompt = $this->getStructurePrompt($structureId);
if ($structurePrompt !== null) {
$systemPrompt = ($systemPrompt ?? '') . "\n\n" . $structurePrompt;
}
// 7. Track timing and get AI response
$startTime = microtime(true);
try {
$result = $this->chatService->chat(
question: $message,
model: $model,
collections: $collections,
limit: $contextLimit,
stylePrompt: $stylePrompt,
customSystemPrompt: $systemPrompt,
temperature: $temperature,
maxTokens: $maxTokens
);
} catch (\Exception $e) {
return ChatResponse::error('Chat-Service Fehler: ' . $e->getMessage());
}
$endTime = microtime(true);
// 8. Prepare sources as JSON strings for storage
/** @var array<int, non-empty-string> $sourcesForStorage */
$sourcesForStorage = array_map(
static fn (array $source): string => json_encode($source, JSON_THROW_ON_ERROR),
$result['sources']
);
// 9. Save assistant message with tracking
$collectionsJson = json_encode($collections);
$this->messageRepo->save(
sessionId: $session['id'],
role: 'assistant',
content: $result['answer'],
model: $model,
tokensInput: $result['usage']['input_tokens'] ?? null,
tokensOutput: $result['usage']['output_tokens'] ?? null,
sources: $sourcesForStorage,
startMicrotime: $startTime,
endMicrotime: $endTime,
authorProfileId: $authorProfileId > 0 ? $authorProfileId : null,
systemPromptId: $systemPromptId > 0 ? $systemPromptId : null,
collectionsJson: $collectionsJson,
contextLimit: $contextLimit
);
// 10. Create response
$response = ChatResponse::fromServiceResponse($result, $endTime - $startTime);
// 11. Run quality validation if enabled
if ($qualityCheck) {
$structureName = $structureId > 0 ? $this->getStructureName($structureId) : null;
$validation = $this->qualityValidator->validate(
question: $message,
answer: $result['answer'],
sources: $result['sources'],
structureName: $structureName
);
$response = $response->withQualityValidation($validation);
}
// 12. Return response
return $response;
}
/**
* Get structure name by ID
*/
private function getStructureName(int $structureId): ?string
{
$structure = $this->configRepo->findByIdAndType($structureId, 'structure');
return $structure['name'] ?? null;
}
/**
* Create default ChatService from credentials
*/
private function createDefaultChatService(): ChatService
{
$config = \Infrastructure\AI\AIConfig::fromCredentialsFile();
return $config->createChatService();
}
/**
* Get style prompt from author profile
*/
private function getStylePromptFromProfile(int $profileId): ?string
{
if ($profileId === 0) {
return null;
}
$profile = $this->configRepo->findByIdAndType($profileId, 'author_profile');
if ($profile === null) {
return null;
}
$config = json_decode($profile['content'] ?? '{}', true);
if ($config === null) {
return null;
}
$parts = [];
if (isset($config['stimme']['ton'])) {
$parts[] = 'Ton: ' . $config['stimme']['ton'];
}
if (isset($config['stimme']['perspektive'])) {
$parts[] = 'Perspektive: ' . $config['stimme']['perspektive'];
}
if (isset($config['stil']['fachsprache']) && $config['stil']['fachsprache']) {
$parts[] = 'Verwende Fachsprache';
}
if (isset($config['stil']['beispiele']) && $config['stil']['beispiele'] === 'häufig') {
$parts[] = 'Nutze häufig Beispiele';
}
if (isset($config['stil']['listen']) && $config['stil']['listen'] === 'bevorzugt') {
$parts[] = 'Bevorzuge Listen und Bullet-Points';
}
if (isset($config['tabus']) && is_array($config['tabus'])) {
$parts[] = 'Vermeide: ' . implode(', ', $config['tabus']);
}
if ($parts === []) {
return null;
}
return 'Schreibstil (' . ($profile['name'] ?? 'Profil') . '): ' . implode('. ', $parts) . '.';
}
/**
* Get system prompt by ID
*/
private function getSystemPromptById(int $promptId): ?string
{
if ($promptId === 0) {
return null;
}
$prompt = $this->configRepo->findByIdAndType($promptId, 'system_prompt');
if ($prompt === null) {
return null;
}
$content = json_decode($prompt['content'] ?? '{}', true);
return $content['prompt'] ?? null;
}
/**
* Get structure formatting prompt
*/
private function getStructurePrompt(int $structureId): ?string
{
if ($structureId === 0) {
return null;
}
$structure = $this->configRepo->findByIdAndType($structureId, 'structure');
if ($structure === null) {
return null;
}
$name = $structure['name'] ?? 'Struktur';
$config = json_decode($structure['content'] ?? '{}', true);
if ($config === null) {
return null;
}
$parts = ["Formatiere deine Antwort als: {$name}"];
// Add structure-specific instructions
if (isset($config['sections']) && is_array($config['sections'])) {
$parts[] = 'Struktur: ' . implode(' → ', $config['sections']);
}
if (isset($config['max_chars'])) {
$parts[] = 'Maximale Länge: ' . $config['max_chars'] . ' Zeichen';
}
if (isset($config['min_words'])) {
$parts[] = 'Mindestens ' . $config['min_words'] . ' Wörter';
}
if (isset($config['max_words'])) {
$parts[] = 'Maximal ' . $config['max_words'] . ' Wörter';
}
if (isset($config['format']) && $config['format'] === 'qa') {
$min = $config['min_questions'] ?? 3;
$parts[] = "Formatiere als FAQ mit mindestens {$min} Frage-Antwort-Paaren";
}
if (isset($config['hashtags']) && $config['hashtags']) {
$parts[] = 'Füge passende Hashtags hinzu';
}
if (isset($config['cta']) && $config['cta']) {
$parts[] = 'Schließe mit einem Call-to-Action ab';
}
return implode('. ', $parts) . '.';
}
}
Vollständig herunterladen
Aktionen
Andere Versionen dieser Datei
| ID |
Version |
Typ |
Größe |
Datum |
| 2084 |
27 |
modified |
11.4 KB |
2025-12-29 00:09 |
| 2083 |
26 |
modified |
11.4 KB |
2025-12-29 00:09 |
| 2082 |
25 |
modified |
11.4 KB |
2025-12-29 00:08 |
| 2081 |
24 |
modified |
11.5 KB |
2025-12-29 00:08 |
| 1947 |
23 |
modified |
11.5 KB |
2025-12-28 02:21 |
| 1946 |
22 |
modified |
11.5 KB |
2025-12-28 02:21 |
| 1945 |
21 |
modified |
11.5 KB |
2025-12-28 02:21 |
| 1944 |
20 |
modified |
11.5 KB |
2025-12-28 02:21 |
| 1943 |
19 |
modified |
11.5 KB |
2025-12-28 02:21 |
| 1942 |
18 |
modified |
11.5 KB |
2025-12-28 02:20 |
| 1941 |
17 |
modified |
11.5 KB |
2025-12-28 02:20 |
| 1614 |
16 |
modified |
9.7 KB |
2025-12-27 01:34 |
| 1131 |
15 |
modified |
9.7 KB |
2025-12-25 09:41 |
| 1130 |
14 |
modified |
9.6 KB |
2025-12-25 09:41 |
| 842 |
13 |
modified |
10.2 KB |
2025-12-23 08:17 |
| 708 |
12 |
modified |
10.4 KB |
2025-12-23 07:54 |
| 568 |
11 |
modified |
10.4 KB |
2025-12-23 03:37 |
| 563 |
10 |
modified |
9.6 KB |
2025-12-23 03:35 |
| 562 |
9 |
modified |
9.4 KB |
2025-12-23 03:35 |
| 561 |
8 |
modified |
9.4 KB |
2025-12-23 03:35 |
| 555 |
7 |
modified |
9.3 KB |
2025-12-23 03:33 |
| 554 |
6 |
modified |
9.2 KB |
2025-12-23 03:33 |
| 549 |
5 |
modified |
7.6 KB |
2025-12-23 02:38 |
| 548 |
4 |
modified |
7.3 KB |
2025-12-23 02:38 |
| 547 |
3 |
modified |
7.2 KB |
2025-12-23 02:38 |
| 471 |
2 |
modified |
7.2 KB |
2025-12-22 14:43 |
| 396 |
1 |
modified |
7.1 KB |
2025-12-22 08:47 |
← Zurück zur Übersicht