Mise en place de la sélection des source que l'on souhaite que ce soit la partie RAG ou la partie analyse approfondie : on est plus obligé d'envoyer tous les PDFs qu'on a dans la partie atelier PDF + IA.
Les réponses ne ce baseront que sur les sources que l'on aura cocher au préalable
This commit is contained in:
@@ -20,13 +20,30 @@
|
||||
@if (uploadError) {
|
||||
<p class="nbd-upload-error">{{ uploadError }}</p>
|
||||
}
|
||||
@if (readySourceCount > 1) {
|
||||
<p class="nbd-sources-hint"
|
||||
[class.partial]="selectedSourceIds.size < readySourceCount">
|
||||
{{ selectedSourceIds.size }}/{{ readySourceCount }} source(s) utilisée(s) par le chat et l'analyse approfondie
|
||||
</p>
|
||||
}
|
||||
@for (s of sources; track s) {
|
||||
<div class="nbd-source">
|
||||
<lucide-icon
|
||||
[img]="s.status === 'READY' ? CheckCircle2 : (s.status === 'FAILED' ? AlertCircle : Loader)"
|
||||
[size]="14"
|
||||
[class.ok]="s.status === 'READY'" [class.fail]="s.status === 'FAILED'" [class.busy]="s.status === 'INDEXING'">
|
||||
</lucide-icon>
|
||||
<div class="nbd-source" [class.nbd-source--off]="s.status === 'READY' && !isSelected(s)">
|
||||
@if (s.status === 'READY') {
|
||||
<input
|
||||
type="checkbox"
|
||||
class="nbd-source-check"
|
||||
[checked]="isSelected(s)"
|
||||
(change)="toggleSource(s)"
|
||||
[title]="isSelected(s)
|
||||
? 'Source utilisée par le chat — décocher pour l\'exclure'
|
||||
: 'Source ignorée par le chat — cocher pour l\'inclure'" />
|
||||
} @else {
|
||||
<lucide-icon
|
||||
[img]="s.status === 'FAILED' ? AlertCircle : Loader"
|
||||
[size]="14"
|
||||
[class.fail]="s.status === 'FAILED'" [class.busy]="s.status === 'INDEXING'">
|
||||
</lucide-icon>
|
||||
}
|
||||
<div class="nbd-source-info">
|
||||
<div class="nbd-source-name">{{ s.filename }}</div>
|
||||
<div class="nbd-source-meta">
|
||||
|
||||
@@ -39,12 +39,36 @@
|
||||
}
|
||||
.nbd-upload-error { color: #e88; font-size: 0.8rem; margin: 0.2rem 0; }
|
||||
|
||||
// Compteur de sources actives — ambre quand une partie est décochée, pour
|
||||
// rappeler que le chat ne voit pas tout.
|
||||
.nbd-sources-hint {
|
||||
font-size: 0.72rem;
|
||||
color: var(--color-text-muted, #9aa0aa);
|
||||
margin: 0.1rem 0 0.2rem;
|
||||
|
||||
&.partial { color: #fbbf24; }
|
||||
}
|
||||
|
||||
.nbd-source {
|
||||
display: flex; align-items: flex-start; gap: 0.5rem; padding: 0.5rem;
|
||||
border-radius: 7px; background: rgba(255,255,255,0.03);
|
||||
lucide-icon.ok { color: #6bd08a; }
|
||||
lucide-icon.fail { color: #e88; }
|
||||
lucide-icon.busy { color: #e0c074; }
|
||||
|
||||
// Case « source utilisée par le chat » (sources prêtes uniquement).
|
||||
.nbd-source-check {
|
||||
margin: 2px 0 0;
|
||||
accent-color: #6bd08a;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
// Source décochée : grisée = hors du périmètre du chat et de l'analyse.
|
||||
&.nbd-source--off {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.nbd-source-info { flex: 1; min-width: 0; }
|
||||
.nbd-source-name { font-size: 0.85rem; font-weight: 500; word-break: break-word; }
|
||||
.nbd-source-meta { font-size: 0.72rem; color: var(--color-text-muted, #9aa0aa); }
|
||||
|
||||
@@ -137,13 +137,54 @@ export class NotebookDetailComponent implements OnInit {
|
||||
this.detail = d;
|
||||
this.sources = d.sources ?? [];
|
||||
this.messages = d.messages ?? [];
|
||||
this.syncSelection();
|
||||
},
|
||||
error: () => this.back()
|
||||
});
|
||||
}
|
||||
|
||||
reloadSources(): void {
|
||||
this.service.get(this.notebookId).subscribe({ next: (d) => this.sources = d.sources ?? [] });
|
||||
this.service.get(this.notebookId).subscribe({ next: (d) => {
|
||||
this.sources = d.sources ?? [];
|
||||
this.syncSelection();
|
||||
} });
|
||||
}
|
||||
|
||||
// --- Sélection des sources utilisées par le chat -------------------------
|
||||
// Par défaut tout est coché ; décocher permet de limiter une question (et
|
||||
// surtout une analyse approfondie, coûteuse en requêtes) à certains PDF.
|
||||
|
||||
/** IDs des sources cochées (sous-ensemble des sources READY). */
|
||||
selectedSourceIds = new Set<string>();
|
||||
/** IDs déjà vus — pour ne cocher par défaut que les NOUVELLES sources. */
|
||||
private knownSourceIds = new Set<string>();
|
||||
|
||||
/** Aligne la sélection sur la liste courante : nouvelles sources cochées par
|
||||
* défaut, sources supprimées retirées, choix de l'utilisateur préservés. */
|
||||
private syncSelection(): void {
|
||||
const readyIds = new Set(this.sources.filter(s => s.status === 'READY').map(s => s.id));
|
||||
for (const id of readyIds) {
|
||||
if (!this.knownSourceIds.has(id)) {
|
||||
this.selectedSourceIds.add(id);
|
||||
this.knownSourceIds.add(id);
|
||||
}
|
||||
}
|
||||
for (const id of [...this.selectedSourceIds]) {
|
||||
if (!readyIds.has(id)) this.selectedSourceIds.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
isSelected(s: NotebookSource): boolean {
|
||||
return this.selectedSourceIds.has(s.id);
|
||||
}
|
||||
|
||||
toggleSource(s: NotebookSource): void {
|
||||
if (this.selectedSourceIds.has(s.id)) this.selectedSourceIds.delete(s.id);
|
||||
else this.selectedSourceIds.add(s.id);
|
||||
}
|
||||
|
||||
get readySourceCount(): number {
|
||||
return this.sources.filter(s => s.status === 'READY').length;
|
||||
}
|
||||
|
||||
// --- Sources ---
|
||||
@@ -181,7 +222,7 @@ export class NotebookDetailComponent implements OnInit {
|
||||
this.messages.push(assistant);
|
||||
this.sending = true;
|
||||
|
||||
this.service.streamChat(this.notebookId, text, deep).subscribe({
|
||||
this.service.streamChat(this.notebookId, text, deep, [...this.selectedSourceIds]).subscribe({
|
||||
next: (ev) => {
|
||||
if (ev.type === 'token') { this.deepProgress = null; assistant.content += ev.value; }
|
||||
else if (ev.type === 'sources') assistant.sources = ev.sources;
|
||||
|
||||
Reference in New Issue
Block a user