@ykurilov/agent-chat-background 0.57.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 +19 -0
- package/dist/postgres.js +6 -11
- package/dist/postgres.js.map +1 -1
- package/dist/types/store/postgres.d.ts +2 -2
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @ykurilov/agent-chat-background
|
|
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
|
+
|
|
3
22
|
## 0.57.0
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
package/dist/postgres.js
CHANGED
|
@@ -18,6 +18,7 @@ import { fileURLToPath } from "url";
|
|
|
18
18
|
import { and, asc, eq, inArray, isNull, lt, or, sql } from "drizzle-orm";
|
|
19
19
|
import { drizzle } from "drizzle-orm/node-postgres";
|
|
20
20
|
import { migrate } from "drizzle-orm/node-postgres/migrator";
|
|
21
|
+
import { migratePostgresSchema } from "@ykurilov/agent-chat-core";
|
|
21
22
|
|
|
22
23
|
// src/store/postgres-schema.ts
|
|
23
24
|
import { index, integer, jsonb, pgSchema, text, timestamp } from "drizzle-orm/pg-core";
|
|
@@ -81,17 +82,11 @@ function toColumns(patch) {
|
|
|
81
82
|
return columns;
|
|
82
83
|
}
|
|
83
84
|
async function migrateBackgroundTasksToLatest(pool, migrationsFolder) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
} finally {
|
|
90
|
-
await client.query("select pg_advisory_unlock($1)", [BACKGROUND_MIGRATION_LOCK_KEY]);
|
|
91
|
-
}
|
|
92
|
-
} finally {
|
|
93
|
-
client.release();
|
|
94
|
-
}
|
|
85
|
+
await migratePostgresSchema({
|
|
86
|
+
connect: () => pool.connect(),
|
|
87
|
+
lockKey: BACKGROUND_MIGRATION_LOCK_KEY,
|
|
88
|
+
migrate: (client) => migrate(drizzle(client), { migrationsFolder, migrationsSchema: "agent_chat", migrationsTable: BACKGROUND_MIGRATIONS_TABLE })
|
|
89
|
+
});
|
|
95
90
|
}
|
|
96
91
|
function createPostgresTaskStore(pool, migrationsFolder) {
|
|
97
92
|
const db = drizzle(pool);
|
package/dist/postgres.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/postgres.ts","../src/store/postgres.ts","../src/store/postgres-schema.ts"],"sourcesContent":["import { fileURLToPath } from \"node:url\";\nimport type { Pool } from \"pg\";\nimport type { TaskStore } from \"./types\";\nimport { createPostgresTaskStore } from \"./store/postgres\";\n\nexport { BACKGROUND_MIGRATIONS_TABLE, BACKGROUND_MIGRATION_LOCK_KEY } from \"./store/migration-settings\";\nexport { backgroundTasks } from \"./store/postgres-schema\";\n\n/** Миграции лежат в пакете рядом с `dist`: `drizzle/postgres`. */\nconst MIGRATIONS_FOLDER = fileURLToPath(new URL(\"../drizzle/postgres\", import.meta.url));\n\n/**\n * Задачи фона в PostgreSQL на пуле приложения (схема `agent_chat`, та же база, что у хранилища чатов).\n * Таблица приводится к последней миграции при первом обращении. Пул закрывает приложение.\n */\nexport function postgresTaskStore(pool: Pool): TaskStore {\n return createPostgresTaskStore(pool, MIGRATIONS_FOLDER);\n}\n","import { and, asc, eq, inArray, isNull, lt, or, sql } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/node-postgres\";\nimport { migrate } from \"drizzle-orm/node-postgres/migrator\";\nimport type { Pool } from \"pg\";\nimport type { TaskCondition, TaskPatch, TaskRecord, TaskStore } from \"../types\";\nimport { BACKGROUND_MIGRATION_LOCK_KEY, BACKGROUND_MIGRATIONS_TABLE } from \"./migration-settings\";\nimport { backgroundTasks } from \"./postgres-schema\";\nimport { meetsCondition, NOTICE_STATUSES, patchEntries, RUNNING_STATUSES, withoutEmptyFields, withoutNul } from \"./task-store\";\n\ntype Row = typeof backgroundTasks.$inferSelect;\n\nconst TIME_FIELDS = [\"leaseUntil\", \"createdAt\", \"startedAt\", \"finishedAt\", \"deliveredAt\"] as const;\nconst toDate = (iso: string | null) => (iso === null ? null : new Date(iso));\nconst toIso = (date: Date | null) => (date === null ? undefined : date.toISOString());\n\nfunction toRecord(row: Row): TaskRecord {\n return withoutEmptyFields({\n ...row,\n leaseUntil: toIso(row.leaseUntil),\n createdAt: toIso(row.createdAt)!,\n startedAt: toIso(row.startedAt),\n finishedAt: toIso(row.finishedAt),\n deliveredAt: toIso(row.deliveredAt),\n } as TaskRecord);\n}\n\n/** Запись или правка как колонки таблицы: времена — `Date`, без символа NUL. */\nfunction toColumns(patch: TaskPatch | TaskRecord): Record<string, unknown> {\n const columns: Record<string, unknown> = {};\n for (const [key, value] of patchEntries(patch)) {\n columns[key] = (TIME_FIELDS as readonly string[]).includes(key) ? toDate(value as string | null) : withoutNul(value);\n }\n return columns;\n}\n\n/**\n * Приводит таблицу задач к последней миграции. Идёт на одном соединении под блокировкой базы:\n * если несколько серверов стартуют сразу, второй ждёт первого и видит применённые миграции.\n */\nexport async function migrateBackgroundTasksToLatest(pool: Pool, migrationsFolder: string): Promise<void> {\n const client = await pool.connect();\n try {\n await client.query(\"select pg_advisory_lock($1)\", [BACKGROUND_MIGRATION_LOCK_KEY]);\n try {\n await migrate(drizzle(client), { migrationsFolder, migrationsSchema: \"agent_chat\", migrationsTable: BACKGROUND_MIGRATIONS_TABLE });\n } finally {\n await client.query(\"select pg_advisory_unlock($1)\", [BACKGROUND_MIGRATION_LOCK_KEY]);\n }\n } finally {\n client.release();\n }\n}\n\n/**\n * Хранилище задач в PostgreSQL (схема `agent_chat`) на пуле приложения. Миграция идёт при первом обращении,\n * каждая операция её дожидается. Закрытие пул не закрывает: он принадлежит приложению.\n */\nexport function createPostgresTaskStore(pool: Pool, migrationsFolder: string): TaskStore {\n const db = drizzle(pool);\n const ready = migrateBackgroundTasksToLatest(pool, migrationsFolder);\n // Ошибка миграции придёт в каждую операцию; без этого она всплыла бы ещё и как необработанная.\n ready.catch(() => {});\n const owned = (ownerId: string, taskId: string) => and(eq(backgroundTasks.id, taskId), eq(backgroundTasks.ownerId, ownerId));\n const isRunning = inArray(backgroundTasks.status, [...RUNNING_STATUSES]);\n /** Условие записи — в том же `update … where`: проверка и запись одним действием базы. */\n const condition = (when: TaskCondition | undefined) =>\n and(\n when?.leaseOwner !== undefined ? eq(backgroundTasks.leaseOwner, when.leaseOwner) : undefined,\n when?.statuses !== undefined ? inArray(backgroundTasks.status, [...when.statuses]) : undefined,\n when?.pendingApprovalId !== undefined ? sql`${backgroundTasks.progress}->'pendingApproval'->>'approvalId' = ${when.pendingApprovalId}` : undefined,\n );\n\n async function get(ownerId: string, taskId: string): Promise<TaskRecord | null> {\n await ready;\n const [row] = await db.select().from(backgroundTasks).where(owned(ownerId, taskId));\n return row ? toRecord(row) : null;\n }\n\n return {\n async create(task) {\n await ready;\n await db.insert(backgroundTasks).values(toColumns(task) as Row);\n },\n get,\n async update(ownerId, taskId, patch, when) {\n const columns = toColumns(patch);\n if (Object.keys(columns).length === 0) {\n const task = await get(ownerId, taskId);\n return task && meetsCondition(task, when) ? task : null;\n }\n await ready;\n const [row] = await db.update(backgroundTasks).set(columns).where(and(owned(ownerId, taskId), condition(when))).returning();\n return row ? toRecord(row) : null;\n },\n async listForChat(ownerId, chatId) {\n await ready;\n const rows = await db\n .select()\n .from(backgroundTasks)\n .where(and(eq(backgroundTasks.ownerId, ownerId), eq(backgroundTasks.chatId, chatId)))\n .orderBy(asc(backgroundTasks.createdAt), asc(backgroundTasks.id));\n return rows.map(toRecord);\n },\n async countActive(ownerId) {\n await ready;\n const [row] = await db\n .select({ count: sql<number>`count(*)::int` })\n .from(backgroundTasks)\n .where(and(eq(backgroundTasks.ownerId, ownerId), isRunning));\n return Number(row?.count ?? 0);\n },\n async claim(taskId, leaseOwner, leaseUntil, now) {\n await ready;\n const nowDate = new Date(now);\n // Одно условное обновление: вторая копия ждёт строку, перепроверяет условие на новой версии и ничего не берёт.\n const [row] = await db\n .update(backgroundTasks)\n .set({\n leaseOwner,\n leaseUntil: new Date(leaseUntil),\n status: sql`case when ${isRunning} then 'running' else ${backgroundTasks.status} end`,\n attempt: sql`case when ${isRunning} then ${backgroundTasks.attempt} + 1 else ${backgroundTasks.attempt} end`,\n startedAt: sql`case when ${isRunning} then coalesce(${backgroundTasks.startedAt}, ${nowDate}) else ${backgroundTasks.startedAt} end`,\n })\n .where(\n and(\n eq(backgroundTasks.id, taskId),\n isNull(backgroundTasks.deliveredAt),\n or(eq(backgroundTasks.leaseOwner, leaseOwner), isNull(backgroundTasks.leaseUntil), lt(backgroundTasks.leaseUntil, nowDate)),\n ),\n )\n .returning();\n return row ? toRecord(row) : null;\n },\n async renewLease(taskId, leaseOwner, leaseUntil) {\n await ready;\n const renewed = await db\n .update(backgroundTasks)\n .set({ leaseUntil: new Date(leaseUntil) })\n .where(and(eq(backgroundTasks.id, taskId), eq(backgroundTasks.leaseOwner, leaseOwner), isNull(backgroundTasks.deliveredAt)))\n .returning({ id: backgroundTasks.id });\n return renewed.length > 0;\n },\n async listExpired(now) {\n await ready;\n const rows = await db\n .select()\n .from(backgroundTasks)\n .where(and(isNull(backgroundTasks.deliveredAt), or(isNull(backgroundTasks.leaseUntil), lt(backgroundTasks.leaseUntil, new Date(now)))))\n .orderBy(asc(backgroundTasks.createdAt));\n return rows.map(toRecord);\n },\n async listUndelivered(ownerId, chatId) {\n await ready;\n const rows = await db\n .select()\n .from(backgroundTasks)\n .where(\n and(\n eq(backgroundTasks.ownerId, ownerId),\n eq(backgroundTasks.chatId, chatId),\n isNull(backgroundTasks.deliveredAt),\n inArray(backgroundTasks.status, [...NOTICE_STATUSES]),\n ),\n )\n .orderBy(asc(backgroundTasks.createdAt), asc(backgroundTasks.id));\n return rows.map(toRecord);\n },\n async markDelivered(taskIds, deliveryKey, at) {\n if (taskIds.length === 0) return;\n await ready;\n await db\n .update(backgroundTasks)\n .set({ deliveredAt: new Date(at), leaseOwner: null, leaseUntil: null })\n .where(\n and(\n inArray(backgroundTasks.id, [...taskIds]),\n eq(backgroundTasks.deliveryKey, deliveryKey),\n inArray(backgroundTasks.status, [...NOTICE_STATUSES]),\n ),\n );\n },\n async deleteForChat(ownerId, chatId) {\n await ready;\n await db.delete(backgroundTasks).where(and(eq(backgroundTasks.ownerId, ownerId), eq(backgroundTasks.chatId, chatId)));\n },\n async close() {\n // Дожидаемся миграции: пул приложения можно закрывать сразу после.\n await ready.catch(() => {});\n },\n };\n}\n","import type { HelperProgress, TaskStatus } from \"@ykurilov/agent-chat-core\";\nimport type { ModelMessage } from \"ai\";\nimport { index, integer, jsonb, pgSchema, text, timestamp } from \"drizzle-orm/pg-core\";\n\n/** Та же схема базы, что у хранилища чатов: у приложения одна база и одна схема `agent_chat`. */\nconst agentChatSchema = pgSchema(\"agent_chat\");\n\nconst moment = (name: string) => timestamp(name, { mode: \"date\", precision: 3, withTimezone: true });\n\nexport const backgroundTasks = agentChatSchema.table(\n \"background_tasks\",\n {\n id: text(\"id\").primaryKey(),\n ownerId: text(\"owner_id\").notNull(),\n chatId: text(\"chat_id\").notNull(),\n messageId: text(\"message_id\").notNull(),\n toolCallId: text(\"tool_call_id\").notNull(),\n channel: text(\"channel\", { enum: [\"web\", \"telegram\", \"vk\", \"max\"] }).notNull(),\n kind: text(\"kind\", { enum: [\"tool\", \"helper\"] }).notNull(),\n name: text(\"name\").notNull(),\n input: jsonb(\"input\"),\n title: text(\"title\").notNull(),\n status: text(\"status\").$type<TaskStatus>().notNull(),\n /** Шаги помощника: с них задача продолжается после перезапуска. */\n helperMessages: jsonb(\"helper_messages\").$type<ModelMessage[]>(),\n progress: jsonb(\"progress\").$type<HelperProgress>(),\n result: jsonb(\"result\"),\n error: text(\"error\"),\n leaseOwner: text(\"lease_owner\"),\n leaseUntil: moment(\"lease_until\"),\n attempt: integer(\"attempt\").notNull().default(0),\n createdAt: moment(\"created_at\").notNull(),\n startedAt: moment(\"started_at\"),\n finishedAt: moment(\"finished_at\"),\n deliveredAt: moment(\"delivered_at\"),\n /** Ключ служебного сообщения, с которым задачу начали доставлять. */\n deliveryKey: text(\"delivery_key\"),\n },\n (table) => [\n index(\"background_tasks_status_lease_idx\").on(table.status, table.leaseUntil),\n index(\"background_tasks_owner_status_idx\").on(table.ownerId, table.status),\n index(\"background_tasks_chat_delivered_idx\").on(table.chatId, table.deliveredAt),\n ],\n);\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/postgres.ts","../src/store/postgres.ts","../src/store/postgres-schema.ts"],"sourcesContent":["import { fileURLToPath } from \"node:url\";\nimport type { Pool } from \"pg\";\nimport type { TaskStore } from \"./types\";\nimport { createPostgresTaskStore } from \"./store/postgres\";\n\nexport { BACKGROUND_MIGRATIONS_TABLE, BACKGROUND_MIGRATION_LOCK_KEY } from \"./store/migration-settings\";\nexport { backgroundTasks } from \"./store/postgres-schema\";\n\n/** Миграции лежат в пакете рядом с `dist`: `drizzle/postgres`. */\nconst MIGRATIONS_FOLDER = fileURLToPath(new URL(\"../drizzle/postgres\", import.meta.url));\n\n/**\n * Задачи фона в PostgreSQL на пуле приложения (схема `agent_chat`, та же база, что у хранилища чатов).\n * Таблица приводится к последней миграции при первом обращении. Пул закрывает приложение.\n */\nexport function postgresTaskStore(pool: Pool): TaskStore {\n return createPostgresTaskStore(pool, MIGRATIONS_FOLDER);\n}\n","import { and, asc, eq, inArray, isNull, lt, or, sql } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/node-postgres\";\nimport { migrate } from \"drizzle-orm/node-postgres/migrator\";\nimport { migratePostgresSchema } from \"@ykurilov/agent-chat-core\";\nimport type { Pool } from \"pg\";\nimport type { TaskCondition, TaskPatch, TaskRecord, TaskStore } from \"../types\";\nimport { BACKGROUND_MIGRATION_LOCK_KEY, BACKGROUND_MIGRATIONS_TABLE } from \"./migration-settings\";\nimport { backgroundTasks } from \"./postgres-schema\";\nimport { meetsCondition, NOTICE_STATUSES, patchEntries, RUNNING_STATUSES, withoutEmptyFields, withoutNul } from \"./task-store\";\n\ntype Row = typeof backgroundTasks.$inferSelect;\n\nconst TIME_FIELDS = [\"leaseUntil\", \"createdAt\", \"startedAt\", \"finishedAt\", \"deliveredAt\"] as const;\nconst toDate = (iso: string | null) => (iso === null ? null : new Date(iso));\nconst toIso = (date: Date | null) => (date === null ? undefined : date.toISOString());\n\nfunction toRecord(row: Row): TaskRecord {\n return withoutEmptyFields({\n ...row,\n leaseUntil: toIso(row.leaseUntil),\n createdAt: toIso(row.createdAt)!,\n startedAt: toIso(row.startedAt),\n finishedAt: toIso(row.finishedAt),\n deliveredAt: toIso(row.deliveredAt),\n } as TaskRecord);\n}\n\n/** Запись или правка как колонки таблицы: времена — `Date`, без символа NUL. */\nfunction toColumns(patch: TaskPatch | TaskRecord): Record<string, unknown> {\n const columns: Record<string, unknown> = {};\n for (const [key, value] of patchEntries(patch)) {\n columns[key] = (TIME_FIELDS as readonly string[]).includes(key) ? toDate(value as string | null) : withoutNul(value);\n }\n return columns;\n}\n\n/**\n * Приводит таблицу задач к последней миграции. Схема `agent_chat` создаётся под общей блокировкой всех пакетов, миграции — под своей:\n * копии пакета ждут друг друга, а разные пакеты стартуют разом на пустой базе без гонки за схему.\n */\nexport async function migrateBackgroundTasksToLatest(pool: Pool, migrationsFolder: string): Promise<void> {\n await migratePostgresSchema({\n connect: () => pool.connect(),\n lockKey: BACKGROUND_MIGRATION_LOCK_KEY,\n migrate: (client) => migrate(drizzle(client), { migrationsFolder, migrationsSchema: \"agent_chat\", migrationsTable: BACKGROUND_MIGRATIONS_TABLE }),\n });\n}\n\n/**\n * Хранилище задач в PostgreSQL (схема `agent_chat`) на пуле приложения. Миграция идёт при первом обращении,\n * каждая операция её дожидается. Закрытие пул не закрывает: он принадлежит приложению.\n */\nexport function createPostgresTaskStore(pool: Pool, migrationsFolder: string): TaskStore {\n const db = drizzle(pool);\n const ready = migrateBackgroundTasksToLatest(pool, migrationsFolder);\n // Ошибка миграции придёт в каждую операцию; без этого она всплыла бы ещё и как необработанная.\n ready.catch(() => {});\n const owned = (ownerId: string, taskId: string) => and(eq(backgroundTasks.id, taskId), eq(backgroundTasks.ownerId, ownerId));\n const isRunning = inArray(backgroundTasks.status, [...RUNNING_STATUSES]);\n /** Условие записи — в том же `update … where`: проверка и запись одним действием базы. */\n const condition = (when: TaskCondition | undefined) =>\n and(\n when?.leaseOwner !== undefined ? eq(backgroundTasks.leaseOwner, when.leaseOwner) : undefined,\n when?.statuses !== undefined ? inArray(backgroundTasks.status, [...when.statuses]) : undefined,\n when?.pendingApprovalId !== undefined ? sql`${backgroundTasks.progress}->'pendingApproval'->>'approvalId' = ${when.pendingApprovalId}` : undefined,\n );\n\n async function get(ownerId: string, taskId: string): Promise<TaskRecord | null> {\n await ready;\n const [row] = await db.select().from(backgroundTasks).where(owned(ownerId, taskId));\n return row ? toRecord(row) : null;\n }\n\n return {\n async create(task) {\n await ready;\n await db.insert(backgroundTasks).values(toColumns(task) as Row);\n },\n get,\n async update(ownerId, taskId, patch, when) {\n const columns = toColumns(patch);\n if (Object.keys(columns).length === 0) {\n const task = await get(ownerId, taskId);\n return task && meetsCondition(task, when) ? task : null;\n }\n await ready;\n const [row] = await db.update(backgroundTasks).set(columns).where(and(owned(ownerId, taskId), condition(when))).returning();\n return row ? toRecord(row) : null;\n },\n async listForChat(ownerId, chatId) {\n await ready;\n const rows = await db\n .select()\n .from(backgroundTasks)\n .where(and(eq(backgroundTasks.ownerId, ownerId), eq(backgroundTasks.chatId, chatId)))\n .orderBy(asc(backgroundTasks.createdAt), asc(backgroundTasks.id));\n return rows.map(toRecord);\n },\n async countActive(ownerId) {\n await ready;\n const [row] = await db\n .select({ count: sql<number>`count(*)::int` })\n .from(backgroundTasks)\n .where(and(eq(backgroundTasks.ownerId, ownerId), isRunning));\n return Number(row?.count ?? 0);\n },\n async claim(taskId, leaseOwner, leaseUntil, now) {\n await ready;\n const nowDate = new Date(now);\n // Одно условное обновление: вторая копия ждёт строку, перепроверяет условие на новой версии и ничего не берёт.\n const [row] = await db\n .update(backgroundTasks)\n .set({\n leaseOwner,\n leaseUntil: new Date(leaseUntil),\n status: sql`case when ${isRunning} then 'running' else ${backgroundTasks.status} end`,\n attempt: sql`case when ${isRunning} then ${backgroundTasks.attempt} + 1 else ${backgroundTasks.attempt} end`,\n startedAt: sql`case when ${isRunning} then coalesce(${backgroundTasks.startedAt}, ${nowDate}) else ${backgroundTasks.startedAt} end`,\n })\n .where(\n and(\n eq(backgroundTasks.id, taskId),\n isNull(backgroundTasks.deliveredAt),\n or(eq(backgroundTasks.leaseOwner, leaseOwner), isNull(backgroundTasks.leaseUntil), lt(backgroundTasks.leaseUntil, nowDate)),\n ),\n )\n .returning();\n return row ? toRecord(row) : null;\n },\n async renewLease(taskId, leaseOwner, leaseUntil) {\n await ready;\n const renewed = await db\n .update(backgroundTasks)\n .set({ leaseUntil: new Date(leaseUntil) })\n .where(and(eq(backgroundTasks.id, taskId), eq(backgroundTasks.leaseOwner, leaseOwner), isNull(backgroundTasks.deliveredAt)))\n .returning({ id: backgroundTasks.id });\n return renewed.length > 0;\n },\n async listExpired(now) {\n await ready;\n const rows = await db\n .select()\n .from(backgroundTasks)\n .where(and(isNull(backgroundTasks.deliveredAt), or(isNull(backgroundTasks.leaseUntil), lt(backgroundTasks.leaseUntil, new Date(now)))))\n .orderBy(asc(backgroundTasks.createdAt));\n return rows.map(toRecord);\n },\n async listUndelivered(ownerId, chatId) {\n await ready;\n const rows = await db\n .select()\n .from(backgroundTasks)\n .where(\n and(\n eq(backgroundTasks.ownerId, ownerId),\n eq(backgroundTasks.chatId, chatId),\n isNull(backgroundTasks.deliveredAt),\n inArray(backgroundTasks.status, [...NOTICE_STATUSES]),\n ),\n )\n .orderBy(asc(backgroundTasks.createdAt), asc(backgroundTasks.id));\n return rows.map(toRecord);\n },\n async markDelivered(taskIds, deliveryKey, at) {\n if (taskIds.length === 0) return;\n await ready;\n await db\n .update(backgroundTasks)\n .set({ deliveredAt: new Date(at), leaseOwner: null, leaseUntil: null })\n .where(\n and(\n inArray(backgroundTasks.id, [...taskIds]),\n eq(backgroundTasks.deliveryKey, deliveryKey),\n inArray(backgroundTasks.status, [...NOTICE_STATUSES]),\n ),\n );\n },\n async deleteForChat(ownerId, chatId) {\n await ready;\n await db.delete(backgroundTasks).where(and(eq(backgroundTasks.ownerId, ownerId), eq(backgroundTasks.chatId, chatId)));\n },\n async close() {\n // Дожидаемся миграции: пул приложения можно закрывать сразу после.\n await ready.catch(() => {});\n },\n };\n}\n","import type { HelperProgress, TaskStatus } from \"@ykurilov/agent-chat-core\";\nimport type { ModelMessage } from \"ai\";\nimport { index, integer, jsonb, pgSchema, text, timestamp } from \"drizzle-orm/pg-core\";\n\n/** Та же схема базы, что у хранилища чатов: у приложения одна база и одна схема `agent_chat`. */\nconst agentChatSchema = pgSchema(\"agent_chat\");\n\nconst moment = (name: string) => timestamp(name, { mode: \"date\", precision: 3, withTimezone: true });\n\nexport const backgroundTasks = agentChatSchema.table(\n \"background_tasks\",\n {\n id: text(\"id\").primaryKey(),\n ownerId: text(\"owner_id\").notNull(),\n chatId: text(\"chat_id\").notNull(),\n messageId: text(\"message_id\").notNull(),\n toolCallId: text(\"tool_call_id\").notNull(),\n channel: text(\"channel\", { enum: [\"web\", \"telegram\", \"vk\", \"max\"] }).notNull(),\n kind: text(\"kind\", { enum: [\"tool\", \"helper\"] }).notNull(),\n name: text(\"name\").notNull(),\n input: jsonb(\"input\"),\n title: text(\"title\").notNull(),\n status: text(\"status\").$type<TaskStatus>().notNull(),\n /** Шаги помощника: с них задача продолжается после перезапуска. */\n helperMessages: jsonb(\"helper_messages\").$type<ModelMessage[]>(),\n progress: jsonb(\"progress\").$type<HelperProgress>(),\n result: jsonb(\"result\"),\n error: text(\"error\"),\n leaseOwner: text(\"lease_owner\"),\n leaseUntil: moment(\"lease_until\"),\n attempt: integer(\"attempt\").notNull().default(0),\n createdAt: moment(\"created_at\").notNull(),\n startedAt: moment(\"started_at\"),\n finishedAt: moment(\"finished_at\"),\n deliveredAt: moment(\"delivered_at\"),\n /** Ключ служебного сообщения, с которым задачу начали доставлять. */\n deliveryKey: text(\"delivery_key\"),\n },\n (table) => [\n index(\"background_tasks_status_lease_idx\").on(table.status, table.leaseUntil),\n index(\"background_tasks_owner_status_idx\").on(table.ownerId, table.status),\n index(\"background_tasks_chat_delivered_idx\").on(table.chatId, table.deliveredAt),\n ],\n);\n"],"mappings":"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","names":[]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Pool } from "pg";
|
|
2
2
|
import type { TaskStore } from "../types.js";
|
|
3
3
|
/**
|
|
4
|
-
* Приводит таблицу задач к последней миграции.
|
|
5
|
-
*
|
|
4
|
+
* Приводит таблицу задач к последней миграции. Схема `agent_chat` создаётся под общей блокировкой всех пакетов, миграции — под своей:
|
|
5
|
+
* копии пакета ждут друг друга, а разные пакеты стартуют разом на пустой базе без гонки за схему.
|
|
6
6
|
*/
|
|
7
7
|
export declare function migrateBackgroundTasksToLatest(pool: Pool, migrationsFolder: string): Promise<void>;
|
|
8
8
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ykurilov/agent-chat-background",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.58.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"ai": "7.0.122",
|
|
49
49
|
"drizzle-orm": "^0.45.3",
|
|
50
|
-
"@ykurilov/agent-chat-core": "0.
|
|
50
|
+
"@ykurilov/agent-chat-core": "0.58.0"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"better-sqlite3": "^13.0.3",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"better-sqlite3": "^13.0.3",
|
|
69
69
|
"drizzle-kit": "^0.31.11",
|
|
70
70
|
"pg": "^8.23.0",
|
|
71
|
-
"@ykurilov/agent-chat-server": "0.
|
|
71
|
+
"@ykurilov/agent-chat-server": "0.58.0"
|
|
72
72
|
},
|
|
73
73
|
"scripts": {
|
|
74
74
|
"build": "node ../../scripts/build-package.mjs",
|