@praxisui/table 8.0.0-beta.91 → 8.0.0-beta.93
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.
- package/fesm2022/{praxisui-table-praxisui-table-BMNKohqK.mjs → praxisui-table-praxisui-table-DXpnCND9.mjs} +67 -17
- package/fesm2022/{praxisui-table-table-agentic-authoring-turn-flow-BZc3KmLG.mjs → praxisui-table-table-agentic-authoring-turn-flow-DUu4AIDF.mjs} +962 -63
- package/fesm2022/{praxisui-table-table-ai.adapter-D034N3RL.mjs → praxisui-table-table-ai.adapter-BWnL5QfP.mjs} +24 -13
- package/fesm2022/praxisui-table.mjs +1 -1
- package/package.json +10 -10
- package/types/praxisui-table.d.ts +4 -2
|
@@ -40,6 +40,10 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
40
40
|
if (pendingCompletion) {
|
|
41
41
|
return Promise.resolve(this.toTurnResult(this.compileAdapterResponse(pendingCompletion, request), request));
|
|
42
42
|
}
|
|
43
|
+
const pendingNumericBandStyle = this.completePendingNumericBandStyleClarification(request);
|
|
44
|
+
if (pendingNumericBandStyle) {
|
|
45
|
+
return Promise.resolve(this.toTurnResult(this.compileAdapterResponse(pendingNumericBandStyle, request), request));
|
|
46
|
+
}
|
|
43
47
|
const run = async () => {
|
|
44
48
|
await this.prepareAuthoringContext();
|
|
45
49
|
const runtimeState = this.optionalJsonObject(this.adapter.getRuntimeState?.());
|
|
@@ -313,15 +317,15 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
313
317
|
const guidedSelectedRecordsFilter = this.localGuidedSelectedRecordsFilterResponse(request, contextHints);
|
|
314
318
|
if (guidedSelectedRecordsFilter)
|
|
315
319
|
return guidedSelectedRecordsFilter;
|
|
320
|
+
const selectedRecordDerivedFilter = this.localSelectedRecordDerivedFilterResponse(request, contextHints);
|
|
321
|
+
if (selectedRecordDerivedFilter)
|
|
322
|
+
return selectedRecordDerivedFilter;
|
|
316
323
|
const declaredFilter = this.localDeclaredFilterApplyResponse(request, contextHints);
|
|
317
324
|
if (declaredFilter)
|
|
318
325
|
return declaredFilter;
|
|
319
326
|
const recommendedIntent = this.toRecord(contextHints?.['recommendedIntent']);
|
|
320
327
|
const intentId = this.stringValue(recommendedIntent?.['id'])
|
|
321
328
|
|| this.stringValue(contextHints?.['opportunityId']);
|
|
322
|
-
if (intentId === 'table-discover-capabilities' || intentId === 'table.capabilities.discover') {
|
|
323
|
-
return this.capabilityDiscoveryGuidedResponse(request, contextHints);
|
|
324
|
-
}
|
|
325
329
|
if (intentId === 'table-enable-advanced-filters' || intentId === 'table.filters.advanced.enable') {
|
|
326
330
|
return this.advancedFiltersRecommendedIntentResponse(request, contextHints);
|
|
327
331
|
}
|
|
@@ -380,58 +384,6 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
380
384
|
],
|
|
381
385
|
};
|
|
382
386
|
}
|
|
383
|
-
capabilityDiscoveryGuidedResponse(request, contextHints) {
|
|
384
|
-
const recommendations = Array.isArray(contextHints?.['availableRecommendations'])
|
|
385
|
-
? contextHints['availableRecommendations']
|
|
386
|
-
: [];
|
|
387
|
-
const optionPayloads = recommendations
|
|
388
|
-
.map((entry) => this.toRecord(entry))
|
|
389
|
-
.filter((entry) => !!entry && !!this.stringValue(entry['label']))
|
|
390
|
-
.slice(0, 6)
|
|
391
|
-
.map((entry, index) => this.capabilityDiscoveryOptionPayload(entry, index));
|
|
392
|
-
if (!optionPayloads.length) {
|
|
393
|
-
return {
|
|
394
|
-
type: 'info',
|
|
395
|
-
sessionId: request.sessionId,
|
|
396
|
-
message: 'Esta tabela já está em um estado estável. Posso orientar ajustes de filtros, colunas, ações, exportação e seleção conforme você precisar.',
|
|
397
|
-
};
|
|
398
|
-
}
|
|
399
|
-
return {
|
|
400
|
-
type: 'clarification',
|
|
401
|
-
sessionId: request.sessionId,
|
|
402
|
-
message: 'Encontrei estes próximos recursos úteis para esta tabela.',
|
|
403
|
-
questions: ['Por onde você quer começar?'],
|
|
404
|
-
optionPayloads,
|
|
405
|
-
warnings: [
|
|
406
|
-
'capability-discovery-local-guided-clarification',
|
|
407
|
-
'Resposta local gerada a partir do catalogo canonico de recommendedIntents da tabela; nao houve roteamento textual primario.',
|
|
408
|
-
],
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
capabilityDiscoveryOptionPayload(entry, index) {
|
|
412
|
-
const label = this.stringValue(entry['label']) || `Opção ${index + 1}`;
|
|
413
|
-
const contextHints = this.toRecord(entry['contextHints']) ?? {};
|
|
414
|
-
const responseContract = this.toRecord(contextHints['responseContract']);
|
|
415
|
-
return {
|
|
416
|
-
value: label,
|
|
417
|
-
label,
|
|
418
|
-
contextHints: {
|
|
419
|
-
...contextHints,
|
|
420
|
-
recommendedIntent: {
|
|
421
|
-
id: this.stringValue(entry['id']) || `table-discovery-option-${index + 1}`,
|
|
422
|
-
label,
|
|
423
|
-
sourceCandidateIds: Array.isArray(entry['sourceCandidateIds']) ? entry['sourceCandidateIds'] : [],
|
|
424
|
-
},
|
|
425
|
-
...(responseContract ? {} : { responseContract: this.toRecord(this.contextHintsForTurn?.['responseContract']) ?? undefined }),
|
|
426
|
-
presentation: {
|
|
427
|
-
kind: 'guided-option',
|
|
428
|
-
icon: this.stringValue(entry['icon']) || 'auto_awesome',
|
|
429
|
-
description: this.stringValue(entry['description']),
|
|
430
|
-
ctaLabel: this.stringValue(entry['requiresConfirmation']) === 'true' ? 'Revisar opção' : 'Usar opção',
|
|
431
|
-
},
|
|
432
|
-
},
|
|
433
|
-
};
|
|
434
|
-
}
|
|
435
387
|
selectedRecordsGuidedAnalysisResponse(request, contextHints) {
|
|
436
388
|
const selectedCount = this.extractSelectedRecordsCount(contextHints);
|
|
437
389
|
if (selectedCount <= 0) {
|
|
@@ -523,6 +475,55 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
523
475
|
],
|
|
524
476
|
};
|
|
525
477
|
}
|
|
478
|
+
localSelectedRecordDerivedFilterResponse(request, contextHints) {
|
|
479
|
+
if (this.selectedRecordsCountForTurn <= 0)
|
|
480
|
+
return null;
|
|
481
|
+
if (!this.runtimeOperationAllowed(contextHints, 'table.filter.apply'))
|
|
482
|
+
return null;
|
|
483
|
+
const normalizedPrompt = this.normalizeLabel(request.prompt ?? '');
|
|
484
|
+
if (!normalizedPrompt || !this.promptRequestsRuntimeFilter(normalizedPrompt))
|
|
485
|
+
return null;
|
|
486
|
+
if (this.promptRequestsVisualOrStructuralEdit(normalizedPrompt))
|
|
487
|
+
return null;
|
|
488
|
+
if (this.promptRequestsExportOperation(normalizedPrompt))
|
|
489
|
+
return null;
|
|
490
|
+
const candidates = this.selectedRecordFilterCandidates(contextHints)
|
|
491
|
+
.map((candidate) => {
|
|
492
|
+
const score = this.selectedRecordFilterPromptGroundingScore(normalizedPrompt, candidate);
|
|
493
|
+
const criteria = this.toRecord(candidate['criteria']);
|
|
494
|
+
return { candidate, score, criteria };
|
|
495
|
+
})
|
|
496
|
+
.filter((entry) => entry.score > 0 && entry.criteria && Object.keys(entry.criteria).length > 0)
|
|
497
|
+
.sort((left, right) => right.score - left.score);
|
|
498
|
+
const top = candidates[0];
|
|
499
|
+
if (!top || top.score < 70)
|
|
500
|
+
return null;
|
|
501
|
+
const secondScore = candidates[1]?.score ?? 0;
|
|
502
|
+
if (secondScore > 0 && top.score - secondScore < 30)
|
|
503
|
+
return null;
|
|
504
|
+
return {
|
|
505
|
+
type: 'patch',
|
|
506
|
+
sessionId: request.sessionId,
|
|
507
|
+
patch: {
|
|
508
|
+
tableRuntimeOperations: {
|
|
509
|
+
kind: 'praxis.table.runtime-operation.batch',
|
|
510
|
+
source: 'selected-record-filter-candidate-local-grounding',
|
|
511
|
+
operations: [{
|
|
512
|
+
operationId: 'table.filter.apply',
|
|
513
|
+
input: {
|
|
514
|
+
criteria: top.criteria,
|
|
515
|
+
source: 'selected-records',
|
|
516
|
+
},
|
|
517
|
+
}],
|
|
518
|
+
},
|
|
519
|
+
},
|
|
520
|
+
explanation: `Filtro por ${this.stringValue(top.candidate['label']) || this.humanizeFilterField(this.stringValue(top.candidate['field']))} preparado a partir dos registros selecionados.`,
|
|
521
|
+
warnings: [
|
|
522
|
+
'selected-record-filter-candidate-materialized',
|
|
523
|
+
'Resposta local gerada somente depois de confirmar table.filter.apply no contrato runtime e ranquear candidatos canônicos de selectedRecordsContext.filterCandidates.',
|
|
524
|
+
],
|
|
525
|
+
};
|
|
526
|
+
}
|
|
526
527
|
localDeclaredFilterApplyResponse(request, contextHints) {
|
|
527
528
|
if (!this.runtimeOperationAllowed(contextHints, 'table.filter.apply'))
|
|
528
529
|
return null;
|
|
@@ -1492,6 +1493,21 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
1492
1493
|
'salário',
|
|
1493
1494
|
].some((token) => normalizedPrompt.includes(this.normalizeLabel(token)));
|
|
1494
1495
|
}
|
|
1496
|
+
promptRequestsExportOperation(normalizedPrompt) {
|
|
1497
|
+
return [
|
|
1498
|
+
'exporta',
|
|
1499
|
+
'exportar',
|
|
1500
|
+
'exportacao',
|
|
1501
|
+
'exportação',
|
|
1502
|
+
'csv',
|
|
1503
|
+
'json',
|
|
1504
|
+
'excel',
|
|
1505
|
+
'xlsx',
|
|
1506
|
+
'pdf',
|
|
1507
|
+
'arquivo',
|
|
1508
|
+
'download',
|
|
1509
|
+
].some((token) => normalizedPrompt.includes(this.normalizeLabel(token)));
|
|
1510
|
+
}
|
|
1495
1511
|
promptRequestsVisualOrStructuralEdit(normalizedPrompt) {
|
|
1496
1512
|
return [
|
|
1497
1513
|
'badge',
|
|
@@ -2167,6 +2183,7 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
2167
2183
|
if (response.sessionId && response.sessionId !== request.sessionId) {
|
|
2168
2184
|
request = { ...request, sessionId: response.sessionId };
|
|
2169
2185
|
}
|
|
2186
|
+
response = this.normalizeComputedColumnAuxiliaryMaterializations(response);
|
|
2170
2187
|
if (response.type === 'clarification') {
|
|
2171
2188
|
const questions = this.toClarificationQuestions(response, request);
|
|
2172
2189
|
const diagnostics = this.buildClarificationDiagnostics(response);
|
|
@@ -2228,7 +2245,7 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
2228
2245
|
}
|
|
2229
2246
|
if (response.patch && Object.keys(response.patch).length > 0) {
|
|
2230
2247
|
const warnings = response.warnings?.filter(Boolean) ?? [];
|
|
2231
|
-
const quickReplies = this.shouldSuppressReviewQuickReplies(response.patch)
|
|
2248
|
+
const quickReplies = this.shouldSuppressReviewQuickReplies(response.patch, response)
|
|
2232
2249
|
? []
|
|
2233
2250
|
: this.toQuickReplies(response, request);
|
|
2234
2251
|
const diagnostics = this.buildReviewDiagnostics(response, warnings);
|
|
@@ -2259,14 +2276,21 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
2259
2276
|
canApply: false,
|
|
2260
2277
|
};
|
|
2261
2278
|
}
|
|
2262
|
-
shouldSuppressReviewQuickReplies(patch) {
|
|
2263
|
-
|
|
2279
|
+
shouldSuppressReviewQuickReplies(patch, response) {
|
|
2280
|
+
if (Array.isArray(response.optionPayloads) && response.optionPayloads.length > 0)
|
|
2281
|
+
return false;
|
|
2282
|
+
const record = this.toRecord(patch);
|
|
2283
|
+
return !!record && Object.keys(record).length > 0;
|
|
2264
2284
|
}
|
|
2265
2285
|
isTurnInProgressResponse(response) {
|
|
2266
2286
|
return this.normalizeLabel(response.code ?? '') === 'turn in progress';
|
|
2267
2287
|
}
|
|
2268
2288
|
compileAdapterResponse(response, request) {
|
|
2269
2289
|
response = this.normalizeCategoricalRendererPalette(response, request);
|
|
2290
|
+
response = this.normalizeStatusPresentationPlan(response, request);
|
|
2291
|
+
response = this.normalizeNumericBandConditionalStylePlan(response, request);
|
|
2292
|
+
response = this.normalizeCompoundColumnPresentationPlan(response, request);
|
|
2293
|
+
response = this.normalizeComputedColumnAuxiliaryMaterializations(response);
|
|
2270
2294
|
if (response.type === 'clarification' || response.type === 'info' || response.type === 'error') {
|
|
2271
2295
|
const continuedEarlyColumnPlan = this.columnPlanForMisroutedVisualSemanticsClarification(response, request);
|
|
2272
2296
|
if (continuedEarlyColumnPlan) {
|
|
@@ -2289,6 +2313,9 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
2289
2313
|
const executableResponse = {
|
|
2290
2314
|
...response,
|
|
2291
2315
|
patch: normalizedExecutable.patch,
|
|
2316
|
+
...(this.safeReviewText(normalizedExecutable.explanation)
|
|
2317
|
+
? { explanation: this.safeReviewText(normalizedExecutable.explanation) }
|
|
2318
|
+
: {}),
|
|
2292
2319
|
...(normalizedExecutable.componentEditPlan
|
|
2293
2320
|
? { componentEditPlan: normalizedExecutable.componentEditPlan }
|
|
2294
2321
|
: {}),
|
|
@@ -4665,6 +4692,14 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
4665
4692
|
?? this.booleanSimNaoOperationsFromGroundedClarification(prompt, responseText, columns)
|
|
4666
4693
|
?? this.booleanSimNaoOperationsFromSingleGroundedBooleanColumn(prompt, responseText, columns)
|
|
4667
4694
|
?? this.booleanSimNaoOperationsFromGroundedResponseField(prompt, responseText, columns);
|
|
4695
|
+
const statusPresentationOperations = this.statusPresentationOperationsFromPrompt(prompt, columns);
|
|
4696
|
+
if (statusPresentationOperations.length) {
|
|
4697
|
+
return this.componentEditPlanResponse(statusPresentationOperations, 'Vou ajustar a apresentacao visual do status sem filtrar as linhas.', [
|
|
4698
|
+
...(response.warnings ?? []),
|
|
4699
|
+
'status-renderer-continued-from-filter-clarification',
|
|
4700
|
+
'Residual continuity guard acted only after the LLM scoped a visual status formatting request but answered with a filter clarification instead of a componentEditPlan.',
|
|
4701
|
+
]);
|
|
4702
|
+
}
|
|
4668
4703
|
const promptRequestsSimNao = this.normalizedTextIncludesAny(this.normalizeLabel(prompt), [
|
|
4669
4704
|
'sim nao',
|
|
4670
4705
|
'sim/nao',
|
|
@@ -4706,6 +4741,14 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
4706
4741
|
'Residual continuity guard acted only after the LLM scoped the boolean column and Sim/Nao option but answered informationally or as confirmation instead of materializing a direct request.',
|
|
4707
4742
|
]);
|
|
4708
4743
|
}
|
|
4744
|
+
const numericBandStyleOperations = this.numericBandConditionalStyleOperationsFromPrompt(prompt, responseText, columns);
|
|
4745
|
+
if (numericBandStyleOperations.length) {
|
|
4746
|
+
return this.componentEditPlanResponse(numericBandStyleOperations, 'Vou aplicar formatacao condicional em faixas visuais na coluna numerica indicada.', [
|
|
4747
|
+
...(response.warnings ?? []),
|
|
4748
|
+
'numeric-band-conditional-style-continued-from-filter-clarification',
|
|
4749
|
+
'Residual continuity guard acted only after the LLM scoped a numeric conditional-formatting request but answered with a filter clarification instead of a componentEditPlan.',
|
|
4750
|
+
]);
|
|
4751
|
+
}
|
|
4709
4752
|
if (!visualSemanticClarification && !booleanInfo)
|
|
4710
4753
|
return null;
|
|
4711
4754
|
const formatOperations = this.formatOperationsFromPrompt(prompt, responseText, columns);
|
|
@@ -4747,6 +4790,447 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
4747
4790
|
warnings,
|
|
4748
4791
|
};
|
|
4749
4792
|
}
|
|
4793
|
+
normalizeCompoundColumnPresentationPlan(response, request) {
|
|
4794
|
+
if (!request)
|
|
4795
|
+
return response;
|
|
4796
|
+
if ((response.warnings ?? []).includes('compound-column-presentation-normalized'))
|
|
4797
|
+
return response;
|
|
4798
|
+
if (this.responseHasComputedColumnAuthoringDecision(response)) {
|
|
4799
|
+
return response;
|
|
4800
|
+
}
|
|
4801
|
+
const prompt = this.componentPresentationPromptForTurn(request);
|
|
4802
|
+
if (!this.promptRequestsCompoundColumnPresentationEdit(this.normalizeLabel(prompt)))
|
|
4803
|
+
return response;
|
|
4804
|
+
const currentConfig = this.adapter.getCurrentConfig?.();
|
|
4805
|
+
const columns = Array.isArray(currentConfig?.['columns'])
|
|
4806
|
+
? currentConfig['columns']
|
|
4807
|
+
.map((column) => this.toRecord(column))
|
|
4808
|
+
.filter((column) => !!column && !!this.stringValue(column['field']))
|
|
4809
|
+
: [];
|
|
4810
|
+
if (!columns.length)
|
|
4811
|
+
return response;
|
|
4812
|
+
const intendedOperations = this.compoundColumnPresentationOperationsFromPrompt(prompt, columns);
|
|
4813
|
+
if (!intendedOperations.length)
|
|
4814
|
+
return response;
|
|
4815
|
+
const originalPlan = this.toRecord(response.componentEditPlan);
|
|
4816
|
+
const originalOperations = this.componentEditOperations(originalPlan);
|
|
4817
|
+
const normalizedOperations = this.mergeCompoundColumnPresentationOperations(originalOperations, intendedOperations);
|
|
4818
|
+
if (!normalizedOperations.length)
|
|
4819
|
+
return response;
|
|
4820
|
+
return {
|
|
4821
|
+
...response,
|
|
4822
|
+
...(response.type === 'clarification' || response.type === 'info' ? { type: 'patch' } : {}),
|
|
4823
|
+
componentEditPlan: {
|
|
4824
|
+
...(originalPlan ?? {}),
|
|
4825
|
+
kind: this.stringValue(originalPlan?.['kind']) || 'praxis.table.component-edit-plan',
|
|
4826
|
+
version: this.stringValue(originalPlan?.['version']) || '1.0',
|
|
4827
|
+
componentId: this.stringValue(originalPlan?.['componentId'])
|
|
4828
|
+
|| this.adapter.componentId
|
|
4829
|
+
|| request.componentId
|
|
4830
|
+
|| 'praxis-table',
|
|
4831
|
+
operations: normalizedOperations,
|
|
4832
|
+
},
|
|
4833
|
+
explanation: response.explanation || 'Vou preparar a visualizacao solicitada preservando cada acao por coluna.',
|
|
4834
|
+
warnings: [
|
|
4835
|
+
...(response.warnings ?? []),
|
|
4836
|
+
'compound-column-presentation-normalized',
|
|
4837
|
+
'Residual continuity guard preserved field-level actions after the LLM authored or clarified a compound visibility/sticky/highlight request.',
|
|
4838
|
+
],
|
|
4839
|
+
};
|
|
4840
|
+
}
|
|
4841
|
+
normalizeStatusPresentationPlan(response, request) {
|
|
4842
|
+
if (!request)
|
|
4843
|
+
return response;
|
|
4844
|
+
if ((response.warnings ?? []).includes('status-presentation-normalized'))
|
|
4845
|
+
return response;
|
|
4846
|
+
const currentConfig = this.adapter.getCurrentConfig?.();
|
|
4847
|
+
const columns = Array.isArray(currentConfig?.['columns'])
|
|
4848
|
+
? currentConfig['columns']
|
|
4849
|
+
.map((column) => this.toRecord(column))
|
|
4850
|
+
.filter((column) => !!column && !!this.stringValue(column['field']))
|
|
4851
|
+
: [];
|
|
4852
|
+
if (!columns.length)
|
|
4853
|
+
return response;
|
|
4854
|
+
const operations = this.statusPresentationOperationsFromPrompt(this.componentPresentationPromptForTurn(request), columns);
|
|
4855
|
+
if (!operations.length)
|
|
4856
|
+
return response;
|
|
4857
|
+
const statusFields = new Set(operations
|
|
4858
|
+
.map((operation) => this.operationTargetField(operation))
|
|
4859
|
+
.filter((field) => !!field));
|
|
4860
|
+
const originalPlan = this.toRecord(response.componentEditPlan);
|
|
4861
|
+
const originalOperations = this.componentEditOperations(originalPlan)
|
|
4862
|
+
.filter((operation) => {
|
|
4863
|
+
const field = this.operationTargetField(operation);
|
|
4864
|
+
const operationId = this.stringValue(operation['operationId']);
|
|
4865
|
+
return !(field && statusFields.has(field) && (operationId === 'column.visibility.set'
|
|
4866
|
+
|| operationId === 'column.renderer.set'
|
|
4867
|
+
|| operationId === 'column.conditionalRenderer.add'
|
|
4868
|
+
|| operationId === 'column.conditionalStyle.add'));
|
|
4869
|
+
});
|
|
4870
|
+
return {
|
|
4871
|
+
...response,
|
|
4872
|
+
...(response.type === 'clarification' || response.type === 'info' ? { type: 'patch' } : {}),
|
|
4873
|
+
componentEditPlan: {
|
|
4874
|
+
...(originalPlan ?? {}),
|
|
4875
|
+
kind: this.stringValue(originalPlan?.['kind']) || 'praxis.table.component-edit-plan',
|
|
4876
|
+
version: this.stringValue(originalPlan?.['version']) || '1.0',
|
|
4877
|
+
componentId: this.stringValue(originalPlan?.['componentId'])
|
|
4878
|
+
|| this.adapter.componentId
|
|
4879
|
+
|| request.componentId
|
|
4880
|
+
|| 'praxis-table',
|
|
4881
|
+
operations: [...originalOperations, ...operations],
|
|
4882
|
+
},
|
|
4883
|
+
explanation: response.explanation || 'Vou ajustar a apresentacao visual do status sem filtrar as linhas.',
|
|
4884
|
+
warnings: [
|
|
4885
|
+
...(response.warnings ?? []),
|
|
4886
|
+
'status-presentation-normalized',
|
|
4887
|
+
'Residual continuity guard preserved a status visual presentation request after the LLM authored or clarified a weaker status operation.',
|
|
4888
|
+
],
|
|
4889
|
+
};
|
|
4890
|
+
}
|
|
4891
|
+
normalizeNumericBandConditionalStylePlan(response, request) {
|
|
4892
|
+
if (!request)
|
|
4893
|
+
return response;
|
|
4894
|
+
if ((response.warnings ?? []).includes('numeric-band-conditional-style-normalized'))
|
|
4895
|
+
return response;
|
|
4896
|
+
const currentConfig = this.adapter.getCurrentConfig?.();
|
|
4897
|
+
const columns = Array.isArray(currentConfig?.['columns'])
|
|
4898
|
+
? currentConfig['columns']
|
|
4899
|
+
.map((column) => this.toRecord(column))
|
|
4900
|
+
.filter((column) => !!column && !!this.stringValue(column['field']))
|
|
4901
|
+
: [];
|
|
4902
|
+
if (!columns.length)
|
|
4903
|
+
return response;
|
|
4904
|
+
const operations = this.numericBandConditionalStyleOperationsFromPrompt(this.componentPresentationPromptForTurn(request), [
|
|
4905
|
+
response.message,
|
|
4906
|
+
response.explanation,
|
|
4907
|
+
...(response.questions ?? []),
|
|
4908
|
+
].join(' '), columns);
|
|
4909
|
+
if (!operations.length)
|
|
4910
|
+
return response;
|
|
4911
|
+
const fields = new Set(operations
|
|
4912
|
+
.map((operation) => this.operationTargetField(operation))
|
|
4913
|
+
.filter((field) => !!field));
|
|
4914
|
+
const originalPlan = this.toRecord(response.componentEditPlan);
|
|
4915
|
+
const originalOperations = this.componentEditOperations(originalPlan)
|
|
4916
|
+
.filter((operation) => {
|
|
4917
|
+
const field = this.operationTargetField(operation);
|
|
4918
|
+
return !(field && fields.has(field) && this.stringValue(operation['operationId']) === 'column.conditionalStyle.add');
|
|
4919
|
+
});
|
|
4920
|
+
return {
|
|
4921
|
+
...response,
|
|
4922
|
+
...(response.type === 'clarification' || response.type === 'info' ? { type: 'patch' } : {}),
|
|
4923
|
+
componentEditPlan: {
|
|
4924
|
+
...(originalPlan ?? {}),
|
|
4925
|
+
kind: this.stringValue(originalPlan?.['kind']) || 'praxis.table.component-edit-plan',
|
|
4926
|
+
version: this.stringValue(originalPlan?.['version']) || '1.0',
|
|
4927
|
+
componentId: this.stringValue(originalPlan?.['componentId'])
|
|
4928
|
+
|| this.adapter.componentId
|
|
4929
|
+
|| request.componentId
|
|
4930
|
+
|| 'praxis-table',
|
|
4931
|
+
operations: [...originalOperations, ...operations],
|
|
4932
|
+
},
|
|
4933
|
+
explanation: response.explanation || 'Vou aplicar formatacao condicional em faixas visuais na coluna numerica indicada.',
|
|
4934
|
+
warnings: [
|
|
4935
|
+
...(response.warnings ?? []),
|
|
4936
|
+
'numeric-band-conditional-style-normalized',
|
|
4937
|
+
'Residual continuity guard grounded numeric band thresholds from the canonical dataProfile when available, avoiding accidental thresholds from prose or visual token numbers.',
|
|
4938
|
+
],
|
|
4939
|
+
};
|
|
4940
|
+
}
|
|
4941
|
+
componentEditPlanHasAnyOperation(componentEditPlan, operationIds) {
|
|
4942
|
+
if (!operationIds.length)
|
|
4943
|
+
return false;
|
|
4944
|
+
const expected = new Set(operationIds);
|
|
4945
|
+
return this.componentEditOperations(componentEditPlan)
|
|
4946
|
+
.some((operation) => expected.has(this.componentEditPlanOperationIdentity(operation)));
|
|
4947
|
+
}
|
|
4948
|
+
responseHasComputedColumnAuthoringDecision(response) {
|
|
4949
|
+
if (this.componentEditPlanHasAnyOperation(response.componentEditPlan, ['column.computed.add', 'column.computed.set'])) {
|
|
4950
|
+
return true;
|
|
4951
|
+
}
|
|
4952
|
+
const patch = this.toRecord(response.patch);
|
|
4953
|
+
const columns = Array.isArray(patch?.['columns']) ? patch['columns'] : [];
|
|
4954
|
+
return columns
|
|
4955
|
+
.map((column) => this.toRecord(column))
|
|
4956
|
+
.some((column) => !!column?.['computed']);
|
|
4957
|
+
}
|
|
4958
|
+
normalizeComputedColumnAuxiliaryMaterializations(response) {
|
|
4959
|
+
const plan = this.toRecord(response.componentEditPlan);
|
|
4960
|
+
const operations = this.componentEditOperations(plan);
|
|
4961
|
+
if (!plan || operations.length < 2)
|
|
4962
|
+
return response;
|
|
4963
|
+
const computedFields = new Set(operations
|
|
4964
|
+
.filter((operation) => this.componentEditPlanOperationIdentity(operation) === 'column.computed.add'
|
|
4965
|
+
|| this.componentEditPlanOperationIdentity(operation) === 'column.computed.set')
|
|
4966
|
+
.map((operation) => this.operationTargetField(operation))
|
|
4967
|
+
.filter((field) => !!field));
|
|
4968
|
+
if (!computedFields.size) {
|
|
4969
|
+
return response;
|
|
4970
|
+
}
|
|
4971
|
+
const auxiliaryAddedFields = new Set();
|
|
4972
|
+
for (const operation of operations) {
|
|
4973
|
+
const operationId = this.componentEditPlanOperationIdentity(operation);
|
|
4974
|
+
const field = this.operationTargetField(operation);
|
|
4975
|
+
if (!field)
|
|
4976
|
+
continue;
|
|
4977
|
+
if (operationId === 'column.add' && !computedFields.has(field)) {
|
|
4978
|
+
auxiliaryAddedFields.add(field);
|
|
4979
|
+
}
|
|
4980
|
+
}
|
|
4981
|
+
const removableFields = auxiliaryAddedFields;
|
|
4982
|
+
if (!removableFields.size)
|
|
4983
|
+
return response;
|
|
4984
|
+
const normalizedOperations = operations.filter((operation) => {
|
|
4985
|
+
const operationId = this.componentEditPlanOperationIdentity(operation);
|
|
4986
|
+
const field = this.operationTargetField(operation);
|
|
4987
|
+
return !(field && removableFields.has(field) && (operationId === 'column.add'
|
|
4988
|
+
|| operationId === 'column.renderer.set'));
|
|
4989
|
+
});
|
|
4990
|
+
if (normalizedOperations.length === operations.length)
|
|
4991
|
+
return response;
|
|
4992
|
+
return {
|
|
4993
|
+
...response,
|
|
4994
|
+
componentEditPlan: {
|
|
4995
|
+
...plan,
|
|
4996
|
+
operations: normalizedOperations,
|
|
4997
|
+
},
|
|
4998
|
+
warnings: [
|
|
4999
|
+
...(response.warnings ?? []),
|
|
5000
|
+
'computed-column-auxiliary-materialization-normalized',
|
|
5001
|
+
'Removed auxiliary column.add/column.renderer.set operations that conflicted with a canonical computed-column decision in the same componentEditPlan.',
|
|
5002
|
+
],
|
|
5003
|
+
};
|
|
5004
|
+
}
|
|
5005
|
+
componentEditPlanOperationIdentity(operation) {
|
|
5006
|
+
const operationId = this.stringValue(operation['operationId']);
|
|
5007
|
+
if (operationId) {
|
|
5008
|
+
return this.canonicalComponentEditPlanOperationIdentity(operationId);
|
|
5009
|
+
}
|
|
5010
|
+
const changeKind = this.stringValue(operation['changeKind']);
|
|
5011
|
+
return this.canonicalComponentEditPlanOperationIdentity(changeKind);
|
|
5012
|
+
}
|
|
5013
|
+
canonicalComponentEditPlanOperationIdentity(operationId) {
|
|
5014
|
+
switch (operationId) {
|
|
5015
|
+
case 'addColumn':
|
|
5016
|
+
case 'add_column':
|
|
5017
|
+
case 'column_add':
|
|
5018
|
+
return 'column.add';
|
|
5019
|
+
case 'setColumnRenderer':
|
|
5020
|
+
case 'set_column_renderer':
|
|
5021
|
+
case 'column_renderer_set':
|
|
5022
|
+
return 'column.renderer.set';
|
|
5023
|
+
case 'set_column_computed':
|
|
5024
|
+
case 'add_column_computed':
|
|
5025
|
+
case 'add_computed_column':
|
|
5026
|
+
case 'set_computed_column':
|
|
5027
|
+
case 'column_computed_add':
|
|
5028
|
+
return 'column.computed.add';
|
|
5029
|
+
case 'column.computed.set':
|
|
5030
|
+
return 'column.computed.add';
|
|
5031
|
+
default:
|
|
5032
|
+
return operationId;
|
|
5033
|
+
}
|
|
5034
|
+
}
|
|
5035
|
+
componentPresentationPromptForTurn(request) {
|
|
5036
|
+
const parts = [
|
|
5037
|
+
request.pendingClarification?.sourcePrompt,
|
|
5038
|
+
...(request.messages ?? [])
|
|
5039
|
+
.filter((message) => message.role === 'user')
|
|
5040
|
+
.map((message) => message.text),
|
|
5041
|
+
request.prompt,
|
|
5042
|
+
];
|
|
5043
|
+
return parts
|
|
5044
|
+
.map((part) => (part ?? '').trim())
|
|
5045
|
+
.filter((part) => !!part)
|
|
5046
|
+
.join(' ');
|
|
5047
|
+
}
|
|
5048
|
+
promptRequestsCompoundColumnPresentationEdit(normalizedPrompt) {
|
|
5049
|
+
const hasVisibility = this.normalizedTextIncludesAny(normalizedPrompt, [
|
|
5050
|
+
'oculta',
|
|
5051
|
+
'ocultar',
|
|
5052
|
+
'oculte',
|
|
5053
|
+
'esconde',
|
|
5054
|
+
'esconder',
|
|
5055
|
+
'esconda',
|
|
5056
|
+
'visao publica',
|
|
5057
|
+
'visão publica',
|
|
5058
|
+
'visualizacao publica',
|
|
5059
|
+
'visualização publica',
|
|
5060
|
+
]);
|
|
5061
|
+
const hasStickyOrHighlight = this.normalizedTextIncludesAny(normalizedPrompt, [
|
|
5062
|
+
'fixa',
|
|
5063
|
+
'fixe',
|
|
5064
|
+
'fixar',
|
|
5065
|
+
'pin',
|
|
5066
|
+
'sticky',
|
|
5067
|
+
'destaca',
|
|
5068
|
+
'destaque',
|
|
5069
|
+
'destacar',
|
|
5070
|
+
'realca',
|
|
5071
|
+
'realce',
|
|
5072
|
+
'realçar',
|
|
5073
|
+
]);
|
|
5074
|
+
return hasVisibility && hasStickyOrHighlight;
|
|
5075
|
+
}
|
|
5076
|
+
compoundColumnPresentationOperationsFromPrompt(prompt, columns) {
|
|
5077
|
+
const normalizedPrompt = this.normalizeLabel(prompt);
|
|
5078
|
+
const operations = [];
|
|
5079
|
+
const seen = new Set();
|
|
5080
|
+
for (const column of columns) {
|
|
5081
|
+
const field = this.stringValue(column['field']);
|
|
5082
|
+
if (!field)
|
|
5083
|
+
continue;
|
|
5084
|
+
const action = this.columnPresentationActionFromPrompt(normalizedPrompt, column);
|
|
5085
|
+
if (!action)
|
|
5086
|
+
continue;
|
|
5087
|
+
const key = `${action}:${field}`;
|
|
5088
|
+
if (seen.has(key))
|
|
5089
|
+
continue;
|
|
5090
|
+
seen.add(key);
|
|
5091
|
+
if (action === 'hide') {
|
|
5092
|
+
operations.push({
|
|
5093
|
+
operationId: 'column.visibility.set',
|
|
5094
|
+
target: { kind: 'column', field },
|
|
5095
|
+
input: { visible: false },
|
|
5096
|
+
});
|
|
5097
|
+
continue;
|
|
5098
|
+
}
|
|
5099
|
+
if (action === 'sticky') {
|
|
5100
|
+
operations.push({
|
|
5101
|
+
operationId: 'column.sticky.set',
|
|
5102
|
+
target: { kind: 'column', field },
|
|
5103
|
+
input: { sticky: 'start' },
|
|
5104
|
+
});
|
|
5105
|
+
continue;
|
|
5106
|
+
}
|
|
5107
|
+
operations.push({
|
|
5108
|
+
operationId: 'column.renderer.set',
|
|
5109
|
+
target: { kind: 'column', field },
|
|
5110
|
+
input: {
|
|
5111
|
+
renderer: {
|
|
5112
|
+
type: 'chip',
|
|
5113
|
+
chip: {
|
|
5114
|
+
textField: field,
|
|
5115
|
+
color: 'info',
|
|
5116
|
+
variant: 'outlined',
|
|
5117
|
+
},
|
|
5118
|
+
},
|
|
5119
|
+
},
|
|
5120
|
+
});
|
|
5121
|
+
}
|
|
5122
|
+
return operations;
|
|
5123
|
+
}
|
|
5124
|
+
columnPresentationActionFromPrompt(normalizedPrompt, column) {
|
|
5125
|
+
const mentionIndex = this.columnMentionIndex(normalizedPrompt, column);
|
|
5126
|
+
if (mentionIndex < 0)
|
|
5127
|
+
return null;
|
|
5128
|
+
const beforeMention = normalizedPrompt.slice(0, mentionIndex);
|
|
5129
|
+
const tokens = [];
|
|
5130
|
+
this.collectActionTokenIndexes(beforeMention, ['oculta', 'ocultar', 'oculte', 'esconde', 'esconder', 'esconda'], 'hide', tokens);
|
|
5131
|
+
this.collectActionTokenIndexes(beforeMention, ['fixa', 'fixe', 'fixar', 'pin', 'sticky'], 'sticky', tokens);
|
|
5132
|
+
this.collectActionTokenIndexes(beforeMention, ['destaca', 'destaque', 'destacar', 'realca', 'realce', 'realçar'], 'highlight', tokens);
|
|
5133
|
+
tokens.sort((left, right) => right.index - left.index);
|
|
5134
|
+
return tokens[0]?.action ?? null;
|
|
5135
|
+
}
|
|
5136
|
+
collectActionTokenIndexes(text, candidates, action, bucket) {
|
|
5137
|
+
for (const candidate of candidates) {
|
|
5138
|
+
const normalized = this.normalizeLabel(candidate);
|
|
5139
|
+
let index = text.indexOf(normalized);
|
|
5140
|
+
while (index >= 0) {
|
|
5141
|
+
bucket.push({ action, index });
|
|
5142
|
+
index = text.indexOf(normalized, index + normalized.length);
|
|
5143
|
+
}
|
|
5144
|
+
}
|
|
5145
|
+
}
|
|
5146
|
+
columnMentionIndex(normalizedPrompt, column) {
|
|
5147
|
+
return this.columnPresentationAliases(column)
|
|
5148
|
+
.map((alias) => normalizedPrompt.indexOf(alias))
|
|
5149
|
+
.filter((index) => index >= 0)
|
|
5150
|
+
.sort((left, right) => left - right)[0] ?? -1;
|
|
5151
|
+
}
|
|
5152
|
+
columnPresentationAliases(column) {
|
|
5153
|
+
const aliases = new Set(this.columnMentionAliases(column));
|
|
5154
|
+
const field = this.normalizeLabel(this.stringValue(column['field']));
|
|
5155
|
+
const header = this.normalizeLabel(this.stringValue(column['header']));
|
|
5156
|
+
if (field.startsWith('nome') || header.includes('nome'))
|
|
5157
|
+
aliases.add('nome');
|
|
5158
|
+
if (field.includes('funcionario') || header.includes('funcionario'))
|
|
5159
|
+
aliases.add('nome');
|
|
5160
|
+
if (field.includes('salario') || header.includes('salario'))
|
|
5161
|
+
aliases.add('salario');
|
|
5162
|
+
if (field.includes('salario') || header.includes('salario'))
|
|
5163
|
+
aliases.add('salário');
|
|
5164
|
+
return [...aliases].sort((left, right) => right.length - left.length);
|
|
5165
|
+
}
|
|
5166
|
+
mergeCompoundColumnPresentationOperations(originalOperations, intendedOperations) {
|
|
5167
|
+
const protectedFields = new Map();
|
|
5168
|
+
const protectedSemanticKeys = new Map();
|
|
5169
|
+
for (const operation of intendedOperations) {
|
|
5170
|
+
const field = this.operationTargetField(operation);
|
|
5171
|
+
const action = this.compoundOperationAction(operation);
|
|
5172
|
+
if (!field || !action)
|
|
5173
|
+
continue;
|
|
5174
|
+
const actions = protectedFields.get(field) ?? new Set();
|
|
5175
|
+
actions.add(action);
|
|
5176
|
+
protectedFields.set(field, actions);
|
|
5177
|
+
const semanticKey = this.compoundOperationFieldSemanticKey(field);
|
|
5178
|
+
if (semanticKey) {
|
|
5179
|
+
const semanticActions = protectedSemanticKeys.get(semanticKey) ?? new Set();
|
|
5180
|
+
semanticActions.add(action);
|
|
5181
|
+
protectedSemanticKeys.set(semanticKey, semanticActions);
|
|
5182
|
+
}
|
|
5183
|
+
}
|
|
5184
|
+
const filteredOriginal = originalOperations.filter((operation) => {
|
|
5185
|
+
const action = this.compoundOperationAction(operation);
|
|
5186
|
+
if (!action)
|
|
5187
|
+
return true;
|
|
5188
|
+
const field = this.operationTargetField(operation);
|
|
5189
|
+
if (!field)
|
|
5190
|
+
return false;
|
|
5191
|
+
const intended = protectedFields.get(field);
|
|
5192
|
+
if (intended)
|
|
5193
|
+
return false;
|
|
5194
|
+
const semanticKey = this.compoundOperationFieldSemanticKey(field);
|
|
5195
|
+
if (semanticKey && protectedSemanticKeys.has(semanticKey))
|
|
5196
|
+
return false;
|
|
5197
|
+
return true;
|
|
5198
|
+
});
|
|
5199
|
+
return [...filteredOriginal, ...intendedOperations];
|
|
5200
|
+
}
|
|
5201
|
+
compoundOperationFieldSemanticKey(field) {
|
|
5202
|
+
const normalized = this.normalizeLabel(field);
|
|
5203
|
+
if (!normalized)
|
|
5204
|
+
return '';
|
|
5205
|
+
if (normalized.startsWith('nome') || normalized.includes('funcionario'))
|
|
5206
|
+
return 'record-name';
|
|
5207
|
+
return '';
|
|
5208
|
+
}
|
|
5209
|
+
compoundOperationAction(operation) {
|
|
5210
|
+
const operationId = this.stringValue(operation['operationId']);
|
|
5211
|
+
const input = this.toRecord(operation['input']) ?? {};
|
|
5212
|
+
if (operationId === 'column.visibility.set' && this.booleanInput(input['visible']) === false)
|
|
5213
|
+
return 'hide';
|
|
5214
|
+
if (operationId === 'column.sticky.set')
|
|
5215
|
+
return 'sticky';
|
|
5216
|
+
if (operationId === 'column.renderer.set' || operationId === 'column.conditionalStyle.add')
|
|
5217
|
+
return 'highlight';
|
|
5218
|
+
return null;
|
|
5219
|
+
}
|
|
5220
|
+
operationTargetField(operation) {
|
|
5221
|
+
const target = this.toRecord(operation['target']);
|
|
5222
|
+
const input = this.toRecord(operation['input']) ?? {};
|
|
5223
|
+
return this.stringValue(target?.['field'])
|
|
5224
|
+
|| this.stringValue(target?.['id'])
|
|
5225
|
+
|| this.stringValue(target?.['name'])
|
|
5226
|
+
|| this.stringValue(operation['target'])
|
|
5227
|
+
|| this.stringValue(operation['targetField'])
|
|
5228
|
+
|| this.stringValue(operation['field'])
|
|
5229
|
+
|| this.stringValue(input['targetField'])
|
|
5230
|
+
|| this.stringValue(input['field'])
|
|
5231
|
+
|| this.stringValue(input['id'])
|
|
5232
|
+
|| this.stringValue(input['name']);
|
|
5233
|
+
}
|
|
4750
5234
|
formatOperationsFromPrompt(prompt, responseText, columns) {
|
|
4751
5235
|
const canonicalFormats = new Set([
|
|
4752
5236
|
'dd/MM/yyyy',
|
|
@@ -4789,6 +5273,291 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
4789
5273
|
});
|
|
4790
5274
|
return this.booleanSimNaoOperationsForColumn(column);
|
|
4791
5275
|
}
|
|
5276
|
+
statusPresentationOperationsFromPrompt(prompt, columns) {
|
|
5277
|
+
const normalizedPrompt = this.normalizeLabel(prompt);
|
|
5278
|
+
const asksStatusVisual = this.normalizedTextIncludesAny(normalizedPrompt, [
|
|
5279
|
+
'status',
|
|
5280
|
+
'ativo',
|
|
5281
|
+
'inativo',
|
|
5282
|
+
'inativos',
|
|
5283
|
+
]) && this.normalizedTextIncludesAny(normalizedPrompt, [
|
|
5284
|
+
'badge',
|
|
5285
|
+
'badges',
|
|
5286
|
+
'chip',
|
|
5287
|
+
'chips',
|
|
5288
|
+
'semantico',
|
|
5289
|
+
'semântico',
|
|
5290
|
+
'verde',
|
|
5291
|
+
'vermelho',
|
|
5292
|
+
'discreto',
|
|
5293
|
+
'discretos',
|
|
5294
|
+
'format',
|
|
5295
|
+
'renderer',
|
|
5296
|
+
]);
|
|
5297
|
+
if (!asksStatusVisual)
|
|
5298
|
+
return [];
|
|
5299
|
+
const column = columns.find((candidate) => {
|
|
5300
|
+
const text = this.normalizeLabel([
|
|
5301
|
+
candidate['field'],
|
|
5302
|
+
candidate['header'],
|
|
5303
|
+
candidate['type'],
|
|
5304
|
+
candidate['dataType'],
|
|
5305
|
+
this.humanizeField(this.stringValue(candidate['field'])),
|
|
5306
|
+
].map((value) => this.stringValue(value)).join(' '));
|
|
5307
|
+
return this.normalizedTextIncludesAny(text, ['status', 'ativo', 'boolean', 'bool']);
|
|
5308
|
+
});
|
|
5309
|
+
const field = this.stringValue(column?.['field']);
|
|
5310
|
+
if (!field)
|
|
5311
|
+
return [];
|
|
5312
|
+
const operations = [
|
|
5313
|
+
this.statusConditionalRendererOperation(field, true, 'Ativo', 'success'),
|
|
5314
|
+
this.statusConditionalRendererOperation(field, false, 'Inativo', 'warn'),
|
|
5315
|
+
];
|
|
5316
|
+
if (this.normalizedTextIncludesAny(normalizedPrompt, ['discreto', 'discretos', 'suave', 'suavizar', 'inativo', 'inativos'])) {
|
|
5317
|
+
operations.push({
|
|
5318
|
+
operationId: 'column.conditionalStyle.add',
|
|
5319
|
+
target: { kind: 'rule', field },
|
|
5320
|
+
input: {
|
|
5321
|
+
id: `${field}-inactive-muted`,
|
|
5322
|
+
condition: { '===': [{ var: field }, false] },
|
|
5323
|
+
style: { opacity: '0.62' },
|
|
5324
|
+
tooltip: { text: 'Funcionário inativo' },
|
|
5325
|
+
description: 'Funcionário inativo com leitura discreta.',
|
|
5326
|
+
},
|
|
5327
|
+
});
|
|
5328
|
+
}
|
|
5329
|
+
return operations;
|
|
5330
|
+
}
|
|
5331
|
+
numericBandConditionalStyleOperationsFromPrompt(prompt, responseText, columns) {
|
|
5332
|
+
const normalizedPrompt = this.normalizeLabel(prompt);
|
|
5333
|
+
const asksConditionalBands = this.normalizedTextIncludesAny(normalizedPrompt, [
|
|
5334
|
+
'formatacao condicional',
|
|
5335
|
+
'formatação condicional',
|
|
5336
|
+
'conditional formatting',
|
|
5337
|
+
'condicional',
|
|
5338
|
+
'faixa',
|
|
5339
|
+
'faixas',
|
|
5340
|
+
'destaca',
|
|
5341
|
+
'destaque',
|
|
5342
|
+
'destacando',
|
|
5343
|
+
'realca',
|
|
5344
|
+
'realce',
|
|
5345
|
+
'realçando',
|
|
5346
|
+
]) && this.normalizedTextIncludesAny(normalizedPrompt, [
|
|
5347
|
+
'alta',
|
|
5348
|
+
'alto',
|
|
5349
|
+
'media',
|
|
5350
|
+
'média',
|
|
5351
|
+
'medio',
|
|
5352
|
+
'médio',
|
|
5353
|
+
'baixa',
|
|
5354
|
+
'baixo',
|
|
5355
|
+
]);
|
|
5356
|
+
if (!asksConditionalBands)
|
|
5357
|
+
return [];
|
|
5358
|
+
const column = columns.find((candidate) => this.numericColumnCandidate(candidate)
|
|
5359
|
+
&& (this.textMentionsColumn(prompt, candidate) || this.textMentionsColumn(responseText, candidate)));
|
|
5360
|
+
const field = this.stringValue(column?.['field']);
|
|
5361
|
+
if (!field)
|
|
5362
|
+
return [];
|
|
5363
|
+
const thresholds = this.numericBandThresholdsForField(field, responseText);
|
|
5364
|
+
if (!thresholds)
|
|
5365
|
+
return [];
|
|
5366
|
+
return [
|
|
5367
|
+
this.numericBandConditionalStyleOperation(field, 'high', {
|
|
5368
|
+
condition: { '>=': [{ var: field }, thresholds.highStart] },
|
|
5369
|
+
description: 'Faixa alta',
|
|
5370
|
+
style: {
|
|
5371
|
+
backgroundColor: 'rgba(46, 125, 50, 0.16)',
|
|
5372
|
+
color: 'rgb(27, 94, 32)',
|
|
5373
|
+
fontWeight: '600',
|
|
5374
|
+
},
|
|
5375
|
+
}),
|
|
5376
|
+
this.numericBandConditionalStyleOperation(field, 'medium', {
|
|
5377
|
+
condition: {
|
|
5378
|
+
and: [
|
|
5379
|
+
{ '>=': [{ var: field }, thresholds.mediumStart] },
|
|
5380
|
+
{ '<': [{ var: field }, thresholds.highStart] },
|
|
5381
|
+
],
|
|
5382
|
+
},
|
|
5383
|
+
description: 'Faixa media',
|
|
5384
|
+
style: {
|
|
5385
|
+
backgroundColor: 'rgba(245, 158, 11, 0.16)',
|
|
5386
|
+
color: 'rgb(146, 64, 14)',
|
|
5387
|
+
fontWeight: '600',
|
|
5388
|
+
},
|
|
5389
|
+
}),
|
|
5390
|
+
this.numericBandConditionalStyleOperation(field, 'low', {
|
|
5391
|
+
condition: { '<': [{ var: field }, thresholds.mediumStart] },
|
|
5392
|
+
description: 'Faixa baixa',
|
|
5393
|
+
style: {
|
|
5394
|
+
backgroundColor: 'rgba(220, 38, 38, 0.12)',
|
|
5395
|
+
color: 'rgb(153, 27, 27)',
|
|
5396
|
+
fontWeight: '600',
|
|
5397
|
+
},
|
|
5398
|
+
}),
|
|
5399
|
+
];
|
|
5400
|
+
}
|
|
5401
|
+
completePendingNumericBandStyleClarification(request) {
|
|
5402
|
+
if (request.action?.kind !== 'clarify')
|
|
5403
|
+
return null;
|
|
5404
|
+
const sourcePrompt = this.pendingClarificationSourcePromptForTurn(request, this.toRecord(request.pendingClarification?.diagnostics) ?? undefined);
|
|
5405
|
+
if (!sourcePrompt)
|
|
5406
|
+
return null;
|
|
5407
|
+
const answerText = [
|
|
5408
|
+
request.action?.displayPrompt,
|
|
5409
|
+
request.action?.value,
|
|
5410
|
+
request.prompt,
|
|
5411
|
+
].map((value) => this.stringValue(value)).filter(Boolean).join(' ');
|
|
5412
|
+
if (!this.normalizedTextIncludesAny(this.normalizeLabel(answerText), [
|
|
5413
|
+
'terco',
|
|
5414
|
+
'terços',
|
|
5415
|
+
'tertil',
|
|
5416
|
+
'automatic',
|
|
5417
|
+
'automático',
|
|
5418
|
+
'automatico',
|
|
5419
|
+
'limites absolutos',
|
|
5420
|
+
'faixa',
|
|
5421
|
+
'faixas',
|
|
5422
|
+
])) {
|
|
5423
|
+
return null;
|
|
5424
|
+
}
|
|
5425
|
+
const columns = this.toArray(this.adapter.getCurrentConfig?.()?.['columns'])
|
|
5426
|
+
.map((column) => this.toRecord(column))
|
|
5427
|
+
.filter((column) => !!column && !!this.stringValue(column['field']));
|
|
5428
|
+
if (!columns.length)
|
|
5429
|
+
return null;
|
|
5430
|
+
const responseText = [
|
|
5431
|
+
request.pendingClarification?.assistantMessage,
|
|
5432
|
+
...(request.pendingClarification?.questions ?? []).map((question) => this.stringValue(question['prompt'])
|
|
5433
|
+
|| this.stringValue(question['question'])
|
|
5434
|
+
|| this.stringValue(question['label'])),
|
|
5435
|
+
answerText,
|
|
5436
|
+
].join(' ');
|
|
5437
|
+
const operations = this.numericBandConditionalStyleOperationsFromPrompt(sourcePrompt, responseText, columns);
|
|
5438
|
+
if (!operations.length)
|
|
5439
|
+
return null;
|
|
5440
|
+
return this.componentEditPlanResponse(operations, 'Vou aplicar formatacao condicional em faixas visuais na coluna numerica indicada.', [
|
|
5441
|
+
'numeric-band-conditional-style-continued-from-clarification-answer',
|
|
5442
|
+
'A resposta de clarificacao continuou uma intencao visual ja ancorada em componentEditPlan; o grounding de campo veio das colunas declaradas e os limites vieram do dataProfile quando a clarificacao nao trouxe valores numericos.',
|
|
5443
|
+
]);
|
|
5444
|
+
}
|
|
5445
|
+
numericColumnCandidate(column) {
|
|
5446
|
+
const text = this.normalizeLabel([
|
|
5447
|
+
column['field'],
|
|
5448
|
+
column['header'],
|
|
5449
|
+
column['type'],
|
|
5450
|
+
column['dataType'],
|
|
5451
|
+
column['format'],
|
|
5452
|
+
this.humanizeField(this.stringValue(column['field'])),
|
|
5453
|
+
].map((value) => this.stringValue(value)).join(' '));
|
|
5454
|
+
return this.normalizedTextIncludesAny(text, [
|
|
5455
|
+
'number',
|
|
5456
|
+
'numeric',
|
|
5457
|
+
'decimal',
|
|
5458
|
+
'integer',
|
|
5459
|
+
'currency',
|
|
5460
|
+
'moeda',
|
|
5461
|
+
'valor',
|
|
5462
|
+
'preco',
|
|
5463
|
+
'preço',
|
|
5464
|
+
'salario',
|
|
5465
|
+
'salário',
|
|
5466
|
+
'brl',
|
|
5467
|
+
]);
|
|
5468
|
+
}
|
|
5469
|
+
numericBandThresholdsForField(field, text) {
|
|
5470
|
+
return this.numericBandThresholdsFromDataProfile(field)
|
|
5471
|
+
?? this.numericBandThresholdsFromText(text);
|
|
5472
|
+
}
|
|
5473
|
+
numericBandThresholdsFromText(text) {
|
|
5474
|
+
const values = this.extractNumericBandSamples(text);
|
|
5475
|
+
if (values.length < 2)
|
|
5476
|
+
return null;
|
|
5477
|
+
const sorted = [...new Set(values)]
|
|
5478
|
+
.sort((left, right) => left - right);
|
|
5479
|
+
if (sorted.length < 2)
|
|
5480
|
+
return null;
|
|
5481
|
+
const min = sorted[0];
|
|
5482
|
+
const max = sorted[sorted.length - 1];
|
|
5483
|
+
if (!Number.isFinite(min) || !Number.isFinite(max) || max <= min)
|
|
5484
|
+
return null;
|
|
5485
|
+
const range = max - min;
|
|
5486
|
+
return {
|
|
5487
|
+
mediumStart: this.roundBandThreshold(min + range / 3),
|
|
5488
|
+
highStart: this.roundBandThreshold(min + (range * 2) / 3),
|
|
5489
|
+
};
|
|
5490
|
+
}
|
|
5491
|
+
numericBandThresholdsFromDataProfile(field) {
|
|
5492
|
+
const dataProfile = this.toRecord(this.adapter.getDataProfile?.());
|
|
5493
|
+
const columns = this.toRecord(dataProfile?.['columns']);
|
|
5494
|
+
const stats = this.toRecord(columns?.[field]);
|
|
5495
|
+
const min = this.numberValue(stats?.['min']);
|
|
5496
|
+
const max = this.numberValue(stats?.['max']);
|
|
5497
|
+
if (min === null || max === null || max <= min)
|
|
5498
|
+
return null;
|
|
5499
|
+
const range = max - min;
|
|
5500
|
+
return {
|
|
5501
|
+
mediumStart: this.roundBandThreshold(min + range / 3),
|
|
5502
|
+
highStart: this.roundBandThreshold(min + (range * 2) / 3),
|
|
5503
|
+
};
|
|
5504
|
+
}
|
|
5505
|
+
extractNumericBandSamples(text) {
|
|
5506
|
+
const matches = text.match(/(?:R\$\s*)?\d{1,3}(?:[.\s]\d{3})*(?:,\d+)?|\d+(?:\.\d+)?/g) ?? [];
|
|
5507
|
+
return matches
|
|
5508
|
+
.map((match) => this.parseLocalizedNumber(match))
|
|
5509
|
+
.filter((value) => Number.isFinite(value));
|
|
5510
|
+
}
|
|
5511
|
+
parseLocalizedNumber(value) {
|
|
5512
|
+
const cleaned = value.replace(/[^\d.,-]+/g, '').trim();
|
|
5513
|
+
if (!cleaned)
|
|
5514
|
+
return null;
|
|
5515
|
+
const normalized = cleaned.includes(',')
|
|
5516
|
+
? cleaned.replace(/\./g, '').replace(',', '.')
|
|
5517
|
+
: cleaned.replace(/\s/g, '');
|
|
5518
|
+
const parsed = Number(normalized);
|
|
5519
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
5520
|
+
}
|
|
5521
|
+
roundBandThreshold(value) {
|
|
5522
|
+
if (Math.abs(value) >= 1000)
|
|
5523
|
+
return Math.round(value / 100) * 100;
|
|
5524
|
+
if (Math.abs(value) >= 100)
|
|
5525
|
+
return Math.round(value / 10) * 10;
|
|
5526
|
+
return Math.round(value);
|
|
5527
|
+
}
|
|
5528
|
+
numericBandConditionalStyleOperation(field, band, input) {
|
|
5529
|
+
return {
|
|
5530
|
+
operationId: 'column.conditionalStyle.add',
|
|
5531
|
+
target: { kind: 'rule', field },
|
|
5532
|
+
input: {
|
|
5533
|
+
id: `${field}-band-${band}`,
|
|
5534
|
+
condition: input.condition,
|
|
5535
|
+
style: input.style,
|
|
5536
|
+
tooltip: { text: input.description },
|
|
5537
|
+
description: input.description,
|
|
5538
|
+
},
|
|
5539
|
+
};
|
|
5540
|
+
}
|
|
5541
|
+
statusConditionalRendererOperation(field, value, text, color) {
|
|
5542
|
+
return {
|
|
5543
|
+
operationId: 'column.conditionalRenderer.add',
|
|
5544
|
+
target: { kind: 'conditionalRenderer', field },
|
|
5545
|
+
input: {
|
|
5546
|
+
id: `${field}-${value ? 'active' : 'inactive'}-badge`,
|
|
5547
|
+
condition: { '===': [{ var: field }, value] },
|
|
5548
|
+
renderer: {
|
|
5549
|
+
type: 'badge',
|
|
5550
|
+
badge: {
|
|
5551
|
+
text,
|
|
5552
|
+
color,
|
|
5553
|
+
variant: 'soft',
|
|
5554
|
+
},
|
|
5555
|
+
},
|
|
5556
|
+
tooltip: { text },
|
|
5557
|
+
description: text,
|
|
5558
|
+
},
|
|
5559
|
+
};
|
|
5560
|
+
}
|
|
4792
5561
|
booleanSimNaoOperationsFromGroundedClarification(prompt, responseText, columns) {
|
|
4793
5562
|
const normalizedPrompt = this.normalizeLabel(prompt);
|
|
4794
5563
|
const normalizedResponse = this.normalizeLabel(responseText.replace(/[^\p{Letter}\p{Number}\s/]+/gu, ' '));
|
|
@@ -5685,6 +6454,8 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
5685
6454
|
columnVisibilityPlanForInvalidExecutableError(response, request, compiledError) {
|
|
5686
6455
|
if (!request)
|
|
5687
6456
|
return null;
|
|
6457
|
+
if ((response.warnings ?? []).includes('column-visibility-continued-from-invalid-executable-plan'))
|
|
6458
|
+
return null;
|
|
5688
6459
|
const prompt = this.columnVisibilityPromptForTurn(request);
|
|
5689
6460
|
if (!this.promptRequestsColumnVisibilityEdit(this.normalizeLabel(prompt)))
|
|
5690
6461
|
return null;
|
|
@@ -5712,10 +6483,15 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
5712
6483
|
.map((column) => this.toRecord(column))
|
|
5713
6484
|
.filter((column) => !!column && !!this.stringValue(column['field']))
|
|
5714
6485
|
: [];
|
|
5715
|
-
const
|
|
6486
|
+
const normalizedPrompt = this.normalizeLabel(prompt);
|
|
6487
|
+
const operations = this.promptRequestsCompoundColumnPresentationEdit(normalizedPrompt)
|
|
6488
|
+
? this.compoundColumnPresentationOperationsFromPrompt(prompt, columns)
|
|
6489
|
+
: this.columnVisibilityOperationsFromPrompt(prompt, columns);
|
|
5716
6490
|
if (!operations.length)
|
|
5717
6491
|
return null;
|
|
5718
|
-
return this.componentEditPlanResponse(operations,
|
|
6492
|
+
return this.componentEditPlanResponse(operations, this.promptRequestsCompoundColumnPresentationEdit(normalizedPrompt)
|
|
6493
|
+
? 'Vou preparar a visualizacao solicitada preservando cada acao por coluna.'
|
|
6494
|
+
: 'Vou ocultar as colunas indicadas para a visualizacao solicitada.', [
|
|
5719
6495
|
...(response.warnings ?? []),
|
|
5720
6496
|
...(compiledError.warnings ?? []),
|
|
5721
6497
|
'column-visibility-continued-from-invalid-executable-plan',
|
|
@@ -6311,7 +7087,33 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
6311
7087
|
...planSummaries.map((summary) => `- ${summary}`),
|
|
6312
7088
|
].join('\n');
|
|
6313
7089
|
}
|
|
6314
|
-
|
|
7090
|
+
const explanation = this.safeReviewText(response.explanation);
|
|
7091
|
+
if (explanation)
|
|
7092
|
+
return explanation;
|
|
7093
|
+
const message = this.safeReviewText(response.message);
|
|
7094
|
+
if (message)
|
|
7095
|
+
return message;
|
|
7096
|
+
return 'Proposta de alteração pronta para revisar.';
|
|
7097
|
+
}
|
|
7098
|
+
safeReviewText(value) {
|
|
7099
|
+
const text = this.stringValue(value).trim();
|
|
7100
|
+
if (!text)
|
|
7101
|
+
return '';
|
|
7102
|
+
const normalized = this.normalizeLabel(text);
|
|
7103
|
+
const looksLikeStructuredEnvelope = ((normalized.includes('componenteditplan') || normalized.includes('tableruntimeoperations'))
|
|
7104
|
+
&& normalized.includes('operationid')) || normalized.includes('responsemode componenteditplan');
|
|
7105
|
+
if (looksLikeStructuredEnvelope)
|
|
7106
|
+
return '';
|
|
7107
|
+
if ((text.startsWith('{') && text.endsWith('}')) || (text.startsWith('[') && text.endsWith(']'))) {
|
|
7108
|
+
try {
|
|
7109
|
+
JSON.parse(text);
|
|
7110
|
+
return '';
|
|
7111
|
+
}
|
|
7112
|
+
catch {
|
|
7113
|
+
// Keep non-JSON prose even when it starts with a brace-like character.
|
|
7114
|
+
}
|
|
7115
|
+
}
|
|
7116
|
+
return text;
|
|
6315
7117
|
}
|
|
6316
7118
|
describeTableRuntimeOperations(patch) {
|
|
6317
7119
|
const envelope = this.toRecord(this.toRecord(patch)?.['tableRuntimeOperations']);
|
|
@@ -7157,12 +7959,97 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
7157
7959
|
}
|
|
7158
7960
|
withCapabilitySystemMessages(messages, contextHints, prompt = '') {
|
|
7159
7961
|
const policies = [
|
|
7962
|
+
this.buildRecommendedCapabilitiesSystemPolicy(contextHints),
|
|
7963
|
+
this.buildComponentPresentationAuthoringSystemPolicy(contextHints, prompt),
|
|
7160
7964
|
this.buildComponentLayoutAuthoringSystemPolicy(contextHints, prompt),
|
|
7161
7965
|
this.buildFilterExpressionSystemPolicy(contextHints),
|
|
7162
|
-
this.buildFilterAuthoringSystemPolicy(contextHints),
|
|
7966
|
+
this.buildFilterAuthoringSystemPolicy(contextHints, prompt),
|
|
7163
7967
|
].filter((message) => !!message);
|
|
7164
7968
|
return policies.length ? [...policies, ...messages] : messages;
|
|
7165
7969
|
}
|
|
7970
|
+
buildRecommendedCapabilitiesSystemPolicy(contextHints) {
|
|
7971
|
+
const recommendedIntent = this.toRecord(contextHints?.['recommendedIntent']);
|
|
7972
|
+
const intentId = this.stringValue(recommendedIntent?.['id'])
|
|
7973
|
+
|| this.stringValue(contextHints?.['opportunityId']);
|
|
7974
|
+
const authoringMode = this.stringValue(contextHints?.['authoringMode']);
|
|
7975
|
+
if (intentId !== 'table-discover-capabilities'
|
|
7976
|
+
&& intentId !== 'table.capabilities.discover'
|
|
7977
|
+
&& authoringMode !== 'table.recommended-capabilities.analysis') {
|
|
7978
|
+
return null;
|
|
7979
|
+
}
|
|
7980
|
+
const candidates = Array.isArray(contextHints?.['availableRecommendations'])
|
|
7981
|
+
? contextHints['availableRecommendations']
|
|
7982
|
+
: [];
|
|
7983
|
+
const candidateLines = candidates
|
|
7984
|
+
.map((entry, index) => {
|
|
7985
|
+
const record = this.toRecord(entry);
|
|
7986
|
+
if (!record)
|
|
7987
|
+
return null;
|
|
7988
|
+
const id = this.stringValue(record['id']) || `candidate-${index + 1}`;
|
|
7989
|
+
const label = this.stringValue(record['label']) || id;
|
|
7990
|
+
const description = this.stringValue(record['description']);
|
|
7991
|
+
const group = this.stringValue(record['groupLabel']);
|
|
7992
|
+
const sourceCandidateIds = Array.isArray(record['sourceCandidateIds'])
|
|
7993
|
+
? record['sourceCandidateIds'].map((value) => this.stringValue(value)).filter(Boolean)
|
|
7994
|
+
: [];
|
|
7995
|
+
return [
|
|
7996
|
+
`- ${id}: ${label}`,
|
|
7997
|
+
description ? `description=${description}` : null,
|
|
7998
|
+
group ? `group=${group}` : null,
|
|
7999
|
+
sourceCandidateIds.length ? `canonicalCandidates=${sourceCandidateIds.join(',')}` : null,
|
|
8000
|
+
].filter(Boolean).join('; ');
|
|
8001
|
+
})
|
|
8002
|
+
.filter((line) => !!line);
|
|
8003
|
+
return {
|
|
8004
|
+
role: 'system',
|
|
8005
|
+
content: [
|
|
8006
|
+
'Praxis table recommended capabilities policy:',
|
|
8007
|
+
'- The user selected the canonical recommended-capabilities intent. Do not merely list all table features.',
|
|
8008
|
+
'- Analyze currentState, runtimeState, schemaFields, dataProfile, and the declared availableRecommendations before answering.',
|
|
8009
|
+
'- Exclude capabilities already materialized in currentState unless the recommendation is a refinement backed by column/data evidence.',
|
|
8010
|
+
'- Prefer recommendations tied to specific columns, data profile signals, selected rows, resource surfaces, navigation destinations, or missing runtime capabilities.',
|
|
8011
|
+
'- Return a concise ranked recommendation set. When a recommendation can be materialized by componentEditPlan or a declared runtime operation, include the safest executable proposal or guided option for review.',
|
|
8012
|
+
'- Do not expose internal ids, operationIds, endpoint paths, or payload details unless the user explicitly asks for technical details.',
|
|
8013
|
+
candidateLines.length ? 'Curated canonical candidate recommendations:' : 'Curated canonical candidate recommendations: none declared.',
|
|
8014
|
+
...candidateLines,
|
|
8015
|
+
].join('\n'),
|
|
8016
|
+
};
|
|
8017
|
+
}
|
|
8018
|
+
buildComponentPresentationAuthoringSystemPolicy(contextHints, prompt) {
|
|
8019
|
+
void contextHints;
|
|
8020
|
+
const normalizedPrompt = this.normalizeLabel(prompt);
|
|
8021
|
+
const presentationIntent = this.promptRequestsVisualOrStructuralEdit(normalizedPrompt)
|
|
8022
|
+
|| this.promptRequestsCompoundColumnPresentationEdit(normalizedPrompt);
|
|
8023
|
+
if (!presentationIntent)
|
|
8024
|
+
return null;
|
|
8025
|
+
const columns = this.toArray(this.adapter.getCurrentConfig()?.['columns'])
|
|
8026
|
+
.map((column) => this.toRecord(column))
|
|
8027
|
+
.filter((column) => !!column && !!this.stringValue(column['field']))
|
|
8028
|
+
.slice(0, 20);
|
|
8029
|
+
if (!columns.length)
|
|
8030
|
+
return null;
|
|
8031
|
+
const columnLines = columns.map((column) => {
|
|
8032
|
+
const field = this.stringValue(column['field']);
|
|
8033
|
+
const label = this.stringValue(column['header']) || this.humanizeField(field);
|
|
8034
|
+
const visible = column['visible'] === false ? 'hidden' : 'visible';
|
|
8035
|
+
const type = this.stringValue(column['type'] ?? column['dataType']) || 'unknown';
|
|
8036
|
+
return `- ${field} (${label}; ${type}; ${visible})`;
|
|
8037
|
+
});
|
|
8038
|
+
return {
|
|
8039
|
+
role: 'system',
|
|
8040
|
+
content: [
|
|
8041
|
+
'Praxis table component presentation authoring policy:',
|
|
8042
|
+
'- Use componentEditPlan operations for visual or structural table changes: renderers, badges, chips, conditional styles, column visibility, sticky columns, formats, and computed columns.',
|
|
8043
|
+
'- Explicit presentation requests such as badge, chip, renderer, color, highlight, format, inactive visual state, public view, hide/show column, pin/sticky column, or calculated column are edit/componentEditPlan requests, not table.filter.apply.',
|
|
8044
|
+
'- When the user asks to create a new column by joining, combining, concatenating, composing, deriving, calculating, merging, juntar, unir, concatenar, combinar, compor, derivar, calcular, or mesclar values from existing declared columns, author exactly one canonical column.computed.add operation for the new field. Use a JSON Logic cat expression for textual joins, include dependencies, and preserve the user requested header/name. Derive a stable camelCase input.field from the requested header/name when the field id is not explicitly provided; do not ask the user to choose a technical field id. Do not materialize derived columns as column.add plus column.renderer.set.',
|
|
8045
|
+
'- For compound prompts, preserve the action attached to each declared column: hide only columns in the hide/privacy scope, pin/fix columns with column.sticky.set, and highlight/destacar columns with renderer or style operations. Never hide a column that the user asked to pin or highlight.',
|
|
8046
|
+
'- If the user answers a previous component-edit clarification with a short option such as "sim", "opcao 2", or "prosseguir", continue the previous component-edit intent instead of treating the answer as a new filter request.',
|
|
8047
|
+
'- Ask clarification only when the declared source columns or authoring capabilities are insufficient to choose a safe componentEditPlan. Do not ask clarification solely to pick an internal field id for a newly named computed column.',
|
|
8048
|
+
'Declared table columns:',
|
|
8049
|
+
...columnLines,
|
|
8050
|
+
].join('\n'),
|
|
8051
|
+
};
|
|
8052
|
+
}
|
|
7166
8053
|
buildComponentLayoutAuthoringSystemPolicy(contextHints, prompt) {
|
|
7167
8054
|
const normalizedPrompt = this.normalizeLabel(prompt);
|
|
7168
8055
|
const position = this.relativeOrderPosition(normalizedPrompt);
|
|
@@ -7195,7 +8082,7 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
7195
8082
|
].join('\n'),
|
|
7196
8083
|
};
|
|
7197
8084
|
}
|
|
7198
|
-
buildFilterAuthoringSystemPolicy(contextHints) {
|
|
8085
|
+
buildFilterAuthoringSystemPolicy(contextHints, prompt = '') {
|
|
7199
8086
|
if (!this.runtimeOperationAllowed(contextHints, 'table.filter.apply'))
|
|
7200
8087
|
return null;
|
|
7201
8088
|
const entries = this.filterFieldCatalogEntries.length
|
|
@@ -7203,6 +8090,7 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
7203
8090
|
: this.extractFilterFieldCatalogEntries(contextHints);
|
|
7204
8091
|
if (!entries.length)
|
|
7205
8092
|
return null;
|
|
8093
|
+
const normalizedPrompt = this.normalizeLabel(prompt);
|
|
7206
8094
|
const catalogLines = entries.slice(0, 12).map((entry) => {
|
|
7207
8095
|
const relatedFields = entry.relatedColumnFields.length
|
|
7208
8096
|
? ` related columns: ${entry.relatedColumnFields.join(', ')}.`
|
|
@@ -7216,6 +8104,9 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
7216
8104
|
'Praxis table filter authoring policy:',
|
|
7217
8105
|
'- Use the declared filterFieldCatalog as the canonical grounding for row filtering decisions.',
|
|
7218
8106
|
'- When the user asks to restrict, search, list, show only, compare values, define ranges, or filter rows, prefer a table.filter.apply runtime operation over visual renderer or column formatting operations.',
|
|
8107
|
+
...(this.promptRequestsVisualOrStructuralEdit(normalizedPrompt)
|
|
8108
|
+
? ['- This turn contains an explicit visual or structural table-edit signal; do not reinterpret that signal as a row filter unless the user also explicitly asks to restrict table rows.']
|
|
8109
|
+
: []),
|
|
7219
8110
|
'- For numeric and date comparisons, use the declared range filter field from filterFieldCatalog instead of the raw listing column field.',
|
|
7220
8111
|
'- If the user intent is ambiguous between filtering rows and changing visual presentation, ask a short clarification instead of choosing a renderer by default.',
|
|
7221
8112
|
'- Do not invent filter field names; use only declared filter fields and their related listing columns for grounding.',
|
|
@@ -8157,6 +9048,14 @@ class TableAgenticAuthoringTurnFlow {
|
|
|
8157
9048
|
stringValue(value) {
|
|
8158
9049
|
return typeof value === 'string' ? value.trim() : '';
|
|
8159
9050
|
}
|
|
9051
|
+
numberValue(value) {
|
|
9052
|
+
const parsed = typeof value === 'number'
|
|
9053
|
+
? value
|
|
9054
|
+
: typeof value === 'string'
|
|
9055
|
+
? Number(value.trim())
|
|
9056
|
+
: Number.NaN;
|
|
9057
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
9058
|
+
}
|
|
8160
9059
|
stringifySurfaceEvidence(value) {
|
|
8161
9060
|
try {
|
|
8162
9061
|
return JSON.stringify(value)?.slice(0, 4000) ?? '';
|