Skip to content

Commit f2c2563

Browse files
authored
[sc-14909] Added a prototype of the ai chat assistant (#2927)
* [sc-14909] Added a prototype of the ai chat assistant * [sc-14909] Changed strategy to work with cross-context questions. Enhanced pages.json * [sc-14909] Improved ux, polished ui * [sc-14909] Do not close the chat when user click on a link in it * [sc-14909] Used docsKb/ask endpoint for second call, hid the feature behind the flag * [sc-14909] Added audit_metadata with orign and accountId to the /ask requests
1 parent 5e01eed commit f2c2563

21 files changed

Lines changed: 1668 additions & 468 deletions

.github/skills/angular-patterns/SKILL.md

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -748,31 +748,32 @@ is suppressed with `eslint-disable`. Do not "fix" it by changing to path aliases
748748

749749
## Common Mistakes to Avoid
750750

751-
| Mistake | Correct approach |
751+
| `*ngIf="cond"` / `*ngFor="let x of list"` | `@if (cond) { ... }` / `@for (x of list; track x.id) { ... }` |
752+
| Mistake | Correct approach |
752753
| ---------------------------------------------------------------- | ----------------------------------------------------------------------- |
753-
| Nested ternary (`a ? b : c ? d : e`) | Use `if / else if` blocks — flat branching is always clearer |
754-
| `if (cond) { doX(); } else { doY(); }` when one branch returns | Use guard-clause / early-return: `if (!cond) { doY(); return; } doX();` |
755-
| `constructor(private svc: Service)` | `private svc = inject(Service)` |
756-
| `@Input() value: string` | `value = input.required<string>()` |
757-
| `@Output() changed = new EventEmitter<string>()` | `changed = output<string>()` |
758-
| `standalone: true` explicitly written | Omit it — defaults to `true` in Angular 19+ |
759-
| `styleUrls: ['./foo.component.scss']` (array) | `styleUrl: './foo.component.scss'` (singular string) |
760-
| Empty `.scss` file committed for a component | Omit `styleUrl` entirely when no styles are needed |
761-
| Page component files at the module root | Put each page component in a named subfolder (`feature-name/`) |
762-
| Missing `changeDetection: ChangeDetectionStrategy.OnPush` | Always include it |
763-
| `effect()` to derive values | Use `computed()` for derived state |
764-
| `this.signal.set(x)` inside `computed()` | Never write inside computed |
765-
| `inject()` inside `ngOnInit` | Move to property initializer or constructor |
766-
| Plain property read in template (`isLoading = false`) | `isLoading = signal(false)` — OnPush won't re-render otherwise |
767-
| `this.cdr.markForCheck()` after every mutation | Convert state to `signal()` and remove `ChangeDetectorRef` |
768-
| `[(value)]="name"` with nsi-/pa- components | `[value]="name()" (valueChange)="name.set($event)"` |
769-
| `arr[i] = val` on a signal array | `const copy = [...arr()]; copy[i] = val; arr.set(copy)` |
770-
| `new Subject<void>()` as Tier 2 refresh trigger | `new BehaviorSubject<void>(undefined)` |
771-
| `toSignal(obs$)` inside a Tier 2 service | Use `tap((v) => this._sig.set(v))` in the pipeline |
772-
| Omit `shareReplay(1)` on service pipelines | Always include — prevents duplicate HTTP requests |
773-
| Omit `catchError` on service pipelines | Always include — otherwise errors permanently kill the stream |
774-
| Adding NgRx `signalStore` | Not used in this codebase — use one of the 3 tiers |
775-
| Forgetting `takeUntilDestroyed()` on subscriptions in components | Always unsubscribe |
754+
| Nested ternary (`a ? b : c ? d : e`) | Use `if / else if` blocks — flat branching is always clearer |
755+
| `if (cond) { doX(); } else { doY(); }` when one branch returns | Use guard-clause / early-return: `if (!cond) { doY(); return; } doX();` |
756+
| `constructor(private svc: Service)` | `private svc = inject(Service)` |
757+
| `@Input() value: string` | `value = input.required<string>()` |
758+
| `@Output() changed = new EventEmitter<string>()` | `changed = output<string>()` |
759+
| `standalone: true` explicitly written | Omit it — defaults to `true` in Angular 19+ |
760+
| `styleUrls: ['./foo.component.scss']` (array) | `styleUrl: './foo.component.scss'` (singular string) |
761+
| Empty `.scss` file committed for a component | Omit `styleUrl` entirely when no styles are needed |
762+
| Page component files at the module root | Put each page component in a named subfolder (`feature-name/`) |
763+
| Missing `changeDetection: ChangeDetectionStrategy.OnPush` | Always include it |
764+
| `effect()` to derive values | Use `computed()` for derived state |
765+
| `this.signal.set(x)` inside `computed()` | Never write inside computed |
766+
| `inject()` inside `ngOnInit` | Move to property initializer or constructor |
767+
| Plain property read in template (`isLoading = false`) | `isLoading = signal(false)` — OnPush won't re-render otherwise |
768+
| `this.cdr.markForCheck()` after every mutation | Convert state to `signal()` and remove `ChangeDetectorRef` |
769+
| `[(value)]="name"` with nsi-/pa- components | `[value]="name()" (valueChange)="name.set($event)"` |
770+
| `arr[i] = val` on a signal array | `const copy = [...arr()]; copy[i] = val; arr.set(copy)` |
771+
| `new Subject<void>()` as Tier 2 refresh trigger | `new BehaviorSubject<void>(undefined)` |
772+
| `toSignal(obs$)` inside a Tier 2 service | Use `tap((v) => this._sig.set(v))` in the pipeline |
773+
| Omit `shareReplay(1)` on service pipelines | Always include — prevents duplicate HTTP requests |
774+
| Omit `catchError` on service pipelines | Always include — otherwise errors permanently kill the stream |
775+
| Adding NgRx `signalStore` | Not used in this codebase — use one of the 3 tiers |
776+
| Forgetting `takeUntilDestroyed()` on subscriptions in components | Always unsubscribe |
776777

777778
---
778779

.github/skills/knowledge-sync/pages-yaml-sync.md renamed to .github/skills/knowledge-sync/pages-json-sync.md

Lines changed: 62 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
# Chat Advice — pages.yaml Build & Sync Guide
1+
# Chat Advice — pages.json Build & Sync Guide
22

3-
This document describes how to build and maintain the `pages.yaml` navigation document used
3+
This document describes how to build and maintain the `pages.json` navigation document used
44
by the chat advice widget. The file lives at:
55

6-
- `apps/dashboard/src/assets/chat/pages.yaml`
6+
- `apps/dashboard/src/assets/chat/pages.json`
77

8-
**Do not write pages.yaml entries from memory or routing tables alone.**
8+
**Do not write pages.json entries from memory or routing tables alone.**
99
The capabilities and summaries must be derived from the actual component code.
1010
Every entry that is written without reading the component will be wrong.
1111

@@ -14,9 +14,9 @@ Every entry that is written without reading the component will be wrong.
1414
## When to run this
1515

1616
- When a new route is added to `apps/dashboard`
17-
- When the staleness check reports routing files newer than `pages.yaml`:
17+
- When the staleness check reports routing files newer than `pages.json`:
1818
```bash
19-
find apps/dashboard/src/app -name "*routing*" -newer apps/dashboard/src/assets/chat/pages.yaml
19+
find apps/dashboard/src/app -name "*routing*" -newer apps/dashboard/src/assets/chat/pages.json
2020
```
2121
- When `knowledge-sync` detects changes in route-related files
2222

@@ -110,28 +110,44 @@ Key patterns to extract from templates:
110110

111111
---
112112

113-
### Step 4 — Write the YAML entry
114-
115-
```yaml
116-
- id: <kebab-case-stable-identifier>
117-
route: '/at/:account/...' # full route template, params as :placeholders
118-
title: '<resolved English page title>'
119-
summary: '<1–2 sentence description of what this page shows, in user language>'
120-
capabilities:
121-
- '<concrete thing user can do or see — start with a verb>'
122-
- '<another capability>'
123-
# aim for 3–6 capabilities; prefer concrete over vague
113+
### Step 4 — Write the JSON entry
114+
115+
Add an entry to the `pages` array in `apps/dashboard/src/assets/chat/pages.json`:
116+
117+
```json
118+
{
119+
"id": "<kebab-case-stable-identifier>",
120+
"route": "/at/:account/...",
121+
"title": "<resolved English page title>",
122+
"summary": "<1–3 sentences: PURPOSE first, then what user sees/achieves. Include user-facing terminology.>",
123+
"capabilities": ["<user task or goal — start with a verb, use natural language>", "<another capability>"]
124+
}
124125
```
125126

126127
**Rules for good entries:**
127128

128-
| Field | Rule |
129-
| -------------- | -------------------------------------------------------------------------------------------------- |
130-
| `id` | Stable kebab-case. Prefix with context: `account-`, `kb-`, `agent-`. Never change after first use. |
131-
| `route` | Full path from root. Keep params as `:placeholders`. |
132-
| `title` | Exactly the resolved English string shown as the page heading. |
133-
| `summary` | What the user _sees_ on this page, not what the page _is_ technically. 1–2 sentences. |
134-
| `capabilities` | Derived from actual template — buttons, actions, data displayed. Not guessed. |
129+
| Field | Rule |
130+
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
131+
| `id` | Stable kebab-case. Prefix with context: `account-`, `kb-`, `agent-`. Never change after first use. |
132+
| `route` | Full path from root. Keep params as `:placeholders`. |
133+
| `title` | Exactly the resolved English string shown as the page heading. |
134+
| `summary` | **Purpose-first**: start with what the user _achieves_ here, not what UI elements exist. Include synonyms and user-facing terminology (e.g. "answer history", "invite team members", "cloud sync"). 1–3 sentences. |
135+
| `capabilities` | User tasks and goals derived from the template — what the user _does_, not what button labels say. |
136+
137+
**Summary quality checklist — before writing:**
138+
139+
1. Does it say what the user _achieves_ (not just what's rendered)?
140+
2. Does it include terminology a user might use when asking a question? (e.g. "answer history", "billing", "embed widget", "change language")
141+
3. Would an AI reading only this entry understand when to recommend this page?
142+
4. Is it free of purely technical descriptions ("this page lists X with Y controls")?
143+
144+
**Bad vs good summary examples:**
145+
146+
| ❌ Bad (UI mechanics) | ✅ Good (purpose + user terminology) |
147+
| ---------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
148+
| "This page lists search activity logs with totals, column controls, filters, exports, and row drill-down." | "View the history of search queries and AI-generated answers for a Knowledge Box. Useful for auditing past interactions and reviewing specific answers." |
149+
| "This page lists saved agent sessions with search, date filters, pagination, and delete actions." | "Browse past conversation sessions with a Retrieval Agent. Each session contains the full history of questions and AI-generated answers." |
150+
| "This page manages the external sources available to the current retrieval agent." | "Manage the knowledge sources (data connections) that the agent uses when answering questions." |
135151

136152
---
137153

@@ -149,26 +165,30 @@ Key patterns to extract from templates:
149165

150166
**Correct entry:**
151167

152-
```yaml
153-
- id: account-home
154-
route: '/at/:account/manage/home'
155-
title: 'Consumption'
156-
summary: "Overview of your account's consumption: token usage and total query counts across all Knowledge Boxes."
157-
capabilities:
158-
- 'View token consumption based on activity, with period selector'
159-
- 'See the list of Knowledge Boxes and navigate directly to one'
160-
- 'View total query counts for the last 30 days, 12 months, and since account creation'
168+
```json
169+
{
170+
"id": "account-home",
171+
"route": "/at/:account/manage/home",
172+
"title": "Consumption",
173+
"summary": "Overview of your account's consumption: token usage and total query counts across all Knowledge Boxes.",
174+
"capabilities": [
175+
"View token consumption based on activity, with period selector",
176+
"See the list of Knowledge Boxes and navigate directly to one",
177+
"View total query counts for the last 30 days, 12 months, and since account creation"
178+
]
179+
}
161180
```
162181

163182
**What was wrong in the manually written version:**
164183

165-
```yaml
166-
# BAD — guessed from routing context, not component code:
167-
title: 'Account Home' # wrong: real title is "Consumption"
168-
summary: 'Overview of your account...' # vague and incorrect
169-
capabilities:
170-
- 'Create a new Knowledge Box' # WRONG: this page has no create action
171-
- 'Switch between KBs and Agents' # WRONG: this is not on this page
184+
```json
185+
{
186+
"id": "account-home",
187+
"route": "/at/:account/manage/home",
188+
"title": "Account Home",
189+
"summary": "Overview of your account...",
190+
"capabilities": ["Create a new Knowledge Box", "Switch between KBs and Agents"]
191+
}
172192
```
173193

174194
---
@@ -202,8 +222,8 @@ grep -oh "'[a-z._-]*' | translate" libs/common/src/lib/<feature>/*.html \
202222

203223
| Changed path | Documentation to update | Priority |
204224
| --------------------------------------------------- | -------------------------------------------------------------------------------------- | -------- |
205-
| `apps/dashboard/src/app/*routing*` | `apps/dashboard/src/assets/chat/pages.yaml` | HIGH |
206-
| `libs/common/src/lib/<feature>/**/*.component.html` | Check if feature maps to a pages.yaml entry; update `summary` and `capabilities` if so | MEDIUM |
225+
| `apps/dashboard/src/app/*routing*` | `apps/dashboard/src/assets/chat/pages.json` | HIGH |
226+
| `libs/common/src/lib/<feature>/**/*.component.html` | Check if feature maps to a pages.json entry; update `summary` and `capabilities` if so | MEDIUM |
207227
| `libs/common/src/lib/<feature>/**/*.component.ts` | Same as above — check for new public actions | LOW |
208228

209229
---

apps/dashboard/src/app/app.component.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,6 @@
55
<div #toastsContainer></div>
66
</div>
77
</aside>
8+
@if (isChatAdviceEnabled | async) {
9+
<stf-chat-advice-bubble></stf-chat-advice-bubble>
10+
}

apps/dashboard/src/app/app.component.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
STFSplashScreenService,
1818
STFUtils,
1919
UserService,
20+
FeaturesService,
2021
} from '@flaps/core';
2122
import { Subject } from 'rxjs';
2223
import { TranslateService as PaTranslateService } from '@guillotinaweb/pastanaga-angular';
@@ -40,6 +41,7 @@ export class AppComponent implements AfterViewInit, OnInit, OnDestroy {
4041
private unsubscribeAll: Subject<void>;
4142

4243
version: string | undefined;
44+
isChatAdviceEnabled = this.features.unstable.chatAdvice;
4345

4446
constructor(
4547
private user: UserService,
@@ -50,6 +52,7 @@ export class AppComponent implements AfterViewInit, OnInit, OnDestroy {
5052
private labelService: LabelsService,
5153
private paTranslate: PaTranslateService,
5254
private modalService: SisModalService,
55+
private features: FeaturesService,
5356
@Inject(DOCUMENT) private document: any,
5457
) {
5558
this.unsubscribeAll = new Subject();

apps/dashboard/src/app/app.module.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,15 @@ import { environment } from '../environments/environment';
1313

1414
import { AppRoutingModule } from './app-routing.module';
1515
import { FarewellModule } from '@nuclia/user';
16-
import { AccountModule, BaseModule, EntitiesModule, SelectAccountKbModule, TopbarModule, UploadModule } from '@flaps/common';
16+
import {
17+
AccountModule,
18+
BaseModule,
19+
ChatAdviceBubbleComponent,
20+
EntitiesModule,
21+
SelectAccountKbModule,
22+
TopbarModule,
23+
UploadModule,
24+
} from '@flaps/common';
1725
import { AppComponent } from './app.component';
1826

1927
// Load locales
@@ -67,6 +75,7 @@ const appModules = [
6775
OverlayModule,
6876
STFPipesModule,
6977
TopbarModule,
78+
ChatAdviceBubbleComponent,
7079
...appModules,
7180
TranslateModule.forRoot({
7281
loader: {

0 commit comments

Comments
 (0)