toga-ai 1.0.475 → 1.0.476
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -95,6 +95,20 @@ so they displayed **double-encoded** (e.g. "Grand & Toy"). `vocabulary/post.
|
|
|
95
95
|
`html_entity_decode()`s values on save so the DB stores **raw** text; `vocabulary/get.php`
|
|
96
96
|
already escapes once at display. Do not re-add encoding on save.
|
|
97
97
|
|
|
98
|
+
**Per-model scoping is STRICT — each model owns its own copy (fixed 2026-07-29).**
|
|
99
|
+
`vocabulary/get.php` reads **strictly** `WHERE c_trueAiModelId = <selectedModel>` with **no
|
|
100
|
+
NULL fallback** — `c_trueAiModelId` is NOT "NULL = global". The per-model scoping (tools #5)
|
|
101
|
+
shipped with **no data backfill**: `Team/2026-07-27a` added `c_trueAiModelId` to
|
|
102
|
+
`TranscriptPromptTerms`/`TranscriptReplacements`/`TranscriptPromptTemplate` as `DEFAULT NULL`
|
|
103
|
+
and nothing populated it, so every existing row was NULL and selecting **any** model showed an
|
|
104
|
+
**empty page** (confirmed prod: 122 terms / 110 replacements / 1 template, all NULL). Fixed by
|
|
105
|
+
`dbchanges2 Team/2026-07-29a` (PR dbchanges2 #444): the existing NULL base rows are assigned to
|
|
106
|
+
**dev-core**, then that base is **replicated into every other active AiModel** (each an
|
|
107
|
+
independent, editable copy with its own fresh UUID). New models added later are **NOT
|
|
108
|
+
auto-seeded** — a known limitation, since a Team migration can't enumerate `Client_True`
|
|
109
|
+
models dynamically (see the separate-clusters gotcha in the
|
|
110
|
+
[worker2 pipeline doc](../../../2.0/apps/worker2/features/talos-transcript-ingestion.md#gotchas--known-issues)).
|
|
111
|
+
|
|
98
112
|
### `/talos/knowledge-bases` — KB list + inline rename
|
|
99
113
|
|
|
100
114
|
The list/admin page (`get.php`) for Talos knowledge bases. Unlike KB Documents (whose KB
|
|
@@ -383,6 +397,16 @@ Refactor from a single hard-coded `development-team` KB to per-AI-model, data-dr
|
|
|
383
397
|
|
|
384
398
|
## Change history
|
|
385
399
|
|
|
400
|
+
- 2026-07-29 — **Fixed the empty Vocabulary page: the per-model scoping shipped with no data
|
|
401
|
+
backfill.** `vocabulary/get.php` reads strictly `WHERE c_trueAiModelId = <model>` (no NULL
|
|
402
|
+
fallback), but `Team/2026-07-27a` added the column `DEFAULT NULL` and nothing populated it —
|
|
403
|
+
so every model showed empty (prod: 122 terms / 110 replacements / 1 template, all NULL). New
|
|
404
|
+
migration `dbchanges2 Team/2026-07-29a - TranscriptVocabularyBackfillAllModels.sql` (PR
|
|
405
|
+
dbchanges2 #444) assigns the base NULL rows to **dev-core** then replicates that base into
|
|
406
|
+
**every active AiModel** as an independent editable copy (fresh UUID each). Decision: vocabulary
|
|
407
|
+
is **strict per-model, each model owns its own copy** (not NULL=global, not shared);
|
|
408
|
+
later-added models are **not auto-seeded** (Team migrations can't enumerate `Client_True`
|
|
409
|
+
models). (ajean)
|
|
386
410
|
- 2026-07-29 — **Refactored KB Documents + Vocabulary to per-AI-model, data-driven scoping**
|
|
387
411
|
(from the single hard-coded `development-team` KB). KB Documents is now an
|
|
388
412
|
`AiModel → VectorIndex → documents` cascade; every document action is tenant-guarded
|
|
@@ -6,8 +6,8 @@ project: Database Changes
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: architecture
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-07-
|
|
10
|
-
owners: [jcardinal, mhammontree, bala]
|
|
9
|
+
updated: 2026-07-29
|
|
10
|
+
owners: [jcardinal, mhammontree, bala, ajean]
|
|
11
11
|
files:
|
|
12
12
|
- Core/
|
|
13
13
|
- Client/
|
|
@@ -293,6 +293,44 @@ tables with a `UNIQUE(parentId, childId)` (e.g. `ContactAddresses`) can't be re-
|
|
|
293
293
|
null-then-delete those. Then delete the non-keeper parents via the materialized double-nested
|
|
294
294
|
subquery. Used in `Client_Prudential/2026-07-07 - Contact Dedup Merge.sql`.
|
|
295
295
|
|
|
296
|
+
## Self-referencing INSERT...SELECT guard — wrap the existing-set subquery in a derived table
|
|
297
|
+
|
|
298
|
+
The same MySQL restriction (and a second, subtler hazard) applies when an
|
|
299
|
+
`INSERT ... SELECT` into a table must be made **idempotent / guarded per-key against that
|
|
300
|
+
SAME table** — e.g. "insert a base row for every model that doesn't already have one."
|
|
301
|
+
|
|
302
|
+
Reading the target table directly in the guard subquery fails two ways:
|
|
303
|
+
|
|
304
|
+
1. **Error 1093** — "You can't specify target table 'X' for update in FROM clause," exactly as
|
|
305
|
+
with the self-referencing DELETE above.
|
|
306
|
+
2. **A guard that flips mid-statement.** Even where MySQL allows it, a correlated
|
|
307
|
+
`NOT IN (SELECT … FROM <target>)` guard can be re-evaluated **as rows are inserted**, so the
|
|
308
|
+
set it checks against grows during the statement and the guard's answer changes partway
|
|
309
|
+
through — producing partial or duplicated inserts.
|
|
310
|
+
|
|
311
|
+
Wrap the existing-set subquery in a **derived table** (with `DISTINCT` or `LIMIT`) so MySQL
|
|
312
|
+
**materializes it once** against the pre-INSERT state:
|
|
313
|
+
|
|
314
|
+
```sql
|
|
315
|
+
-- CORRECT — the derived table snapshots the "already present" set before any insert
|
|
316
|
+
INSERT INTO TranscriptPromptTerms (uuid, category, term, isActive, c_trueAiModelId)
|
|
317
|
+
SELECT UUID(), t.category, t.term, t.isActive, m.mid
|
|
318
|
+
FROM <base rows> t
|
|
319
|
+
CROSS JOIN <active model ids> m
|
|
320
|
+
WHERE m.mid NOT IN (
|
|
321
|
+
SELECT mid FROM (
|
|
322
|
+
SELECT DISTINCT c_trueAiModelId AS mid
|
|
323
|
+
FROM TranscriptPromptTerms
|
|
324
|
+
WHERE c_trueAiModelId IN (<active model ids>)
|
|
325
|
+
) existing
|
|
326
|
+
);
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
The extra `SELECT … FROM ( … ) existing` layer both dodges error 1093 and pins the guard to a
|
|
330
|
+
one-time snapshot, so a per-key idempotent backfill stays correct. Used in
|
|
331
|
+
`Team/2026-07-29a - TranscriptVocabularyBackfillAllModels.sql` to replicate the dev-core base
|
|
332
|
+
vocabulary into every other active AI model exactly once.
|
|
333
|
+
|
|
296
334
|
## Relationship to the rest of 2.0
|
|
297
335
|
|
|
298
336
|
`dbchanges2` is registered as a **2.0 core repo** (`role: core` in `registry.json`) — it is
|
|
@@ -303,6 +341,11 @@ defined in `2.0/apps/_underscore/architecture.md`, and its change files create/a
|
|
|
303
341
|
tables that `_Model_*` classes map to.
|
|
304
342
|
|
|
305
343
|
## Change history
|
|
344
|
+
- 2026-07-29 — Added *Self-referencing INSERT...SELECT guard — wrap the existing-set subquery in
|
|
345
|
+
a derived table*: an `INSERT...SELECT` guarded per-key against its own target must wrap the
|
|
346
|
+
existing-set subquery in a derived table (DISTINCT/LIMIT) so MySQL materializes it once —
|
|
347
|
+
avoids error 1093 and stops the guard flipping mid-statement as rows are inserted. First used
|
|
348
|
+
in `Team/2026-07-29a - TranscriptVocabularyBackfillAllModels.sql` (PR #444). (ajean)
|
|
306
349
|
- 2026-07-21 — Added rule #6 to *Adding a new change*: never seed a `uuid` column with MySQL
|
|
307
350
|
`UUID()` (time/MAC-based v1, violates the 2.0 v4-UUID standard); pre-generate a v4 UUID
|
|
308
351
|
literal and hardcode it in `VALUES`. `Core/2026-05-08 … CronJobs_WorkerCleanup_insert` is a
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
| [Startech Webhook Handler (worker2)](features/startech-webhook-handler.md) | Receives inbound webhook events from Startech (Easeedesk) and creates or updates the corresponding ticket in TOGA 2.0. | worker2/Worker/Startech.php |
|
|
30
30
|
| [Talos (TOGa IQ) Meeting-Notes Integration & Token Auto-Refresh (consumer)](features/talos-meeting-notes-integration.md) | How a **dev tool / agent consumes Talos (TOGa IQ)** to query the team meeting-notes corpus programmatically. | .claude/skills/plan-ticket/scripts/talos.js |
|
|
31
31
|
| [Talos Pricing Automation (worker2 Cron — AWS Actuals, Calibration, Monthly Report)](features/talos-pricing-automation.md) | The worker2 half of the **Talos Pricing Platform** (see the talos `pricing-cogs-model` and tools `talos-pricing-ui` docs for the other halves). | worker2/Worker/Talos/Pricing.php, worker2/Database/TalosPricingCrons.sql |
|
|
32
|
-
| [Talos Transcript Ingestion Pipeline (worker2 → AWS Bedrock KBs)](features/talos-transcript-ingestion.md) | > **DB-DRIVEN AI-MODEL ROUTING (2026-07-29).** Which knowledge base a transcript is cleaned > into is now decided by the **meeting organizer's "home" AI model** | worker2/Worker/Team/Transcripts.php, worker2/bin/sync-knowledge-bases.php, worker2/Config/production.ini, worker2/Database/TeamsTranscriptExports.sql, dbchanges2/Client_True/2026-07-27a - TranscriptAiModelRoutingColumns.sql, dbchanges2/Client_True/2026-07-27b - TranscriptAiModelRoutingData.sql, dbchanges2/Team/2026-07-27a - TranscriptVocabularyAiModelScope.sql, dbchanges2/Team/2026-06-30a, dbchanges2/Team/2026-06-30b, dbchanges2/Team/2026-06-30c, dbchanges2/Team/2026-06-30d, dbchanges2/Team/2026-06-30e, dbchanges2/Core/2026-06-30a, dbchanges2/Core/2026-07-02a, dbchanges2/Team/2026-07-02a, dbchanges2/Team/2026-07-08a, dbchanges2/Team/2026-07-09a, dbchanges2/Team/2026-07-10a, dbchanges2/Team/2026-07-28a - TranscriptProcessingRetryAttempts.sql, dbchanges2/Team/2026-07-28b - TranscriptPromptTemplateConverseModel.sql, dbchanges2/Core/2026-07-28a - TeamsTranscriptRetryCron.sql |
|
|
32
|
+
| [Talos Transcript Ingestion Pipeline (worker2 → AWS Bedrock KBs)](features/talos-transcript-ingestion.md) | > **DB-DRIVEN AI-MODEL ROUTING (2026-07-29).** Which knowledge base a transcript is cleaned > into is now decided by the **meeting organizer's "home" AI model** | worker2/Worker/Team/Transcripts.php, worker2/bin/sync-knowledge-bases.php, worker2/Config/production.ini, worker2/Database/TeamsTranscriptExports.sql, dbchanges2/Client_True/2026-07-27a - TranscriptAiModelRoutingColumns.sql, dbchanges2/Client_True/2026-07-27b - TranscriptAiModelRoutingData.sql, dbchanges2/Team/2026-07-27a - TranscriptVocabularyAiModelScope.sql, dbchanges2/Team/2026-07-29a - TranscriptVocabularyBackfillAllModels.sql, dbchanges2/Team/2026-06-30a, dbchanges2/Team/2026-06-30b, dbchanges2/Team/2026-06-30c, dbchanges2/Team/2026-06-30d, dbchanges2/Team/2026-06-30e, dbchanges2/Core/2026-06-30a, dbchanges2/Core/2026-07-02a, dbchanges2/Team/2026-07-02a, dbchanges2/Team/2026-07-08a, dbchanges2/Team/2026-07-09a, dbchanges2/Team/2026-07-10a, dbchanges2/Team/2026-07-28a - TranscriptProcessingRetryAttempts.sql, dbchanges2/Team/2026-07-28b - TranscriptPromptTemplateConverseModel.sql, dbchanges2/Core/2026-07-28a - TeamsTranscriptRetryCron.sql |
|
|
33
33
|
| [Team Sprint Management & Reporting](features/team-sprint-management.md) | `_Worker_Team_Sprint` (file `Worker/Team/Sprint.php`) is the engine behind TOGA's internal **development-sprint process and reporting**. | worker2/Worker/Team/Sprint.php, _underscore/Model/Team/Sprint.php, dbchanges2/Core/CronJobs (SprintLockScheduled seed) |
|
|
34
34
|
| [Teams Meeting Transcript Export](features/teams-transcript-export.md) | > **SUPERSEDED (2026-07-09) — the S3-staging model below is history.** `Export` is now a thin > **GRAPH-DIRECT** cron poller: it no longer archives raw VTT to ` | worker2/Worker/Team/Transcripts.php, worker2/Config/production.ini, worker2/Database/TeamsTranscriptExports.sql, dbchanges2/Core/2026-06-18a - Teams Transcript Export schedule.sql |
|
|
35
35
|
| [VAPI Webhook Handler (worker2 — AI-BDR end-of-call processing)](features/vapi-webhook-handler.md) | `_Worker_Vapi` ([worker2/Worker/Vapi.php](worker2/Worker/Vapi.php)) is the **PHP side of the AI-BDR call loop** — the webhook that receives VAPI's end-of-call r | worker2/Worker/Vapi.php, worker2/Worker/Ai/Bdr/Vapi.php |
|
|
@@ -16,6 +16,7 @@ files:
|
|
|
16
16
|
- dbchanges2/Client_True/2026-07-27a - TranscriptAiModelRoutingColumns.sql
|
|
17
17
|
- dbchanges2/Client_True/2026-07-27b - TranscriptAiModelRoutingData.sql
|
|
18
18
|
- dbchanges2/Team/2026-07-27a - TranscriptVocabularyAiModelScope.sql
|
|
19
|
+
- dbchanges2/Team/2026-07-29a - TranscriptVocabularyBackfillAllModels.sql
|
|
19
20
|
- dbchanges2/Team/2026-06-30a
|
|
20
21
|
- dbchanges2/Team/2026-06-30b
|
|
21
22
|
- dbchanges2/Team/2026-06-30c
|
|
@@ -297,9 +298,17 @@ spacing (Bedrock allows only one in-flight ingestion per KB, so parallel syncs r
|
|
|
297
298
|
index selection in `Process`).
|
|
298
299
|
- **`Team.TranscriptPromptTerms` / `TranscriptReplacements` / `TranscriptPromptTemplate`** gained
|
|
299
300
|
**`c_trueAiModelId`** (soft ref → `Client_True.AiModels.id`, dbchanges2 `Team/2026-07-27a`) so
|
|
300
|
-
the transcript-cleanup vocabulary is scoped per AI model.
|
|
301
|
+
the transcript-cleanup vocabulary is scoped per AI model. **`c_trueAiModelId` is STRICT
|
|
302
|
+
per-model, NOT "NULL = global"** — each model owns its own independent copy. The `2026-07-27a`
|
|
303
|
+
add shipped `DEFAULT NULL` with no backfill, so `dbchanges2 Team/2026-07-29a` (PR #444) seeds
|
|
304
|
+
it: the base NULL rows → **dev-core**, then that base is replicated into every active AiModel
|
|
305
|
+
(fresh UUID per copy). Later-added models are **not** auto-seeded (a Team migration can't
|
|
306
|
+
enumerate `Client_True` models — see the separate-clusters gotcha).
|
|
301
307
|
- The `2026-07-27b` data backfill maps existing dev emails → dev-core model, `ecastellucci` →
|
|
302
308
|
operations model, and each VectorIndex's S3 target.
|
|
309
|
+
- **Active `Client_True.AiModels` (confirmed prod 2026-07-29):** id 1 `hr`, 2 `dev-core`
|
|
310
|
+
("Talos DevCore"), 3 `one`, 4 `sales`, 6 `legal`, 7 `contact-center`, 8 `operations`,
|
|
311
|
+
9 `executive`, 10 `sales-demo`; id 5 `tech-support` is **INACTIVE**.
|
|
303
312
|
- **These `c_`-prefixed columns needed ZERO `_underscore`/model-layer changes** — see the
|
|
304
313
|
framework note under Gotchas.
|
|
305
314
|
|
|
@@ -453,9 +462,37 @@ part of the ingestion loop** (raw reads removed). Credential values live only in
|
|
|
453
462
|
- **Cross-ACCOUNT S3.** `togaiq` (us-east-1) approved/archive writes use the `[talos]` key;
|
|
454
463
|
`CopyObject` across accounts is impossible, so archive is get(in-memory)+put. (The old
|
|
455
464
|
toga-private cross-account read is no longer in the loop.)
|
|
465
|
+
- **The `Team` DB and `Client_True` live on SEPARATE clusters** (Team on the core cluster,
|
|
466
|
+
`Client_True` on the client cluster). Therefore a **Team migration cannot resolve a
|
|
467
|
+
`Client_True.AiModels.id` via a cross-DB subquery** — the two are not queryable in one
|
|
468
|
+
statement. This is why `c_trueAiModelId` is a **soft cross-DB ref with no FK** (nothing to
|
|
469
|
+
validate in-DB) and why any Team-side migration that needs a model id must **hardcode the id
|
|
470
|
+
literal** and document the resolving query + date in the file header (as
|
|
471
|
+
`Team/2026-07-29a` does). It is also why later-added models can't be auto-seeded with
|
|
472
|
+
vocabulary from a Team migration.
|
|
473
|
+
- **worker2 reads the cleanup vocabulary GLOBALLY — it is NOT yet scoped by model.**
|
|
474
|
+
`_Worker_Team_Transcripts::loadActiveTemplate()` / `loadPromptTerms()` /
|
|
475
|
+
`loadActiveReplacements()` filter only `WHERE isActive = 1`, with **no `c_trueAiModelId`
|
|
476
|
+
scope**. So today `c_trueAiModelId` is consumed **only by the tools `/talos/vocabulary` admin
|
|
477
|
+
UI**, not by transcript cleaning — the worker cleans every transcript with whatever the
|
|
478
|
+
**global** set returns. **FOLLOW-UP (not done):** scope these three loaders by the transcript's
|
|
479
|
+
`aiModelId`; once models have divergent vocabulary the global read will clean every transcript
|
|
480
|
+
with the merged/global set instead of the routed model's copy.
|
|
456
481
|
|
|
457
482
|
## Change history
|
|
458
483
|
|
|
484
|
+
- 2026-07-29 — **Backfilled the per-model transcript vocabulary and recorded three durable
|
|
485
|
+
facts.** `c_trueAiModelId` (added `DEFAULT NULL` by `Team/2026-07-27a`) was never populated, so
|
|
486
|
+
the tools `/talos/vocabulary` UI (which reads it strictly, no NULL fallback) showed empty for
|
|
487
|
+
every model. `dbchanges2 Team/2026-07-29a` (PR #444) assigns the base NULL rows to **dev-core**
|
|
488
|
+
and replicates that base into every active AiModel (independent copy, fresh UUID). Decision:
|
|
489
|
+
vocabulary is **strict per-model** (not NULL=global, not shared); later-added models are not
|
|
490
|
+
auto-seeded. Recorded: (1) **Team and `Client_True` are on separate clusters** → Team migrations
|
|
491
|
+
can't cross-DB-resolve a `Client_True.AiModels.id` and must hardcode the id literal; (2)
|
|
492
|
+
**worker2 reads vocabulary globally** (`isActive=1` only, no `c_trueAiModelId` scope) — the
|
|
493
|
+
column is consumed only by the tools UI today, with a follow-up to scope the worker's three
|
|
494
|
+
vocabulary loaders by the transcript's `aiModelId`; (3) the full active `Client_True.AiModels`
|
|
495
|
+
set (id 5 `tech-support` inactive). (ajean)
|
|
459
496
|
- 2026-07-29 — **DB-driven per-user AI-model routing.** `Export` now polls
|
|
460
497
|
`Client_True.Users WHERE c_transcriptAiModelId IS NOT NULL AND isActive=1` (the `[teams]`
|
|
461
498
|
organizer id/email lists were removed) and threads `aiModelId` into each `Process` job;
|
package/package.json
CHANGED