@ykurilov/agent-chat-flows 0.56.0 → 0.58.0
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/CHANGELOG.md +64 -0
- package/dist/index.js +55 -11
- package/dist/index.js.map +1 -1
- package/dist/postgres.js +33 -12
- package/dist/postgres.js.map +1 -1
- package/dist/react.js +9 -7
- package/dist/react.js.map +1 -1
- package/dist/sqlite.js +25 -1
- package/dist/sqlite.js.map +1 -1
- package/dist/types/labels/editor.d.ts +2 -0
- package/dist/types/react/editor/use-flow-editor.d.ts +1 -1
- package/dist/types/runtime/flow-types.d.ts +37 -5
- package/dist/types/runtime/migrate-chats.d.ts +5 -0
- package/dist/types/store/flow-store.d.ts +13 -0
- package/dist/types/store/postgres.d.ts +2 -2
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,69 @@
|
|
|
1
1
|
# @ykurilov/agent-chat-flows
|
|
2
2
|
|
|
3
|
+
## 0.58.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 760962e: Хранилища пакетов на PostgreSQL можно открывать одновременно на совсем пустой базе. Раньше пакеты разом создавали
|
|
8
|
+
схему `agent_chat`, проигравший падал на `CREATE SCHEMA IF NOT EXISTS`, и его хранилище не работало до перезапуска.
|
|
9
|
+
Теперь схема создаётся под общей блокировкой всех пакетов, она держится только на создании схемы. Миграции идут под
|
|
10
|
+
своей блокировкой пакета: разные пакеты друг друга не ждут, взаимной блокировки нет. Столкновение «такое уже есть»
|
|
11
|
+
повторяется само. Новое в core: `migratePostgresSchema({ connect, lockKey, migrate })` — для своих пакетов на той же
|
|
12
|
+
схеме.
|
|
13
|
+
|
|
14
|
+
### Как обновить приложение
|
|
15
|
+
|
|
16
|
+
Ничего делать не нужно.
|
|
17
|
+
- Updated dependencies [332d90e]
|
|
18
|
+
- Updated dependencies [2ed600d]
|
|
19
|
+
- Updated dependencies [760962e]
|
|
20
|
+
- @ykurilov/agent-chat-core@0.58.0
|
|
21
|
+
- @ykurilov/agent-chat-react@0.58.0
|
|
22
|
+
|
|
23
|
+
## 0.57.0
|
|
24
|
+
|
|
25
|
+
### Minor Changes
|
|
26
|
+
|
|
27
|
+
- 7c82b87: Перевод чатов на новую версию теперь отбирает чаты. `flow.chatsByState` и `flow.migrateChats` по умолчанию не берут
|
|
28
|
+
закончившиеся разговоры и тестовые чаты редактора (`FlowTestChat`). Свой отбор — `chatIds` и `filter(chat)` (запись
|
|
29
|
+
прогона и `Chat.data`), одинаково в обоих вызовах: что посчитали, то и переведут. Вернуть прежнее поведение —
|
|
30
|
+
`includeEnded: true, includeTestChats: true`. Окно «Перевести чаты» после перевода перечитывает счётчик.
|
|
31
|
+
|
|
32
|
+
### Как обновить приложение
|
|
33
|
+
|
|
34
|
+
1. Если приложение держало чужие строки `agent_chat.flow_chats` своей транзакцией (`for update`), чтобы `migrateChats`
|
|
35
|
+
их пропустил, — уберите этот обход и передайте отбор прямо в вызовы:
|
|
36
|
+
|
|
37
|
+
Было:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
await client.query("select chat_id from agent_chat.flow_chats where ... for update", [...]);
|
|
41
|
+
await flow.migrateChats({ flowId, toVersionId, map, flow: next });
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Стало:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
await flow.migrateChats({ flowId, toVersionId, map, flow: next, chatIds });
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
В `chatsByState` передайте тот же отбор (`chatIds` или `filter`).
|
|
51
|
+
2. Нужны закончившиеся разговоры или тестовые чаты в переводе — добавьте `includeEnded: true` или
|
|
52
|
+
`includeTestChats: true` в оба вызова.
|
|
53
|
+
3. Своё хранилище сценариев (`FlowStore`) — добавьте метод `listFlowChats({ flowId, exceptVersionId?, chatIds? })` и
|
|
54
|
+
поддержку `chatIds` в `migrateBatch`. Хранилища SDK (память, SQLite, PostgreSQL) уже умеют.
|
|
55
|
+
4. Проверка: `pnpm typecheck` проходит; окно «Перевести чаты» показывает число без закончившихся и тестовых чатов.
|
|
56
|
+
|
|
57
|
+
### Patch Changes
|
|
58
|
+
|
|
59
|
+
- 34074f9: Шапка доски сценария на телефоне: лишние кнопки уходят в меню «⋯», Прогон, Проверить граф и сохранение остаются на виду.
|
|
60
|
+
Новая подпись `labels.header.more`.
|
|
61
|
+
- Updated dependencies [ade99cc]
|
|
62
|
+
- Updated dependencies [86afa20]
|
|
63
|
+
- Updated dependencies [624c10c]
|
|
64
|
+
- @ykurilov/agent-chat-core@0.57.0
|
|
65
|
+
- @ykurilov/agent-chat-react@0.57.0
|
|
66
|
+
|
|
3
67
|
## 0.56.0
|
|
4
68
|
|
|
5
69
|
### Patch Changes
|
package/dist/index.js
CHANGED
|
@@ -118,6 +118,9 @@ import {
|
|
|
118
118
|
|
|
119
119
|
// src/runtime/agent-flow.ts
|
|
120
120
|
import { createHash, randomUUID as randomUUID4 } from "crypto";
|
|
121
|
+
import {
|
|
122
|
+
ChatNotFoundError as ChatNotFoundError4
|
|
123
|
+
} from "@ykurilov/agent-chat-core";
|
|
121
124
|
|
|
122
125
|
// src/prompt/rules/time-zone.ts
|
|
123
126
|
var TIME_ZONE_RULES = `## \u0414\u043D\u0438 \u0438 \u0432\u0440\u0435\u043C\u044F
|
|
@@ -1679,6 +1682,7 @@ function createFlowTimers(input) {
|
|
|
1679
1682
|
|
|
1680
1683
|
// src/runtime/migrate-chats.ts
|
|
1681
1684
|
var MIGRATE_BATCH = 100;
|
|
1685
|
+
var chunks = (items, size) => Array.from({ length: Math.ceil(items.length / size) }, (_, index) => items.slice(index * size, (index + 1) * size));
|
|
1682
1686
|
function createChatMigrator(deps) {
|
|
1683
1687
|
const { store } = deps;
|
|
1684
1688
|
async function newVersion(flowId, versionId, raw) {
|
|
@@ -1699,28 +1703,53 @@ function createChatMigrator(deps) {
|
|
|
1699
1703
|
if (!found) throw new Error(`\u0412\u0435\u0440\u0441\u0438\u0438 ${versionId} \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u044F \xAB${flowId}\xBB \u043D\u0435\u0442 \u0432 \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0435: \u043F\u0435\u0440\u0435\u0434\u0430\u0439\u0442\u0435 \u0435\u0451 \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439 \u0432 \u043F\u043E\u043B\u0435 flow.`);
|
|
1700
1704
|
return { flow: found.flow };
|
|
1701
1705
|
}
|
|
1706
|
+
async function selectedChats(flowId, exceptVersionId, selection) {
|
|
1707
|
+
const rows = await store.listFlowChats({ flowId, ...exceptVersionId !== void 0 && { exceptVersionId }, ...selection.chatIds && { chatIds: selection.chatIds } });
|
|
1708
|
+
const needsData = selection.filter !== void 0 || selection.includeTestChats !== true;
|
|
1709
|
+
const candidate = async ({ endedAt, ...row }) => {
|
|
1710
|
+
const data = needsData ? await deps.chatData(row) : {};
|
|
1711
|
+
const test = testChatOffset(data) !== null;
|
|
1712
|
+
if (test && selection.includeTestChats !== true) return null;
|
|
1713
|
+
const chat = { ...row, ended: endedAt !== null, test, data };
|
|
1714
|
+
return !selection.filter || await selection.filter(chat) ? chat : null;
|
|
1715
|
+
};
|
|
1716
|
+
const selected = [];
|
|
1717
|
+
for (const part of chunks(rows.filter((row) => row.endedAt === null || selection.includeEnded === true), MIGRATE_BATCH)) {
|
|
1718
|
+
for (const chat of await Promise.all(part.map(candidate))) if (chat) selected.push(chat);
|
|
1719
|
+
}
|
|
1720
|
+
return selected;
|
|
1721
|
+
}
|
|
1722
|
+
function countByState(chats) {
|
|
1723
|
+
const counts = /* @__PURE__ */ new Map();
|
|
1724
|
+
for (const chat of chats) {
|
|
1725
|
+
const key = JSON.stringify([chat.versionId, chat.stateId]);
|
|
1726
|
+
const entry = counts.get(key) ?? { versionId: chat.versionId, stateId: chat.stateId, chats: 0 };
|
|
1727
|
+
entry.chats += 1;
|
|
1728
|
+
counts.set(key, entry);
|
|
1729
|
+
}
|
|
1730
|
+
return [...counts.values()].sort(byVersionAndState);
|
|
1731
|
+
}
|
|
1702
1732
|
return {
|
|
1703
|
-
async chatsByState(flowId, options) {
|
|
1704
|
-
|
|
1705
|
-
return options?.exceptVersionId === void 0 ? rows : rows.filter((row) => row.versionId !== options.exceptVersionId);
|
|
1733
|
+
async chatsByState(flowId, options = {}) {
|
|
1734
|
+
return countByState(await selectedChats(flowId, options.exceptVersionId, options));
|
|
1706
1735
|
},
|
|
1707
|
-
async migrateChats(
|
|
1736
|
+
async migrateChats(input) {
|
|
1737
|
+
const { flowId, toVersionId, map, flow: raw } = input;
|
|
1708
1738
|
const { flow: next, save } = await newVersion(flowId, toVersionId, raw);
|
|
1709
1739
|
const removedMap = Object.fromEntries(Object.entries(map).filter(([stateId]) => !stateById(next, stateId)));
|
|
1710
1740
|
for (const [from, to] of Object.entries(removedMap)) {
|
|
1711
1741
|
if (!stateById(next, to)) throw new Error(`\u041A\u0443\u0434\u0430 \u043F\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u0447\u0430\u0442\u044B \u0438\u0437 \xAB${from}\xBB: \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u044F \xAB${to}\xBB \u043D\u0435\u0442 \u0432 \u0432\u0435\u0440\u0441\u0438\u0438 ${toVersionId}.`);
|
|
1712
1742
|
}
|
|
1713
|
-
const
|
|
1714
|
-
const lost = [...new Set(
|
|
1743
|
+
const chats = await selectedChats(flowId, toVersionId, input);
|
|
1744
|
+
const lost = [...new Set(chats.filter((chat) => !stateById(next, chat.stateId) && removedMap[chat.stateId] === void 0).map((chat) => chat.stateId))];
|
|
1715
1745
|
if (lost.length > 0) {
|
|
1716
1746
|
throw new Error(`\u0412 \u0432\u0435\u0440\u0441\u0438\u0438 ${toVersionId} \u043D\u0435\u0442 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0439 ${lost.map((id) => `\xAB${id}\xBB`).join(", ")}, \u0430 \u0432 \u043D\u0438\u0445 \u0435\u0441\u0442\u044C \u0447\u0430\u0442\u044B: \u0432\u044B\u0431\u0435\u0440\u0438\u0442\u0435, \u043A\u0443\u0434\u0430 \u0438\u0445 \u043F\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438.`);
|
|
1717
1747
|
}
|
|
1718
1748
|
await save?.();
|
|
1719
1749
|
const at = deps.now().toISOString();
|
|
1720
1750
|
let moved = 0;
|
|
1721
|
-
for (
|
|
1722
|
-
const batch = await store.migrateBatch({ flowId, toVersionId, map: removedMap, limit:
|
|
1723
|
-
if (batch.moved.length === 0) break;
|
|
1751
|
+
for (const part of chunks(chats.map((chat) => chat.chatId), MIGRATE_BATCH)) {
|
|
1752
|
+
const batch = await store.migrateBatch({ flowId, toVersionId, map: removedMap, chatIds: part, limit: part.length, at });
|
|
1724
1753
|
moved += batch.moved.length;
|
|
1725
1754
|
for (const chatId of batch.moved) {
|
|
1726
1755
|
const record = await store.getChat(chatId);
|
|
@@ -2376,7 +2405,8 @@ function buildAgentFlow(options, modules = {}) {
|
|
|
2376
2405
|
now: clock,
|
|
2377
2406
|
checkedFlow: (loaded) => checkedFlow(loaded.flowId, loaded.versionId, loaded.flow),
|
|
2378
2407
|
saveVersion: saveChecked,
|
|
2379
|
-
rescheduleMigrated
|
|
2408
|
+
rescheduleMigrated,
|
|
2409
|
+
chatData
|
|
2380
2410
|
})
|
|
2381
2411
|
);
|
|
2382
2412
|
const background = /* @__PURE__ */ new Set();
|
|
@@ -2603,6 +2633,15 @@ function buildAgentFlow(options, modules = {}) {
|
|
|
2603
2633
|
const run = storedRun(values[FLOW_STATE_KEY]);
|
|
2604
2634
|
if (run) await reschedule(ownerId, chatId, run, run.answerId ?? null, at);
|
|
2605
2635
|
}
|
|
2636
|
+
async function chatData(chat) {
|
|
2637
|
+
if (!port && !attachment) return {};
|
|
2638
|
+
try {
|
|
2639
|
+
return await (await portOf(chat)).getChatData(chat.ownerId, chat.chatId);
|
|
2640
|
+
} catch (error) {
|
|
2641
|
+
if (error instanceof ChatNotFoundError4) return {};
|
|
2642
|
+
throw error;
|
|
2643
|
+
}
|
|
2644
|
+
}
|
|
2606
2645
|
async function rescheduleMigrated({ record, flow, map }) {
|
|
2607
2646
|
try {
|
|
2608
2647
|
const { values } = await (await portOf(record)).getChatState(record.ownerId, record.chatId);
|
|
@@ -3029,9 +3068,14 @@ function memoryFlowStore() {
|
|
|
3029
3068
|
}
|
|
3030
3069
|
return [...counts.values()].sort(byVersionAndState);
|
|
3031
3070
|
},
|
|
3071
|
+
async listFlowChats({ flowId, exceptVersionId, chatIds }) {
|
|
3072
|
+
const only = chatIds && new Set(chatIds);
|
|
3073
|
+
return [...chats.values()].filter((row) => row.flowId === flowId && row.versionId !== exceptVersionId && (!only || only.has(row.chatId))).sort((left, right) => compareText(left.chatId, right.chatId)).map(({ chatId, ownerId, flowId: id, versionId, stateId, endedAt, endReason }) => ({ chatId, ownerId, flowId: id, versionId, stateId, endedAt, endReason }));
|
|
3074
|
+
},
|
|
3032
3075
|
async migrateBatch(input) {
|
|
3033
3076
|
const change = migratePending(input);
|
|
3034
|
-
const
|
|
3077
|
+
const only = input.chatIds && new Set(input.chatIds);
|
|
3078
|
+
const batch = [...chats.values()].filter((row) => row.flowId === input.flowId && row.versionId !== input.toVersionId && (!only || only.has(row.chatId))).sort((left, right) => compareText(left.chatId, right.chatId)).slice(0, input.limit);
|
|
3035
3079
|
for (const row of batch) {
|
|
3036
3080
|
row.versionId = input.toVersionId;
|
|
3037
3081
|
row.stateId = mappedState(row.stateId, input.map);
|