Add admin views for quick replies, settings, and ticket details

- Created `quick-replies.blade.php` for managing quick replies.
- Added `settings.blade.php` for admin settings management.
- Implemented `ticket-show.blade.php` to display ticket details.
- Introduced `timeline-card.blade.php` component for displaying timeline information.

Enhance quick reply management functionality

- Developed `quick-reply-manager.blade.php` for creating and editing quick replies.
- Integrated Livewire for dynamic interaction and validation.

Implement settings page for AI configuration

- Created `settings-page.blade.php` for managing AI settings, including prompts and provider instances.
- Added functionality for managing models and embeddings.

Add ticket show functionality with real-time updates

- Implemented ticket details view with processing status and tool call logs.
- Added support for displaying article suggestions and error messages.

Create unit tests for AI classifier and domain info tool

- Added `AIClassifierServiceTest.php` to validate AI classifier functionality.
- Implemented `DomainInfoToolTest.php` for domain parameter validation.
- Created `OxxaClientTest.php` to test API interactions and password hashing.
This commit is contained in:
SitiWeb
2026-04-30 01:50:21 +02:00
parent 01aa115a49
commit f939133fe0
103 changed files with 4721 additions and 245 deletions

View File

@@ -4,25 +4,43 @@ namespace App\Repositories;
use App\DTOs\ArticleCandidateDTO;
use App\Models\Article;
use App\Models\ArticleChunk;
use App\Repositories\Contracts\ArticleRepositoryInterface;
use Illuminate\Support\Facades\DB;
class ArticleRepository implements ArticleRepositoryInterface
{
public function findSimilarByEmbedding(array $embedding, int $limit = 5): array
public function findSimilarByEmbedding(array $embedding, int $limit = 5, array $embeddingContext = []): array
{
$vector = '['.implode(',', array_map(static fn ($value) => (float) $value, $embedding)).']';
$rows = Article::query()
->select('articles.*')
->selectRaw('embedding <=> ?::vector as distance', [$vector])
$chunkDistances = ArticleChunk::query()
->selectRaw('article_id, MIN(embedding <=> ?::vector) as distance', [$vector])
->whereNotNull('embedding')
->orderByRaw('embedding <=> ?::vector', [$vector])
->when($embeddingContext !== [], function ($query) use ($embeddingContext) {
$query
->where('embedding_provider_instance_id', $embeddingContext['provider_instance_id'] ?? null)
->where('embedding_model', $embeddingContext['embedding_model'] ?? null);
})
->groupBy('article_id')
->orderByRaw('MIN(embedding <=> ?::vector)', [$vector])
->limit($limit)
->get();
return $rows
->map(fn (Article $article) => ArticleCandidateDTO::fromArticle($article, (float) $article->distance))
if ($chunkDistances->isEmpty()) {
return [];
}
$distanceByArticleId = $chunkDistances->pluck('distance', 'article_id');
$articleIds = $chunkDistances->pluck('article_id')->all();
$articles = Article::query()
->whereIn('id', $articleIds)
->get()
->sortBy(fn (Article $a) => (float) ($distanceByArticleId[$a->id] ?? 1))
->values();
return $articles
->map(fn (Article $article) => ArticleCandidateDTO::fromArticle($article, (float) ($distanceByArticleId[$article->id] ?? 1)))
->all();
}
}
}

View File

@@ -7,5 +7,5 @@ use App\DTOs\ArticleCandidateDTO;
interface ArticleRepositoryInterface
{
/** @return array<ArticleCandidateDTO> */
public function findSimilarByEmbedding(array $embedding, int $limit = 5): array;
}
public function findSimilarByEmbedding(array $embedding, int $limit = 5, array $embeddingContext = []): array;
}