@ykurilov/agent-chat-sandbox 0.57.0 → 0.59.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 +30 -0
- package/dist/postgres.js +6 -11
- package/dist/postgres.js.map +1 -1
- package/dist/types/journal/postgres-journal.d.ts +2 -2
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# @ykurilov/agent-chat-sandbox
|
|
2
2
|
|
|
3
|
+
## 0.59.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [fc1f387]
|
|
8
|
+
- @ykurilov/agent-chat-core@0.59.0
|
|
9
|
+
- @ykurilov/agent-chat-server@0.59.0
|
|
10
|
+
- @ykurilov/agent-chat-react@0.59.0
|
|
11
|
+
|
|
12
|
+
## 0.58.0
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- 760962e: Хранилища пакетов на PostgreSQL можно открывать одновременно на совсем пустой базе. Раньше пакеты разом создавали
|
|
17
|
+
схему `agent_chat`, проигравший падал на `CREATE SCHEMA IF NOT EXISTS`, и его хранилище не работало до перезапуска.
|
|
18
|
+
Теперь схема создаётся под общей блокировкой всех пакетов, она держится только на создании схемы. Миграции идут под
|
|
19
|
+
своей блокировкой пакета: разные пакеты друг друга не ждут, взаимной блокировки нет. Столкновение «такое уже есть»
|
|
20
|
+
повторяется само. Новое в core: `migratePostgresSchema({ connect, lockKey, migrate })` — для своих пакетов на той же
|
|
21
|
+
схеме.
|
|
22
|
+
|
|
23
|
+
### Как обновить приложение
|
|
24
|
+
|
|
25
|
+
Ничего делать не нужно.
|
|
26
|
+
- Updated dependencies [332d90e]
|
|
27
|
+
- Updated dependencies [2ed600d]
|
|
28
|
+
- Updated dependencies [760962e]
|
|
29
|
+
- @ykurilov/agent-chat-core@0.58.0
|
|
30
|
+
- @ykurilov/agent-chat-server@0.58.0
|
|
31
|
+
- @ykurilov/agent-chat-react@0.58.0
|
|
32
|
+
|
|
3
33
|
## 0.57.0
|
|
4
34
|
|
|
5
35
|
### Patch Changes
|
package/dist/postgres.js
CHANGED
|
@@ -10,6 +10,7 @@ import pg from "pg";
|
|
|
10
10
|
import { and, asc, eq, inArray, lt, notExists, or } from "drizzle-orm";
|
|
11
11
|
import { drizzle } from "drizzle-orm/node-postgres";
|
|
12
12
|
import { migrate } from "drizzle-orm/node-postgres/migrator";
|
|
13
|
+
import { migratePostgresSchema } from "@ykurilov/agent-chat-core";
|
|
13
14
|
|
|
14
15
|
// src/journal/postgres-schema.ts
|
|
15
16
|
import { boolean, customType, doublePrecision, index, integer, jsonb, pgSchema, text, timestamp } from "drizzle-orm/pg-core";
|
|
@@ -102,17 +103,11 @@ var sandboxAnswerEdits = agentChatSchema.table(
|
|
|
102
103
|
var MIGRATION_LOCK_KEY = 7311900443;
|
|
103
104
|
var MIGRATIONS_TABLE = "__sandbox_migrations";
|
|
104
105
|
async function migrateJournalToLatest(pool, migrationsFolder2) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
} finally {
|
|
111
|
-
await client.query("select pg_advisory_unlock($1)", [MIGRATION_LOCK_KEY]);
|
|
112
|
-
}
|
|
113
|
-
} finally {
|
|
114
|
-
client.release();
|
|
115
|
-
}
|
|
106
|
+
await migratePostgresSchema({
|
|
107
|
+
connect: () => pool.connect(),
|
|
108
|
+
lockKey: MIGRATION_LOCK_KEY,
|
|
109
|
+
migrate: (client) => migrate(drizzle(client), { migrationsFolder: migrationsFolder2, migrationsSchema: "agent_chat", migrationsTable: MIGRATIONS_TABLE })
|
|
110
|
+
});
|
|
116
111
|
}
|
|
117
112
|
function createPostgresJournalStore(pool) {
|
|
118
113
|
const db = drizzle(pool);
|
package/dist/postgres.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/postgres.ts","../src/journal/postgres-journal.ts","../src/journal/postgres-schema.ts"],"sourcesContent":["import { fileURLToPath } from \"node:url\";\nimport pg from \"pg\";\nimport type { JournalOptions } from \"./journal/journal\";\nimport { createSandboxJournal } from \"./journal/journal\";\nimport { createPostgresJournalStore, migrateJournalToLatest } from \"./journal/postgres-journal\";\nimport type { SandboxJournal } from \"./types\";\n\nexport type { LogError, SandboxJournal } from \"./types\";\n\nexport type PostgresSandboxJournalOptions = JournalOptions & {\n /** Например `postgres://user:password@host:5432/database`; та же база, что у `createPostgresChatStorage`. */\n connectionString: string;\n /** Сколько соединений держать открытыми. По умолчанию 5. */\n poolSize?: number;\n};\n\nconst DEFAULT_POOL_SIZE = 5;\nconst migrationsFolder = fileURLToPath(new URL(\"../drizzle/postgres\", import.meta.url));\n\n/**\n * Журнал песочницы в PostgreSQL. Таблицы лежат в схеме `agent_chat`; схема приводится\n * к последней миграции при создании, под блокировкой базы.\n */\nexport async function createPostgresSandboxJournal(options: PostgresSandboxJournalOptions): Promise<SandboxJournal> {\n const logError = options.logError ?? ((message: string) => console.error(message));\n const pool = new pg.Pool({ connectionString: options.connectionString, max: options.poolSize ?? DEFAULT_POOL_SIZE });\n // Без обработчика оборвавшееся соединение простаивающего пула роняет процесс.\n pool.on(\"error\", (error) => logError(`журнал песочницы: соединение с PostgreSQL оборвалось: ${error.message}`));\n try {\n await migrateJournalToLatest(pool, migrationsFolder);\n } catch (error) {\n await pool.end().catch(() => {});\n throw failure(error, options.connectionString);\n }\n return createSandboxJournal(createPostgresJournalStore(pool), { ...options, logError });\n}\n\n/** Ошибка без строки подключения: пароль в текст не попадает. */\nfunction failure(cause: unknown, connectionString: string): Error {\n const reason = cause instanceof Error ? cause.message : String(cause);\n return new Error(`Не удалось подготовить журнал песочницы в PostgreSQL: ${reason.split(connectionString).join(\"<строка подключения>\")}`);\n}\n","import { and, asc, eq, inArray, lt, notExists, or } 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 { BlobRow, JournalStore, ModelCallRow, ToolCallRow } from \"./journal-store\";\nimport { sandboxAnswerEdits, sandboxBlobRefs, sandboxBlobs, sandboxModelCalls, sandboxSettings, sandboxToolCalls } from \"./postgres-schema\";\n\n/** Постоянное число для блокировки базы миграциями журнала; не совпадает с ключом хранилища чатов. */\nconst MIGRATION_LOCK_KEY = 7_311_900_443;\n\n/** Своя таблица учёта: хранилище чатов живёт в той же схеме и ведёт свою (`__drizzle_migrations`). */\nconst MIGRATIONS_TABLE = \"__sandbox_migrations\";\n\n/**\n * Приводит таблицы журнала к последней миграции. Идёт на одном соединении под блокировкой базы:\n * если несколько серверов стартуют сразу, второй ждёт первого и видит применённые миграции.\n */\nexport async function migrateJournalToLatest(pool: Pool, migrationsFolder: string): Promise<void> {\n const client = await pool.connect();\n try {\n await client.query(\"select pg_advisory_lock($1)\", [MIGRATION_LOCK_KEY]);\n try {\n await migrate(drizzle(client), { migrationsFolder, migrationsSchema: \"agent_chat\", migrationsTable: MIGRATIONS_TABLE });\n } finally {\n await client.query(\"select pg_advisory_unlock($1)\", [MIGRATION_LOCK_KEY]);\n }\n } finally {\n client.release();\n }\n}\n\n/** Хранилище журнала в PostgreSQL (схема `agent_chat`). Закрытие закрывает и пул. */\nexport function createPostgresJournalStore(pool: Pool): JournalStore {\n const db = drizzle(pool);\n type Executor = Pick<typeof db, \"delete\" | \"select\" | \"insert\">;\n\n /** Куски, на которые не осталось ссылок. */\n async function dropUnreferencedBlobs(tx: Executor): Promise<void> {\n await tx.delete(sandboxBlobs).where(notExists(tx.select({ one: sandboxBlobRefs.hash }).from(sandboxBlobRefs).where(eq(sandboxBlobRefs.hash, sandboxBlobs.hash))));\n }\n\n /**\n * Куски обращения пишутся одним запросом: в длинном чате их сотни (вся история), и запрос на каждый кусок\n * превращал одну запись журнала в сотни походов к базе. Порядок по хэшу одинаков у всех записей —\n * параллельные записи с общими кусками не ждут друг друга крест-накрест.\n */\n async function saveBlobs(tx: Executor, callId: string, row: { chatId: string; ownerId: string }, hashes: string[], blobs: BlobRow[]): Promise<void> {\n const unique = [...new Map(blobs.map((blob) => [blob.hash, blob])).values()].sort((left, right) => (left.hash < right.hash ? -1 : left.hash > right.hash ? 1 : 0));\n if (unique.length > 0) {\n await tx\n .insert(sandboxBlobs)\n .values(unique.map((blob) => ({ hash: blob.hash, data: Buffer.from(blob.data), bytes: blob.bytes, truncated: blob.truncated })))\n .onConflictDoNothing();\n }\n const refs = [...new Set(hashes)].map((hash) => ({ callId, hash, chatId: row.chatId, ownerId: row.ownerId }));\n if (refs.length > 0) await tx.insert(sandboxBlobRefs).values(refs);\n }\n\n return {\n async saveModelCall(row, blobs) {\n await db.transaction(async (tx) => {\n const inserted = await tx.insert(sandboxModelCalls).values({ ...row, error: withoutNul(row.error) }).onConflictDoNothing().returning({ id: sandboxModelCalls.id });\n if (inserted.length === 0) return;\n await saveBlobs(tx, row.id, row, [row.instructionsHash, ...row.messageHashes, row.toolsHash, row.settingsHash, row.responseHash], blobs);\n });\n },\n async saveToolCall(row, blobs) {\n await db.transaction(async (tx) => {\n const inserted = await tx.insert(sandboxToolCalls).values({ ...row, error: withoutNul(row.error) }).onConflictDoNothing().returning({ id: sandboxToolCalls.id });\n if (inserted.length === 0) return;\n await saveBlobs(tx, row.id, row, row.outputHash ? [row.inputHash, row.outputHash] : [row.inputHash], blobs);\n });\n },\n async listModelCalls(chatId) {\n return db.select().from(sandboxModelCalls).where(eq(sandboxModelCalls.chatId, chatId)).orderBy(asc(sandboxModelCalls.startedAt));\n },\n async listToolCalls(chatId) {\n return db.select().from(sandboxToolCalls).where(eq(sandboxToolCalls.chatId, chatId)).orderBy(asc(sandboxToolCalls.startedAt));\n },\n async findModelCall(id) {\n return (await db.select().from(sandboxModelCalls).where(eq(sandboxModelCalls.id, id)).limit(1))[0] ?? null;\n },\n async findToolCall(id) {\n return (await db.select().from(sandboxToolCalls).where(eq(sandboxToolCalls.id, id)).limit(1))[0] ?? null;\n },\n async loadBlobs(hashes) {\n if (hashes.length === 0) return [];\n return (await db.select().from(sandboxBlobs).where(inArray(sandboxBlobs.hash, hashes))).map(toBlobRow);\n },\n async loadBlobForOwner(hash, ownerId) {\n const owned = await db.select({ hash: sandboxBlobRefs.hash }).from(sandboxBlobRefs).where(and(eq(sandboxBlobRefs.hash, hash), eq(sandboxBlobRefs.ownerId, ownerId))).limit(1);\n if (owned.length === 0) return null;\n const row = (await db.select().from(sandboxBlobs).where(eq(sandboxBlobs.hash, hash)).limit(1))[0];\n return row ? toBlobRow(row) : null;\n },\n async getSettings(chatId) {\n return (await db.select().from(sandboxSettings).where(eq(sandboxSettings.chatId, chatId)).limit(1))[0] ?? null;\n },\n async setSettings(settings) {\n const clean = { ...settings, instructions: withoutNul(settings.instructions) };\n await db.insert(sandboxSettings).values(clean).onConflictDoUpdate({ target: sandboxSettings.chatId, set: clean });\n },\n async saveAnswerEdit(record) {\n await db.insert(sandboxAnswerEdits).values(record).onConflictDoUpdate({ target: sandboxAnswerEdits.messageId, set: record });\n },\n async listAnswerEdits(chatId) {\n return db.select().from(sandboxAnswerEdits).where(eq(sandboxAnswerEdits.chatId, chatId)).orderBy(asc(sandboxAnswerEdits.editedAt));\n },\n async deleteChat(chatId) {\n await db.transaction(async (tx) => {\n await tx.delete(sandboxBlobRefs).where(eq(sandboxBlobRefs.chatId, chatId));\n await tx.delete(sandboxModelCalls).where(eq(sandboxModelCalls.chatId, chatId));\n await tx.delete(sandboxToolCalls).where(eq(sandboxToolCalls.chatId, chatId));\n await tx.delete(sandboxSettings).where(eq(sandboxSettings.chatId, chatId));\n await tx.delete(sandboxAnswerEdits).where(eq(sandboxAnswerEdits.chatId, chatId));\n await dropUnreferencedBlobs(tx);\n });\n },\n async deleteOlderThan(before) {\n return db.transaction(async (tx) => {\n const oldModels = tx.select({ id: sandboxModelCalls.id }).from(sandboxModelCalls).where(lt(sandboxModelCalls.startedAt, before));\n const oldTools = tx.select({ id: sandboxToolCalls.id }).from(sandboxToolCalls).where(lt(sandboxToolCalls.startedAt, before));\n await tx.delete(sandboxBlobRefs).where(or(inArray(sandboxBlobRefs.callId, oldModels), inArray(sandboxBlobRefs.callId, oldTools)));\n const models = await tx.delete(sandboxModelCalls).where(lt(sandboxModelCalls.startedAt, before)).returning({ id: sandboxModelCalls.id });\n const tools = await tx.delete(sandboxToolCalls).where(lt(sandboxToolCalls.startedAt, before)).returning({ id: sandboxToolCalls.id });\n await dropUnreferencedBlobs(tx);\n return models.length + tools.length;\n });\n },\n async close() {\n await pool.end();\n },\n };\n}\n\n/** PostgreSQL не хранит символ NUL в тексте: сообщение об ошибке или инструкция с ним отклонили бы запись. */\nfunction withoutNul(text: string | null): string | null {\n return text === null ? null : text.replaceAll(\"\\u0000\", \"\");\n}\n\nfunction toBlobRow(row: typeof sandboxBlobs.$inferSelect): BlobRow {\n return { hash: row.hash, data: new Uint8Array(row.data), bytes: row.bytes, truncated: row.truncated };\n}\n","import type { ModelCallEvent } from \"@ykurilov/agent-chat-core\";\nimport { boolean, customType, doublePrecision, index, integer, jsonb, pgSchema, text, timestamp } from \"drizzle-orm/pg-core\";\n\n/** Та же схема базы, что у хранилища чатов: у приложения одна база и одна схема `agent_chat`. */\nconst agentChatSchema = pgSchema(\"agent_chat\");\n\nconst bytea = customType<{ data: Buffer; driverData: Buffer }>({\n dataType: () => \"bytea\",\n});\n\nconst startedAt = (name: string) => timestamp(name, { mode: \"date\", precision: 3, withTimezone: true });\n\nexport const sandboxBlobs = agentChatSchema.table(\"sandbox_blobs\", {\n hash: text(\"hash\").primaryKey(),\n data: bytea(\"data\").notNull(),\n bytes: integer(\"bytes\").notNull(),\n truncated: boolean(\"truncated\").notNull(),\n});\n\nexport const sandboxModelCalls = agentChatSchema.table(\n \"sandbox_model_calls\",\n {\n id: text(\"id\").primaryKey(),\n chatId: text(\"chat_id\").notNull(),\n ownerId: text(\"owner_id\").notNull(),\n messageId: text(\"message_id\"),\n purpose: text(\"purpose\").$type<ModelCallEvent[\"purpose\"]>().notNull(),\n attempt: integer(\"attempt\").notNull(),\n modelId: text(\"model_id\").notNull(),\n providerId: text(\"provider_id\").notNull(),\n usage: jsonb(\"usage\").$type<ModelCallEvent[\"usage\"]>(),\n costUsd: doublePrecision(\"cost_usd\"),\n startedAt: startedAt(\"started_at\").notNull(),\n firstChunkMs: integer(\"first_chunk_ms\"),\n durationMs: integer(\"duration_ms\").notNull(),\n status: text(\"status\").$type<ModelCallEvent[\"status\"]>().notNull(),\n error: text(\"error\"),\n instructionsHash: text(\"instructions_hash\").notNull(),\n messageHashes: jsonb(\"message_hashes\").$type<string[]>().notNull(),\n toolsHash: text(\"tools_hash\").notNull(),\n settingsHash: text(\"settings_hash\").notNull(),\n responseHash: text(\"response_hash\").notNull(),\n toolCallIds: jsonb(\"tool_call_ids\").$type<string[]>().notNull(),\n },\n (table) => [index(\"sandbox_model_calls_chat_idx\").on(table.chatId, table.startedAt), index(\"sandbox_model_calls_started_idx\").on(table.startedAt)],\n);\n\nexport const sandboxToolCalls = agentChatSchema.table(\n \"sandbox_tool_calls\",\n {\n id: text(\"id\").primaryKey(),\n chatId: text(\"chat_id\").notNull(),\n ownerId: text(\"owner_id\").notNull(),\n messageId: text(\"message_id\").notNull(),\n toolCallId: text(\"tool_call_id\").notNull(),\n toolName: text(\"tool_name\").notNull(),\n inputHash: text(\"input_hash\").notNull(),\n outputHash: text(\"output_hash\"),\n error: text(\"error\"),\n startedAt: startedAt(\"started_at\").notNull(),\n durationMs: integer(\"duration_ms\").notNull(),\n },\n (table) => [index(\"sandbox_tool_calls_chat_idx\").on(table.chatId, table.startedAt), index(\"sandbox_tool_calls_started_idx\").on(table.startedAt)],\n);\n\n/** Кто на какой кусок ссылается: по этим строкам решается, чей кусок и какие куски уже никому не нужны. */\nexport const sandboxBlobRefs = agentChatSchema.table(\n \"sandbox_blob_refs\",\n {\n callId: text(\"call_id\").notNull(),\n hash: text(\"hash\").notNull(),\n chatId: text(\"chat_id\").notNull(),\n ownerId: text(\"owner_id\").notNull(),\n },\n (table) => [index(\"sandbox_blob_refs_call_idx\").on(table.callId), index(\"sandbox_blob_refs_hash_idx\").on(table.hash, table.ownerId), index(\"sandbox_blob_refs_chat_idx\").on(table.chatId)],\n);\n\nexport const sandboxSettings = agentChatSchema.table(\"sandbox_settings\", {\n chatId: text(\"chat_id\").primaryKey(),\n instructions: text(\"instructions\"),\n updatedBy: text(\"updated_by\").notNull(),\n updatedAt: startedAt(\"updated_at\").notNull(),\n});\n\nexport const sandboxAnswerEdits = agentChatSchema.table(\n \"sandbox_answer_edits\",\n {\n messageId: text(\"message_id\").primaryKey(),\n sourceMessageId: text(\"source_message_id\").notNull(),\n chatId: text(\"chat_id\").notNull(),\n textEdited: boolean(\"text_edited\").notNull(),\n editedToolCallIds: jsonb(\"edited_tool_call_ids\").$type<string[]>().notNull(),\n editedBy: text(\"edited_by\").notNull(),\n editedAt: startedAt(\"edited_at\").notNull(),\n },\n (table) => [index(\"sandbox_answer_edits_chat_idx\").on(table.chatId, table.editedAt)],\n);\n"],"mappings":";;;;;AAAA,SAAS,qBAAqB;AAC9B,OAAO,QAAQ;;;ACDf,SAAS,KAAK,KAAK,IAAI,SAAS,IAAI,WAAW,UAAU;AACzD,SAAS,eAAe;AACxB,SAAS,eAAe;;;ACDxB,SAAS,SAAS,YAAY,iBAAiB,OAAO,SAAS,OAAO,UAAU,MAAM,iBAAiB;AAGvG,IAAM,kBAAkB,SAAS,YAAY;AAE7C,IAAM,QAAQ,WAAiD;AAAA,EAC7D,UAAU,MAAM;AAClB,CAAC;AAED,IAAM,YAAY,CAAC,SAAiB,UAAU,MAAM,EAAE,MAAM,QAAQ,WAAW,GAAG,cAAc,KAAK,CAAC;AAE/F,IAAM,eAAe,gBAAgB,MAAM,iBAAiB;AAAA,EACjE,MAAM,KAAK,MAAM,EAAE,WAAW;AAAA,EAC9B,MAAM,MAAM,MAAM,EAAE,QAAQ;AAAA,EAC5B,OAAO,QAAQ,OAAO,EAAE,QAAQ;AAAA,EAChC,WAAW,QAAQ,WAAW,EAAE,QAAQ;AAC1C,CAAC;AAEM,IAAM,oBAAoB,gBAAgB;AAAA,EAC/C;AAAA,EACA;AAAA,IACE,IAAI,KAAK,IAAI,EAAE,WAAW;AAAA,IAC1B,QAAQ,KAAK,SAAS,EAAE,QAAQ;AAAA,IAChC,SAAS,KAAK,UAAU,EAAE,QAAQ;AAAA,IAClC,WAAW,KAAK,YAAY;AAAA,IAC5B,SAAS,KAAK,SAAS,EAAE,MAAiC,EAAE,QAAQ;AAAA,IACpE,SAAS,QAAQ,SAAS,EAAE,QAAQ;AAAA,IACpC,SAAS,KAAK,UAAU,EAAE,QAAQ;AAAA,IAClC,YAAY,KAAK,aAAa,EAAE,QAAQ;AAAA,IACxC,OAAO,MAAM,OAAO,EAAE,MAA+B;AAAA,IACrD,SAAS,gBAAgB,UAAU;AAAA,IACnC,WAAW,UAAU,YAAY,EAAE,QAAQ;AAAA,IAC3C,cAAc,QAAQ,gBAAgB;AAAA,IACtC,YAAY,QAAQ,aAAa,EAAE,QAAQ;AAAA,IAC3C,QAAQ,KAAK,QAAQ,EAAE,MAAgC,EAAE,QAAQ;AAAA,IACjE,OAAO,KAAK,OAAO;AAAA,IACnB,kBAAkB,KAAK,mBAAmB,EAAE,QAAQ;AAAA,IACpD,eAAe,MAAM,gBAAgB,EAAE,MAAgB,EAAE,QAAQ;AAAA,IACjE,WAAW,KAAK,YAAY,EAAE,QAAQ;AAAA,IACtC,cAAc,KAAK,eAAe,EAAE,QAAQ;AAAA,IAC5C,cAAc,KAAK,eAAe,EAAE,QAAQ;AAAA,IAC5C,aAAa,MAAM,eAAe,EAAE,MAAgB,EAAE,QAAQ;AAAA,EAChE;AAAA,EACA,CAAC,UAAU,CAAC,MAAM,8BAA8B,EAAE,GAAG,MAAM,QAAQ,MAAM,SAAS,GAAG,MAAM,iCAAiC,EAAE,GAAG,MAAM,SAAS,CAAC;AACnJ;AAEO,IAAM,mBAAmB,gBAAgB;AAAA,EAC9C;AAAA,EACA;AAAA,IACE,IAAI,KAAK,IAAI,EAAE,WAAW;AAAA,IAC1B,QAAQ,KAAK,SAAS,EAAE,QAAQ;AAAA,IAChC,SAAS,KAAK,UAAU,EAAE,QAAQ;AAAA,IAClC,WAAW,KAAK,YAAY,EAAE,QAAQ;AAAA,IACtC,YAAY,KAAK,cAAc,EAAE,QAAQ;AAAA,IACzC,UAAU,KAAK,WAAW,EAAE,QAAQ;AAAA,IACpC,WAAW,KAAK,YAAY,EAAE,QAAQ;AAAA,IACtC,YAAY,KAAK,aAAa;AAAA,IAC9B,OAAO,KAAK,OAAO;AAAA,IACnB,WAAW,UAAU,YAAY,EAAE,QAAQ;AAAA,IAC3C,YAAY,QAAQ,aAAa,EAAE,QAAQ;AAAA,EAC7C;AAAA,EACA,CAAC,UAAU,CAAC,MAAM,6BAA6B,EAAE,GAAG,MAAM,QAAQ,MAAM,SAAS,GAAG,MAAM,gCAAgC,EAAE,GAAG,MAAM,SAAS,CAAC;AACjJ;AAGO,IAAM,kBAAkB,gBAAgB;AAAA,EAC7C;AAAA,EACA;AAAA,IACE,QAAQ,KAAK,SAAS,EAAE,QAAQ;AAAA,IAChC,MAAM,KAAK,MAAM,EAAE,QAAQ;AAAA,IAC3B,QAAQ,KAAK,SAAS,EAAE,QAAQ;AAAA,IAChC,SAAS,KAAK,UAAU,EAAE,QAAQ;AAAA,EACpC;AAAA,EACA,CAAC,UAAU,CAAC,MAAM,4BAA4B,EAAE,GAAG,MAAM,MAAM,GAAG,MAAM,4BAA4B,EAAE,GAAG,MAAM,MAAM,MAAM,OAAO,GAAG,MAAM,4BAA4B,EAAE,GAAG,MAAM,MAAM,CAAC;AAC3L;AAEO,IAAM,kBAAkB,gBAAgB,MAAM,oBAAoB;AAAA,EACvE,QAAQ,KAAK,SAAS,EAAE,WAAW;AAAA,EACnC,cAAc,KAAK,cAAc;AAAA,EACjC,WAAW,KAAK,YAAY,EAAE,QAAQ;AAAA,EACtC,WAAW,UAAU,YAAY,EAAE,QAAQ;AAC7C,CAAC;AAEM,IAAM,qBAAqB,gBAAgB;AAAA,EAChD;AAAA,EACA;AAAA,IACE,WAAW,KAAK,YAAY,EAAE,WAAW;AAAA,IACzC,iBAAiB,KAAK,mBAAmB,EAAE,QAAQ;AAAA,IACnD,QAAQ,KAAK,SAAS,EAAE,QAAQ;AAAA,IAChC,YAAY,QAAQ,aAAa,EAAE,QAAQ;AAAA,IAC3C,mBAAmB,MAAM,sBAAsB,EAAE,MAAgB,EAAE,QAAQ;AAAA,IAC3E,UAAU,KAAK,WAAW,EAAE,QAAQ;AAAA,IACpC,UAAU,UAAU,WAAW,EAAE,QAAQ;AAAA,EAC3C;AAAA,EACA,CAAC,UAAU,CAAC,MAAM,+BAA+B,EAAE,GAAG,MAAM,QAAQ,MAAM,QAAQ,CAAC;AACrF;;;ADxFA,IAAM,qBAAqB;AAG3B,IAAM,mBAAmB;AAMzB,eAAsB,uBAAuB,MAAYA,mBAAyC;AAChG,QAAM,SAAS,MAAM,KAAK,QAAQ;AAClC,MAAI;AACF,UAAM,OAAO,MAAM,+BAA+B,CAAC,kBAAkB,CAAC;AACtE,QAAI;AACF,YAAM,QAAQ,QAAQ,MAAM,GAAG,EAAE,kBAAAA,mBAAkB,kBAAkB,cAAc,iBAAiB,iBAAiB,CAAC;AAAA,IACxH,UAAE;AACA,YAAM,OAAO,MAAM,iCAAiC,CAAC,kBAAkB,CAAC;AAAA,IAC1E;AAAA,EACF,UAAE;AACA,WAAO,QAAQ;AAAA,EACjB;AACF;AAGO,SAAS,2BAA2B,MAA0B;AACnE,QAAM,KAAK,QAAQ,IAAI;AAIvB,iBAAe,sBAAsB,IAA6B;AAChE,UAAM,GAAG,OAAO,YAAY,EAAE,MAAM,UAAU,GAAG,OAAO,EAAE,KAAK,gBAAgB,KAAK,CAAC,EAAE,KAAK,eAAe,EAAE,MAAM,GAAG,gBAAgB,MAAM,aAAa,IAAI,CAAC,CAAC,CAAC;AAAA,EAClK;AAOA,iBAAe,UAAU,IAAc,QAAgB,KAA0C,QAAkB,OAAiC;AAClJ,UAAM,SAAS,CAAC,GAAG,IAAI,IAAI,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,MAAM,IAAI,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM,UAAW,KAAK,OAAO,MAAM,OAAO,KAAK,KAAK,OAAO,MAAM,OAAO,IAAI,CAAE;AACjK,QAAI,OAAO,SAAS,GAAG;AACrB,YAAM,GACH,OAAO,YAAY,EACnB,OAAO,OAAO,IAAI,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,MAAM,OAAO,KAAK,KAAK,IAAI,GAAG,OAAO,KAAK,OAAO,WAAW,KAAK,UAAU,EAAE,CAAC,EAC9H,oBAAoB;AAAA,IACzB;AACA,UAAM,OAAO,CAAC,GAAG,IAAI,IAAI,MAAM,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,MAAM,QAAQ,IAAI,QAAQ,SAAS,IAAI,QAAQ,EAAE;AAC5G,QAAI,KAAK,SAAS,EAAG,OAAM,GAAG,OAAO,eAAe,EAAE,OAAO,IAAI;AAAA,EACnE;AAEA,SAAO;AAAA,IACL,MAAM,cAAc,KAAK,OAAO;AAC9B,YAAM,GAAG,YAAY,OAAO,OAAO;AACjC,cAAM,WAAW,MAAM,GAAG,OAAO,iBAAiB,EAAE,OAAO,EAAE,GAAG,KAAK,OAAO,WAAW,IAAI,KAAK,EAAE,CAAC,EAAE,oBAAoB,EAAE,UAAU,EAAE,IAAI,kBAAkB,GAAG,CAAC;AACjK,YAAI,SAAS,WAAW,EAAG;AAC3B,cAAM,UAAU,IAAI,IAAI,IAAI,KAAK,CAAC,IAAI,kBAAkB,GAAG,IAAI,eAAe,IAAI,WAAW,IAAI,cAAc,IAAI,YAAY,GAAG,KAAK;AAAA,MACzI,CAAC;AAAA,IACH;AAAA,IACA,MAAM,aAAa,KAAK,OAAO;AAC7B,YAAM,GAAG,YAAY,OAAO,OAAO;AACjC,cAAM,WAAW,MAAM,GAAG,OAAO,gBAAgB,EAAE,OAAO,EAAE,GAAG,KAAK,OAAO,WAAW,IAAI,KAAK,EAAE,CAAC,EAAE,oBAAoB,EAAE,UAAU,EAAE,IAAI,iBAAiB,GAAG,CAAC;AAC/J,YAAI,SAAS,WAAW,EAAG;AAC3B,cAAM,UAAU,IAAI,IAAI,IAAI,KAAK,IAAI,aAAa,CAAC,IAAI,WAAW,IAAI,UAAU,IAAI,CAAC,IAAI,SAAS,GAAG,KAAK;AAAA,MAC5G,CAAC;AAAA,IACH;AAAA,IACA,MAAM,eAAe,QAAQ;AAC3B,aAAO,GAAG,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,GAAG,kBAAkB,QAAQ,MAAM,CAAC,EAAE,QAAQ,IAAI,kBAAkB,SAAS,CAAC;AAAA,IACjI;AAAA,IACA,MAAM,cAAc,QAAQ;AAC1B,aAAO,GAAG,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,GAAG,iBAAiB,QAAQ,MAAM,CAAC,EAAE,QAAQ,IAAI,iBAAiB,SAAS,CAAC;AAAA,IAC9H;AAAA,IACA,MAAM,cAAc,IAAI;AACtB,cAAQ,MAAM,GAAG,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,GAAG,kBAAkB,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,KAAK;AAAA,IACxG;AAAA,IACA,MAAM,aAAa,IAAI;AACrB,cAAQ,MAAM,GAAG,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,GAAG,iBAAiB,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,KAAK;AAAA,IACtG;AAAA,IACA,MAAM,UAAU,QAAQ;AACtB,UAAI,OAAO,WAAW,EAAG,QAAO,CAAC;AACjC,cAAQ,MAAM,GAAG,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,QAAQ,aAAa,MAAM,MAAM,CAAC,GAAG,IAAI,SAAS;AAAA,IACvG;AAAA,IACA,MAAM,iBAAiB,MAAM,SAAS;AACpC,YAAM,QAAQ,MAAM,GAAG,OAAO,EAAE,MAAM,gBAAgB,KAAK,CAAC,EAAE,KAAK,eAAe,EAAE,MAAM,IAAI,GAAG,gBAAgB,MAAM,IAAI,GAAG,GAAG,gBAAgB,SAAS,OAAO,CAAC,CAAC,EAAE,MAAM,CAAC;AAC5K,UAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,YAAM,OAAO,MAAM,GAAG,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,GAAG,aAAa,MAAM,IAAI,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC;AAChG,aAAO,MAAM,UAAU,GAAG,IAAI;AAAA,IAChC;AAAA,IACA,MAAM,YAAY,QAAQ;AACxB,cAAQ,MAAM,GAAG,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,GAAG,gBAAgB,QAAQ,MAAM,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,KAAK;AAAA,IAC5G;AAAA,IACA,MAAM,YAAY,UAAU;AAC1B,YAAM,QAAQ,EAAE,GAAG,UAAU,cAAc,WAAW,SAAS,YAAY,EAAE;AAC7E,YAAM,GAAG,OAAO,eAAe,EAAE,OAAO,KAAK,EAAE,mBAAmB,EAAE,QAAQ,gBAAgB,QAAQ,KAAK,MAAM,CAAC;AAAA,IAClH;AAAA,IACA,MAAM,eAAe,QAAQ;AAC3B,YAAM,GAAG,OAAO,kBAAkB,EAAE,OAAO,MAAM,EAAE,mBAAmB,EAAE,QAAQ,mBAAmB,WAAW,KAAK,OAAO,CAAC;AAAA,IAC7H;AAAA,IACA,MAAM,gBAAgB,QAAQ;AAC5B,aAAO,GAAG,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,GAAG,mBAAmB,QAAQ,MAAM,CAAC,EAAE,QAAQ,IAAI,mBAAmB,QAAQ,CAAC;AAAA,IACnI;AAAA,IACA,MAAM,WAAW,QAAQ;AACvB,YAAM,GAAG,YAAY,OAAO,OAAO;AACjC,cAAM,GAAG,OAAO,eAAe,EAAE,MAAM,GAAG,gBAAgB,QAAQ,MAAM,CAAC;AACzE,cAAM,GAAG,OAAO,iBAAiB,EAAE,MAAM,GAAG,kBAAkB,QAAQ,MAAM,CAAC;AAC7E,cAAM,GAAG,OAAO,gBAAgB,EAAE,MAAM,GAAG,iBAAiB,QAAQ,MAAM,CAAC;AAC3E,cAAM,GAAG,OAAO,eAAe,EAAE,MAAM,GAAG,gBAAgB,QAAQ,MAAM,CAAC;AACzE,cAAM,GAAG,OAAO,kBAAkB,EAAE,MAAM,GAAG,mBAAmB,QAAQ,MAAM,CAAC;AAC/E,cAAM,sBAAsB,EAAE;AAAA,MAChC,CAAC;AAAA,IACH;AAAA,IACA,MAAM,gBAAgB,QAAQ;AAC5B,aAAO,GAAG,YAAY,OAAO,OAAO;AAClC,cAAM,YAAY,GAAG,OAAO,EAAE,IAAI,kBAAkB,GAAG,CAAC,EAAE,KAAK,iBAAiB,EAAE,MAAM,GAAG,kBAAkB,WAAW,MAAM,CAAC;AAC/H,cAAM,WAAW,GAAG,OAAO,EAAE,IAAI,iBAAiB,GAAG,CAAC,EAAE,KAAK,gBAAgB,EAAE,MAAM,GAAG,iBAAiB,WAAW,MAAM,CAAC;AAC3H,cAAM,GAAG,OAAO,eAAe,EAAE,MAAM,GAAG,QAAQ,gBAAgB,QAAQ,SAAS,GAAG,QAAQ,gBAAgB,QAAQ,QAAQ,CAAC,CAAC;AAChI,cAAM,SAAS,MAAM,GAAG,OAAO,iBAAiB,EAAE,MAAM,GAAG,kBAAkB,WAAW,MAAM,CAAC,EAAE,UAAU,EAAE,IAAI,kBAAkB,GAAG,CAAC;AACvI,cAAM,QAAQ,MAAM,GAAG,OAAO,gBAAgB,EAAE,MAAM,GAAG,iBAAiB,WAAW,MAAM,CAAC,EAAE,UAAU,EAAE,IAAI,iBAAiB,GAAG,CAAC;AACnI,cAAM,sBAAsB,EAAE;AAC9B,eAAO,OAAO,SAAS,MAAM;AAAA,MAC/B,CAAC;AAAA,IACH;AAAA,IACA,MAAM,QAAQ;AACZ,YAAM,KAAK,IAAI;AAAA,IACjB;AAAA,EACF;AACF;AAGA,SAAS,WAAWC,OAAoC;AACtD,SAAOA,UAAS,OAAO,OAAOA,MAAK,WAAW,MAAU,EAAE;AAC5D;AAEA,SAAS,UAAU,KAAgD;AACjE,SAAO,EAAE,MAAM,IAAI,MAAM,MAAM,IAAI,WAAW,IAAI,IAAI,GAAG,OAAO,IAAI,OAAO,WAAW,IAAI,UAAU;AACtG;;;AD9HA,IAAM,oBAAoB;AAC1B,IAAM,mBAAmB,cAAc,IAAI,IAAI,uBAAuB,YAAY,GAAG,CAAC;AAMtF,eAAsB,6BAA6B,SAAiE;AAClH,QAAM,WAAW,QAAQ,aAAa,CAAC,YAAoB,QAAQ,MAAM,OAAO;AAChF,QAAM,OAAO,IAAI,GAAG,KAAK,EAAE,kBAAkB,QAAQ,kBAAkB,KAAK,QAAQ,YAAY,kBAAkB,CAAC;AAEnH,OAAK,GAAG,SAAS,CAAC,UAAU,SAAS,6OAAyD,MAAM,OAAO,EAAE,CAAC;AAC9G,MAAI;AACF,UAAM,uBAAuB,MAAM,gBAAgB;AAAA,EACrD,SAAS,OAAO;AACd,UAAM,KAAK,IAAI,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAC/B,UAAM,QAAQ,OAAO,QAAQ,gBAAgB;AAAA,EAC/C;AACA,SAAO,qBAAqB,2BAA2B,IAAI,GAAG,EAAE,GAAG,SAAS,SAAS,CAAC;AACxF;AAGA,SAAS,QAAQ,OAAgB,kBAAiC;AAChE,QAAM,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACpE,SAAO,IAAI,MAAM,6OAAyD,OAAO,MAAM,gBAAgB,EAAE,KAAK,2GAAsB,CAAC,EAAE;AACzI;","names":["migrationsFolder","text"]}
|
|
1
|
+
{"version":3,"sources":["../src/postgres.ts","../src/journal/postgres-journal.ts","../src/journal/postgres-schema.ts"],"sourcesContent":["import { fileURLToPath } from \"node:url\";\nimport pg from \"pg\";\nimport type { JournalOptions } from \"./journal/journal\";\nimport { createSandboxJournal } from \"./journal/journal\";\nimport { createPostgresJournalStore, migrateJournalToLatest } from \"./journal/postgres-journal\";\nimport type { SandboxJournal } from \"./types\";\n\nexport type { LogError, SandboxJournal } from \"./types\";\n\nexport type PostgresSandboxJournalOptions = JournalOptions & {\n /** Например `postgres://user:password@host:5432/database`; та же база, что у `createPostgresChatStorage`. */\n connectionString: string;\n /** Сколько соединений держать открытыми. По умолчанию 5. */\n poolSize?: number;\n};\n\nconst DEFAULT_POOL_SIZE = 5;\nconst migrationsFolder = fileURLToPath(new URL(\"../drizzle/postgres\", import.meta.url));\n\n/**\n * Журнал песочницы в PostgreSQL. Таблицы лежат в схеме `agent_chat`; схема приводится\n * к последней миграции при создании, под блокировкой базы.\n */\nexport async function createPostgresSandboxJournal(options: PostgresSandboxJournalOptions): Promise<SandboxJournal> {\n const logError = options.logError ?? ((message: string) => console.error(message));\n const pool = new pg.Pool({ connectionString: options.connectionString, max: options.poolSize ?? DEFAULT_POOL_SIZE });\n // Без обработчика оборвавшееся соединение простаивающего пула роняет процесс.\n pool.on(\"error\", (error) => logError(`журнал песочницы: соединение с PostgreSQL оборвалось: ${error.message}`));\n try {\n await migrateJournalToLatest(pool, migrationsFolder);\n } catch (error) {\n await pool.end().catch(() => {});\n throw failure(error, options.connectionString);\n }\n return createSandboxJournal(createPostgresJournalStore(pool), { ...options, logError });\n}\n\n/** Ошибка без строки подключения: пароль в текст не попадает. */\nfunction failure(cause: unknown, connectionString: string): Error {\n const reason = cause instanceof Error ? cause.message : String(cause);\n return new Error(`Не удалось подготовить журнал песочницы в PostgreSQL: ${reason.split(connectionString).join(\"<строка подключения>\")}`);\n}\n","import { and, asc, eq, inArray, lt, notExists, or } 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 { BlobRow, JournalStore, ModelCallRow, ToolCallRow } from \"./journal-store\";\nimport { sandboxAnswerEdits, sandboxBlobRefs, sandboxBlobs, sandboxModelCalls, sandboxSettings, sandboxToolCalls } from \"./postgres-schema\";\n\n/** Постоянное число для блокировки базы миграциями журнала; не совпадает с ключом хранилища чатов. */\nconst MIGRATION_LOCK_KEY = 7_311_900_443;\n\n/** Своя таблица учёта: хранилище чатов живёт в той же схеме и ведёт свою (`__drizzle_migrations`). */\nconst MIGRATIONS_TABLE = \"__sandbox_migrations\";\n\n/**\n * Приводит таблицы журнала к последней миграции. Схема `agent_chat` создаётся под общей блокировкой всех пакетов, миграции — под своей:\n * копии пакета ждут друг друга, а разные пакеты стартуют разом на пустой базе без гонки за схему.\n */\nexport async function migrateJournalToLatest(pool: Pool, migrationsFolder: string): Promise<void> {\n await migratePostgresSchema({\n connect: () => pool.connect(),\n lockKey: MIGRATION_LOCK_KEY,\n migrate: (client) => migrate(drizzle(client), { migrationsFolder, migrationsSchema: \"agent_chat\", migrationsTable: MIGRATIONS_TABLE }),\n });\n}\n\n/** Хранилище журнала в PostgreSQL (схема `agent_chat`). Закрытие закрывает и пул. */\nexport function createPostgresJournalStore(pool: Pool): JournalStore {\n const db = drizzle(pool);\n type Executor = Pick<typeof db, \"delete\" | \"select\" | \"insert\">;\n\n /** Куски, на которые не осталось ссылок. */\n async function dropUnreferencedBlobs(tx: Executor): Promise<void> {\n await tx.delete(sandboxBlobs).where(notExists(tx.select({ one: sandboxBlobRefs.hash }).from(sandboxBlobRefs).where(eq(sandboxBlobRefs.hash, sandboxBlobs.hash))));\n }\n\n /**\n * Куски обращения пишутся одним запросом: в длинном чате их сотни (вся история), и запрос на каждый кусок\n * превращал одну запись журнала в сотни походов к базе. Порядок по хэшу одинаков у всех записей —\n * параллельные записи с общими кусками не ждут друг друга крест-накрест.\n */\n async function saveBlobs(tx: Executor, callId: string, row: { chatId: string; ownerId: string }, hashes: string[], blobs: BlobRow[]): Promise<void> {\n const unique = [...new Map(blobs.map((blob) => [blob.hash, blob])).values()].sort((left, right) => (left.hash < right.hash ? -1 : left.hash > right.hash ? 1 : 0));\n if (unique.length > 0) {\n await tx\n .insert(sandboxBlobs)\n .values(unique.map((blob) => ({ hash: blob.hash, data: Buffer.from(blob.data), bytes: blob.bytes, truncated: blob.truncated })))\n .onConflictDoNothing();\n }\n const refs = [...new Set(hashes)].map((hash) => ({ callId, hash, chatId: row.chatId, ownerId: row.ownerId }));\n if (refs.length > 0) await tx.insert(sandboxBlobRefs).values(refs);\n }\n\n return {\n async saveModelCall(row, blobs) {\n await db.transaction(async (tx) => {\n const inserted = await tx.insert(sandboxModelCalls).values({ ...row, error: withoutNul(row.error) }).onConflictDoNothing().returning({ id: sandboxModelCalls.id });\n if (inserted.length === 0) return;\n await saveBlobs(tx, row.id, row, [row.instructionsHash, ...row.messageHashes, row.toolsHash, row.settingsHash, row.responseHash], blobs);\n });\n },\n async saveToolCall(row, blobs) {\n await db.transaction(async (tx) => {\n const inserted = await tx.insert(sandboxToolCalls).values({ ...row, error: withoutNul(row.error) }).onConflictDoNothing().returning({ id: sandboxToolCalls.id });\n if (inserted.length === 0) return;\n await saveBlobs(tx, row.id, row, row.outputHash ? [row.inputHash, row.outputHash] : [row.inputHash], blobs);\n });\n },\n async listModelCalls(chatId) {\n return db.select().from(sandboxModelCalls).where(eq(sandboxModelCalls.chatId, chatId)).orderBy(asc(sandboxModelCalls.startedAt));\n },\n async listToolCalls(chatId) {\n return db.select().from(sandboxToolCalls).where(eq(sandboxToolCalls.chatId, chatId)).orderBy(asc(sandboxToolCalls.startedAt));\n },\n async findModelCall(id) {\n return (await db.select().from(sandboxModelCalls).where(eq(sandboxModelCalls.id, id)).limit(1))[0] ?? null;\n },\n async findToolCall(id) {\n return (await db.select().from(sandboxToolCalls).where(eq(sandboxToolCalls.id, id)).limit(1))[0] ?? null;\n },\n async loadBlobs(hashes) {\n if (hashes.length === 0) return [];\n return (await db.select().from(sandboxBlobs).where(inArray(sandboxBlobs.hash, hashes))).map(toBlobRow);\n },\n async loadBlobForOwner(hash, ownerId) {\n const owned = await db.select({ hash: sandboxBlobRefs.hash }).from(sandboxBlobRefs).where(and(eq(sandboxBlobRefs.hash, hash), eq(sandboxBlobRefs.ownerId, ownerId))).limit(1);\n if (owned.length === 0) return null;\n const row = (await db.select().from(sandboxBlobs).where(eq(sandboxBlobs.hash, hash)).limit(1))[0];\n return row ? toBlobRow(row) : null;\n },\n async getSettings(chatId) {\n return (await db.select().from(sandboxSettings).where(eq(sandboxSettings.chatId, chatId)).limit(1))[0] ?? null;\n },\n async setSettings(settings) {\n const clean = { ...settings, instructions: withoutNul(settings.instructions) };\n await db.insert(sandboxSettings).values(clean).onConflictDoUpdate({ target: sandboxSettings.chatId, set: clean });\n },\n async saveAnswerEdit(record) {\n await db.insert(sandboxAnswerEdits).values(record).onConflictDoUpdate({ target: sandboxAnswerEdits.messageId, set: record });\n },\n async listAnswerEdits(chatId) {\n return db.select().from(sandboxAnswerEdits).where(eq(sandboxAnswerEdits.chatId, chatId)).orderBy(asc(sandboxAnswerEdits.editedAt));\n },\n async deleteChat(chatId) {\n await db.transaction(async (tx) => {\n await tx.delete(sandboxBlobRefs).where(eq(sandboxBlobRefs.chatId, chatId));\n await tx.delete(sandboxModelCalls).where(eq(sandboxModelCalls.chatId, chatId));\n await tx.delete(sandboxToolCalls).where(eq(sandboxToolCalls.chatId, chatId));\n await tx.delete(sandboxSettings).where(eq(sandboxSettings.chatId, chatId));\n await tx.delete(sandboxAnswerEdits).where(eq(sandboxAnswerEdits.chatId, chatId));\n await dropUnreferencedBlobs(tx);\n });\n },\n async deleteOlderThan(before) {\n return db.transaction(async (tx) => {\n const oldModels = tx.select({ id: sandboxModelCalls.id }).from(sandboxModelCalls).where(lt(sandboxModelCalls.startedAt, before));\n const oldTools = tx.select({ id: sandboxToolCalls.id }).from(sandboxToolCalls).where(lt(sandboxToolCalls.startedAt, before));\n await tx.delete(sandboxBlobRefs).where(or(inArray(sandboxBlobRefs.callId, oldModels), inArray(sandboxBlobRefs.callId, oldTools)));\n const models = await tx.delete(sandboxModelCalls).where(lt(sandboxModelCalls.startedAt, before)).returning({ id: sandboxModelCalls.id });\n const tools = await tx.delete(sandboxToolCalls).where(lt(sandboxToolCalls.startedAt, before)).returning({ id: sandboxToolCalls.id });\n await dropUnreferencedBlobs(tx);\n return models.length + tools.length;\n });\n },\n async close() {\n await pool.end();\n },\n };\n}\n\n/** PostgreSQL не хранит символ NUL в тексте: сообщение об ошибке или инструкция с ним отклонили бы запись. */\nfunction withoutNul(text: string | null): string | null {\n return text === null ? null : text.replaceAll(\"\\u0000\", \"\");\n}\n\nfunction toBlobRow(row: typeof sandboxBlobs.$inferSelect): BlobRow {\n return { hash: row.hash, data: new Uint8Array(row.data), bytes: row.bytes, truncated: row.truncated };\n}\n","import type { ModelCallEvent } from \"@ykurilov/agent-chat-core\";\nimport { boolean, customType, doublePrecision, index, integer, jsonb, pgSchema, text, timestamp } from \"drizzle-orm/pg-core\";\n\n/** Та же схема базы, что у хранилища чатов: у приложения одна база и одна схема `agent_chat`. */\nconst agentChatSchema = pgSchema(\"agent_chat\");\n\nconst bytea = customType<{ data: Buffer; driverData: Buffer }>({\n dataType: () => \"bytea\",\n});\n\nconst startedAt = (name: string) => timestamp(name, { mode: \"date\", precision: 3, withTimezone: true });\n\nexport const sandboxBlobs = agentChatSchema.table(\"sandbox_blobs\", {\n hash: text(\"hash\").primaryKey(),\n data: bytea(\"data\").notNull(),\n bytes: integer(\"bytes\").notNull(),\n truncated: boolean(\"truncated\").notNull(),\n});\n\nexport const sandboxModelCalls = agentChatSchema.table(\n \"sandbox_model_calls\",\n {\n id: text(\"id\").primaryKey(),\n chatId: text(\"chat_id\").notNull(),\n ownerId: text(\"owner_id\").notNull(),\n messageId: text(\"message_id\"),\n purpose: text(\"purpose\").$type<ModelCallEvent[\"purpose\"]>().notNull(),\n attempt: integer(\"attempt\").notNull(),\n modelId: text(\"model_id\").notNull(),\n providerId: text(\"provider_id\").notNull(),\n usage: jsonb(\"usage\").$type<ModelCallEvent[\"usage\"]>(),\n costUsd: doublePrecision(\"cost_usd\"),\n startedAt: startedAt(\"started_at\").notNull(),\n firstChunkMs: integer(\"first_chunk_ms\"),\n durationMs: integer(\"duration_ms\").notNull(),\n status: text(\"status\").$type<ModelCallEvent[\"status\"]>().notNull(),\n error: text(\"error\"),\n instructionsHash: text(\"instructions_hash\").notNull(),\n messageHashes: jsonb(\"message_hashes\").$type<string[]>().notNull(),\n toolsHash: text(\"tools_hash\").notNull(),\n settingsHash: text(\"settings_hash\").notNull(),\n responseHash: text(\"response_hash\").notNull(),\n toolCallIds: jsonb(\"tool_call_ids\").$type<string[]>().notNull(),\n },\n (table) => [index(\"sandbox_model_calls_chat_idx\").on(table.chatId, table.startedAt), index(\"sandbox_model_calls_started_idx\").on(table.startedAt)],\n);\n\nexport const sandboxToolCalls = agentChatSchema.table(\n \"sandbox_tool_calls\",\n {\n id: text(\"id\").primaryKey(),\n chatId: text(\"chat_id\").notNull(),\n ownerId: text(\"owner_id\").notNull(),\n messageId: text(\"message_id\").notNull(),\n toolCallId: text(\"tool_call_id\").notNull(),\n toolName: text(\"tool_name\").notNull(),\n inputHash: text(\"input_hash\").notNull(),\n outputHash: text(\"output_hash\"),\n error: text(\"error\"),\n startedAt: startedAt(\"started_at\").notNull(),\n durationMs: integer(\"duration_ms\").notNull(),\n },\n (table) => [index(\"sandbox_tool_calls_chat_idx\").on(table.chatId, table.startedAt), index(\"sandbox_tool_calls_started_idx\").on(table.startedAt)],\n);\n\n/** Кто на какой кусок ссылается: по этим строкам решается, чей кусок и какие куски уже никому не нужны. */\nexport const sandboxBlobRefs = agentChatSchema.table(\n \"sandbox_blob_refs\",\n {\n callId: text(\"call_id\").notNull(),\n hash: text(\"hash\").notNull(),\n chatId: text(\"chat_id\").notNull(),\n ownerId: text(\"owner_id\").notNull(),\n },\n (table) => [index(\"sandbox_blob_refs_call_idx\").on(table.callId), index(\"sandbox_blob_refs_hash_idx\").on(table.hash, table.ownerId), index(\"sandbox_blob_refs_chat_idx\").on(table.chatId)],\n);\n\nexport const sandboxSettings = agentChatSchema.table(\"sandbox_settings\", {\n chatId: text(\"chat_id\").primaryKey(),\n instructions: text(\"instructions\"),\n updatedBy: text(\"updated_by\").notNull(),\n updatedAt: startedAt(\"updated_at\").notNull(),\n});\n\nexport const sandboxAnswerEdits = agentChatSchema.table(\n \"sandbox_answer_edits\",\n {\n messageId: text(\"message_id\").primaryKey(),\n sourceMessageId: text(\"source_message_id\").notNull(),\n chatId: text(\"chat_id\").notNull(),\n textEdited: boolean(\"text_edited\").notNull(),\n editedToolCallIds: jsonb(\"edited_tool_call_ids\").$type<string[]>().notNull(),\n editedBy: text(\"edited_by\").notNull(),\n editedAt: startedAt(\"edited_at\").notNull(),\n },\n (table) => [index(\"sandbox_answer_edits_chat_idx\").on(table.chatId, table.editedAt)],\n);\n"],"mappings":"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","names":["migrationsFolder","text"]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Pool } from "pg";
|
|
2
2
|
import type { JournalStore } from "./journal-store.js";
|
|
3
3
|
/**
|
|
4
|
-
* Приводит таблицы журнала к последней миграции.
|
|
5
|
-
*
|
|
4
|
+
* Приводит таблицы журнала к последней миграции. Схема `agent_chat` создаётся под общей блокировкой всех пакетов, миграции — под своей:
|
|
5
|
+
* копии пакета ждут друг друга, а разные пакеты стартуют разом на пустой базе без гонки за схему.
|
|
6
6
|
*/
|
|
7
7
|
export declare function migrateJournalToLatest(pool: Pool, migrationsFolder: string): Promise<void>;
|
|
8
8
|
/** Хранилище журнала в PostgreSQL (схема `agent_chat`). Закрытие закрывает и пул. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ykurilov/agent-chat-sandbox",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.59.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -46,14 +46,14 @@
|
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"drizzle-orm": "^0.45.3",
|
|
49
|
-
"@ykurilov/agent-chat-core": "0.
|
|
50
|
-
"@ykurilov/agent-chat-server": "0.
|
|
49
|
+
"@ykurilov/agent-chat-core": "0.59.0",
|
|
50
|
+
"@ykurilov/agent-chat-server": "0.59.0"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"better-sqlite3": "^13.0.3",
|
|
54
54
|
"pg": "^8.23.0",
|
|
55
55
|
"react": "^19.3.0",
|
|
56
|
-
"@ykurilov/agent-chat-react": "^0.
|
|
56
|
+
"@ykurilov/agent-chat-react": "^0.59.0"
|
|
57
57
|
},
|
|
58
58
|
"peerDependenciesMeta": {
|
|
59
59
|
"better-sqlite3": {
|
|
@@ -85,10 +85,10 @@
|
|
|
85
85
|
"pg": "^8.23.0",
|
|
86
86
|
"react": "^19.3.0",
|
|
87
87
|
"react-dom": "^19.3.0",
|
|
88
|
-
"@ykurilov/agent-chat-
|
|
89
|
-
"@ykurilov/agent-chat-
|
|
90
|
-
"@ykurilov/agent-chat-
|
|
91
|
-
"@ykurilov/agent-chat-
|
|
88
|
+
"@ykurilov/agent-chat-images": "0.59.0",
|
|
89
|
+
"@ykurilov/agent-chat-memory": "0.59.0",
|
|
90
|
+
"@ykurilov/agent-chat-react": "0.59.0",
|
|
91
|
+
"@ykurilov/agent-chat-attachments": "0.59.0"
|
|
92
92
|
},
|
|
93
93
|
"scripts": {
|
|
94
94
|
"build": "node ../../scripts/build-package.mjs",
|