@things-factory/ai-assistant 10.1.91 → 10.1.98
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/client/components/assistant-chat.ts +5 -3
- package/client/components/entry-review.ts +56 -0
- package/client/utils/entry-proposal.ts +20 -5
- package/dist-client/components/assistant-chat.js +5 -3
- package/dist-client/components/assistant-chat.js.map +1 -1
- package/dist-client/components/entry-review.d.ts +5 -0
- package/dist-client/components/entry-review.js +55 -0
- package/dist-client/components/entry-review.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/utils/entry-proposal.d.ts +5 -0
- package/dist-client/utils/entry-proposal.js +16 -5
- package/dist-client/utils/entry-proposal.js.map +1 -1
- package/dist-server/service/data-entry/entry-apply.d.ts +3 -3
- package/dist-server/service/data-entry/entry-apply.js +55 -2
- package/dist-server/service/data-entry/entry-apply.js.map +1 -1
- package/dist-server/service/data-entry/entry-resolver.js +3 -7
- package/dist-server/service/data-entry/entry-resolver.js.map +1 -1
- package/dist-server/service/data-entry/entry-target.d.ts +51 -0
- package/dist-server/service/data-entry/entry-target.js +77 -34
- package/dist-server/service/data-entry/entry-target.js.map +1 -1
- package/dist-server/service/data-entry/entry-tools.d.ts +7 -1
- package/dist-server/service/data-entry/entry-tools.js +79 -5
- package/dist-server/service/data-entry/entry-tools.js.map +1 -1
- package/dist-server/service/data-entry/entry-update.d.ts +99 -0
- package/dist-server/service/data-entry/entry-update.js +196 -0
- package/dist-server/service/data-entry/entry-update.js.map +1 -0
- package/dist-server/service/workspace/workspace-blocks.js +9 -1
- package/dist-server/service/workspace/workspace-blocks.js.map +1 -1
- package/dist-server/service/workspace/workspace-tools.js +43 -1
- package/dist-server/service/workspace/workspace-tools.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/server/service/data-entry/data-entry-update.test.ts +207 -0
- package/server/service/data-entry/entry-apply.ts +63 -6
- package/server/service/data-entry/entry-resolver.ts +4 -9
- package/server/service/data-entry/entry-target.ts +97 -31
- package/server/service/data-entry/entry-tools.ts +83 -5
- package/server/service/data-entry/entry-update.ts +209 -0
- package/server/service/workspace/workspace-blocks.ts +8 -1
- package/server/service/workspace/workspace-tools.ts +43 -1
- package/server/service/workspace/workspace.test.ts +16 -0
- package/test/entry-proposal.test.ts +15 -2
- package/translations/en.json +16 -1
- package/translations/ja.json +16 -1
- package/translations/ko.json +16 -1
- package/translations/ms.json +16 -1
- package/translations/zh.json +16 -1
|
@@ -64,7 +64,7 @@ import { isDifferentConversation, resolveConversation, type AssistantMode, type
|
|
|
64
64
|
import { parseProposalOutcomeLine, parseProposalRequestLine, proposalCardState, type ParsedProposalOutcome } from './proposal-outcome-line.js'
|
|
65
65
|
import { availableTriggers, type TriggerChip } from './chat-triggers.js'
|
|
66
66
|
import './entry-review.js'
|
|
67
|
-
import { entryReadyCount, isEntryProposal, latestEntryKeys } from '../utils/entry-proposal.js'
|
|
67
|
+
import { entryGroupOf, entryReadyCount, isChangeProposal, isEntryProposal, latestEntryKeys } from '../utils/entry-proposal.js'
|
|
68
68
|
import { isWorkspaceProposal, versionOfCard, workspaceAddress } from '../utils/workspace.js'
|
|
69
69
|
|
|
70
70
|
interface ChatLine {
|
|
@@ -3958,7 +3958,7 @@ export class OxAssistantChat extends LitElement {
|
|
|
3958
3958
|
if (isWorkspaceProposal(p)) return this.renderWorkspaceCard(p, key)
|
|
3959
3959
|
const card = proposalCardState(key, { outcomes, requested, sentHere: this.sentProposals.has(key) })
|
|
3960
3960
|
const entry = isEntryProposal(p)
|
|
3961
|
-
const replaced = entry && latestEntry?.get(p
|
|
3961
|
+
const replaced = entry && latestEntry?.get(entryGroupOf(p)) !== key && card.state === 'ready'
|
|
3962
3962
|
const ready = entry ? entryReadyCount(p) : 0
|
|
3963
3963
|
const sent = card.state !== 'ready' || replaced || (entry && ready === 0)
|
|
3964
3964
|
/*
|
|
@@ -4000,7 +4000,9 @@ export class OxAssistantChat extends LitElement {
|
|
|
4000
4000
|
? this.proposalStateText(key)
|
|
4001
4001
|
: entry
|
|
4002
4002
|
? ready
|
|
4003
|
-
?
|
|
4003
|
+
? isChangeProposal(p)
|
|
4004
|
+
? i18next.t('ai-assistant.entry.change', { defaultValue: 'change {count} records', count: ready })
|
|
4005
|
+
: i18next.t('ai-assistant.entry.enter', { defaultValue: 'enter {count} rows', count: ready })
|
|
4004
4006
|
: i18next.t('ai-assistant.entry.nothing-to-enter', { defaultValue: 'nothing to enter' })
|
|
4005
4007
|
: i18next.t('ai-assistant.button.run-proposal', { defaultValue: 'run' })}
|
|
4006
4008
|
</button>
|
|
@@ -103,6 +103,11 @@ export class AssistantEntryReview extends LitElement {
|
|
|
103
103
|
text-underline-offset: 3px;
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
.before {
|
|
107
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
108
|
+
text-decoration: line-through;
|
|
109
|
+
}
|
|
110
|
+
|
|
106
111
|
.missing {
|
|
107
112
|
color: var(--md-sys-color-error);
|
|
108
113
|
font-style: italic;
|
|
@@ -182,8 +187,59 @@ export class AssistantEntryReview extends LitElement {
|
|
|
182
187
|
</div>`
|
|
183
188
|
}
|
|
184
189
|
|
|
190
|
+
/**
|
|
191
|
+
* A 수정안 - each record by its name, each changing field as what it holds now and what it becomes, and how the
|
|
192
|
+
* record was picked. The "now" is the server's reading, never the model's memory.
|
|
193
|
+
*/
|
|
194
|
+
private renderChanges(p: any) {
|
|
195
|
+
const columns: { name: string; type: string; labelKey?: string }[] = p.columns ?? []
|
|
196
|
+
const rows: any[] = p.rows ?? []
|
|
197
|
+
const counts = p.counts ?? {}
|
|
198
|
+
const via = (row: any) =>
|
|
199
|
+
row.via === 'current' ? t('via-current', 'the record on screen') : row.via === 'selected' ? t('via-selected', 'selected on screen') : t('via-id', 'found in the conversation')
|
|
200
|
+
return html`
|
|
201
|
+
<div class="summary" role="status">
|
|
202
|
+
<span>${t('changes', '{count} records', { count: counts.rows ?? rows.length })}</span>
|
|
203
|
+
<span>${t('ready', '{count} ready', { count: counts.ready ?? 0 })}</span>
|
|
204
|
+
${counts.blocked ? html`<span class="blocked">${t('not-changed', '{count} will not be changed', { count: counts.blocked })}</span>` : nothing}
|
|
205
|
+
</div>
|
|
206
|
+
<div class="scroll">
|
|
207
|
+
<table>
|
|
208
|
+
<thead>
|
|
209
|
+
<tr>
|
|
210
|
+
<th>#</th>
|
|
211
|
+
<th>${t('record', 'record')}</th>
|
|
212
|
+
${columns.map(column => html`<th>${column.labelKey ? i18next.t(column.labelKey) : column.name}</th>`)}
|
|
213
|
+
<th>${t('picked', 'picked')}</th>
|
|
214
|
+
</tr>
|
|
215
|
+
</thead>
|
|
216
|
+
<tbody>
|
|
217
|
+
${rows.slice(0, SHOWN).map(row => {
|
|
218
|
+
const blocked = (row.issues ?? []).some((issue: EntryIssueView) => issue.blocking)
|
|
219
|
+
const whole = entryCellIssues(row)
|
|
220
|
+
return html`<tr class=${blocked ? 'blocked' : ''}>
|
|
221
|
+
<td class="num">${row.index + 1}</td>
|
|
222
|
+
<td>${row.label || row.id}${whole.map(issue => html`<span class="row-issue ${issue.blocking ? 'blocking' : ''}">${issueText(issue)}</span>`)}</td>
|
|
223
|
+
${columns.map(column => {
|
|
224
|
+
const issues = entryCellIssues(row, column.name)
|
|
225
|
+
const bad = issues.some(issue => issue.blocking)
|
|
226
|
+
const changes = column.name in (row.values ?? {})
|
|
227
|
+
return html`<td class="${column.type === 'number' || column.type === 'integer' ? 'num' : ''} ${bad ? 'bad' : issues.length ? 'note' : ''}" title=${issues.map(issueText).join('\n')}>
|
|
228
|
+
${changes ? html`<span class="before">${shown(row.before?.[column.name]) || '—'}</span> → <strong>${shown(row.values[column.name]) || '—'}</strong>` : ''}
|
|
229
|
+
</td>`
|
|
230
|
+
})}
|
|
231
|
+
<td class="source">${via(row)}</td>
|
|
232
|
+
</tr>`
|
|
233
|
+
})}
|
|
234
|
+
</tbody>
|
|
235
|
+
</table>
|
|
236
|
+
</div>
|
|
237
|
+
`
|
|
238
|
+
}
|
|
239
|
+
|
|
185
240
|
render() {
|
|
186
241
|
const p = this.proposal ?? {}
|
|
242
|
+
if (p.op === 'update') return this.renderChanges(p)
|
|
187
243
|
const columns: { name: string; type: string; required?: boolean; labelKey?: string }[] = p.columns ?? []
|
|
188
244
|
const rows: EntryRowView[] = p.rows ?? []
|
|
189
245
|
const counts = p.counts ?? {}
|
|
@@ -43,6 +43,8 @@ export interface EntryRowResult {
|
|
|
43
43
|
|
|
44
44
|
export interface EntryApplyAnswer extends ProposalOutcome {
|
|
45
45
|
rows?: EntryRowResult[]
|
|
46
|
+
/** Set by the handler from the card - which words the conversation line uses. */
|
|
47
|
+
op?: string
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
export function isEntryProposal(proposal: any): boolean {
|
|
@@ -66,9 +68,13 @@ export function entryCellIssues(row: EntryRowView, field?: string): EntryIssueVi
|
|
|
66
68
|
/** What stays in the conversation - the model reads it on its next turn, and the person sees what went in. */
|
|
67
69
|
export function entryOutcomeNote(answer: EntryApplyAnswer, t: (key: string, options?: any) => string): string {
|
|
68
70
|
const refused = (answer.rows ?? []).filter(row => !row.ok)
|
|
69
|
-
const head = answer.
|
|
70
|
-
?
|
|
71
|
-
|
|
71
|
+
const head = answer.op === 'update'
|
|
72
|
+
? answer.applied
|
|
73
|
+
? t('ai-assistant.entry.changed', { defaultValue: 'changed {count} records.', count: answer.applied })
|
|
74
|
+
: t('ai-assistant.entry.changed-none', { defaultValue: 'no record was changed.' })
|
|
75
|
+
: answer.applied
|
|
76
|
+
? t('ai-assistant.entry.entered', { defaultValue: 'entered {count} rows.', count: answer.applied })
|
|
77
|
+
: t('ai-assistant.entry.entered-none', { defaultValue: 'no row was entered.' })
|
|
72
78
|
const lines = refused.slice(0, 20).map(row => t('ai-assistant.entry.row-refused', { defaultValue: 'row {n}: {reason}', n: row.index + 1, reason: row.error || '' }))
|
|
73
79
|
const more = refused.length > 20 ? [t('ai-assistant.entry.more-refused', { defaultValue: '{count} more rows were not entered.', count: refused.length - 20 })] : []
|
|
74
80
|
return [head, ...lines, ...more].join('\n')
|
|
@@ -81,7 +87,7 @@ export function entryProposalHandler(
|
|
|
81
87
|
) {
|
|
82
88
|
return async (proposal: any, context: ProposalContext): Promise<ProposalOutcome> => {
|
|
83
89
|
if (!context.sessionId || !proposal?.key) return { verdict: 'unavailable', errorCode: 'no-conversation' }
|
|
84
|
-
const answer = await apply(context.sessionId, proposal.key)
|
|
90
|
+
const answer = { ...(await apply(context.sessionId, proposal.key)), op: proposal.op }
|
|
85
91
|
return {
|
|
86
92
|
verdict: answer.verdict,
|
|
87
93
|
...(answer.errorCode ? { errorCode: answer.errorCode } : {}),
|
|
@@ -98,6 +104,15 @@ export function entryProposalHandler(
|
|
|
98
104
|
*/
|
|
99
105
|
export function latestEntryKeys(proposalsInOrder: any[][]): Map<string, string> {
|
|
100
106
|
const latest = new Map<string, string>()
|
|
101
|
-
for (const list of proposalsInOrder) for (const one of list ?? []) if (isEntryProposal(one) && one.key) latest.set(one
|
|
107
|
+
for (const list of proposalsInOrder) for (const one of list ?? []) if (isEntryProposal(one) && one.key) latest.set(entryGroupOf(one), one.key)
|
|
102
108
|
return latest
|
|
103
109
|
}
|
|
110
|
+
|
|
111
|
+
/** A 입력안 and a 수정안 of one record are counted apart - a newer one of the same kind replaces the older. */
|
|
112
|
+
export function entryGroupOf(proposal: any): string {
|
|
113
|
+
return `${proposal?.target}:${proposal?.op ?? 'create'}`
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function isChangeProposal(proposal: any): boolean {
|
|
117
|
+
return isEntryProposal(proposal) && proposal.op === 'update'
|
|
118
|
+
}
|
|
@@ -48,7 +48,7 @@ import { isDifferentConversation, resolveConversation, pickInitialMode } from '.
|
|
|
48
48
|
import { parseProposalOutcomeLine, parseProposalRequestLine, proposalCardState } from './proposal-outcome-line.js';
|
|
49
49
|
import { availableTriggers } from './chat-triggers.js';
|
|
50
50
|
import './entry-review.js';
|
|
51
|
-
import { entryReadyCount, isEntryProposal, latestEntryKeys } from '../utils/entry-proposal.js';
|
|
51
|
+
import { entryGroupOf, entryReadyCount, isChangeProposal, isEntryProposal, latestEntryKeys } from '../utils/entry-proposal.js';
|
|
52
52
|
import { isWorkspaceProposal, versionOfCard, workspaceAddress } from '../utils/workspace.js';
|
|
53
53
|
function formatFileSize(bytes) {
|
|
54
54
|
if (!bytes || bytes < 1024)
|
|
@@ -3514,7 +3514,7 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
3514
3514
|
return this.renderWorkspaceCard(p, key);
|
|
3515
3515
|
const card = proposalCardState(key, { outcomes, requested, sentHere: this.sentProposals.has(key) });
|
|
3516
3516
|
const entry = isEntryProposal(p);
|
|
3517
|
-
const replaced = entry && latestEntry?.get(p
|
|
3517
|
+
const replaced = entry && latestEntry?.get(entryGroupOf(p)) !== key && card.state === 'ready';
|
|
3518
3518
|
const ready = entry ? entryReadyCount(p) : 0;
|
|
3519
3519
|
const sent = card.state !== 'ready' || replaced || (entry && ready === 0);
|
|
3520
3520
|
/*
|
|
@@ -3555,7 +3555,9 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
3555
3555
|
? this.proposalStateText(key)
|
|
3556
3556
|
: entry
|
|
3557
3557
|
? ready
|
|
3558
|
-
?
|
|
3558
|
+
? isChangeProposal(p)
|
|
3559
|
+
? i18next.t('ai-assistant.entry.change', { defaultValue: 'change {count} records', count: ready })
|
|
3560
|
+
: i18next.t('ai-assistant.entry.enter', { defaultValue: 'enter {count} rows', count: ready })
|
|
3559
3561
|
: i18next.t('ai-assistant.entry.nothing-to-enter', { defaultValue: 'nothing to enter' })
|
|
3560
3562
|
: i18next.t('ai-assistant.button.run-proposal', { defaultValue: 'run' })}
|
|
3561
3563
|
</button>
|