@case-framework/survey-assistant 0.7.0 → 0.8.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/README.md +58 -3
- package/dist/{attachments-B0hg3vpT.mjs → attachments-BuNni5vB.mjs} +1 -1
- package/dist/{attachments-B0hg3vpT.mjs.map → attachments-BuNni5vB.mjs.map} +1 -1
- package/dist/{authoring-references-BJQ_1nMX.mjs → authoring-references-DbuOKerg.mjs} +12 -3
- package/dist/authoring-references-DbuOKerg.mjs.map +1 -0
- package/dist/{capabilities-DVTfogjv.d.mts → capabilities-CmeeAjEc.d.mts} +3 -4
- package/dist/capabilities-CmeeAjEc.d.mts.map +1 -0
- package/dist/{capabilities-1KNnNO1_.mjs → capabilities-DCMA71PP.mjs} +4 -19
- package/dist/capabilities-DCMA71PP.mjs.map +1 -0
- package/dist/capabilities-default.d.mts +13 -12
- package/dist/capabilities-default.d.mts.map +1 -1
- package/dist/capabilities-default.mjs +2 -411
- package/dist/{constants-Be91Gay9.d.mts → constants-B6HzpEsx.d.mts} +7 -28
- package/dist/constants-B6HzpEsx.d.mts.map +1 -0
- package/dist/{constants-0UHGFcRJ.mjs → constants-Ct-vv9cu.mjs} +1 -1
- package/dist/{constants-0UHGFcRJ.mjs.map → constants-Ct-vv9cu.mjs.map} +1 -1
- package/dist/controller-proxy-D7uQfm5f.d.mts +65 -0
- package/dist/controller-proxy-D7uQfm5f.d.mts.map +1 -0
- package/dist/{controller-proxy-BH05e-C2.mjs → controller-proxy-DEeFl9IA.mjs} +10 -20
- package/dist/controller-proxy-DEeFl9IA.mjs.map +1 -0
- package/dist/default-fhSon2RM.mjs +760 -0
- package/dist/default-fhSon2RM.mjs.map +1 -0
- package/dist/{digest-SJmoYVaK.d.mts → digest-C39WyAWG.d.mts} +2 -3
- package/dist/digest-C39WyAWG.d.mts.map +1 -0
- package/dist/digest.d.mts +1 -1
- package/dist/digest.mjs.map +1 -1
- package/dist/{engine-Bo85Oyj0.mjs → engine-nXoCWYpQ.mjs} +755 -590
- package/dist/engine-nXoCWYpQ.mjs.map +1 -0
- package/dist/engine.d.mts +3 -3
- package/dist/engine.mjs +3 -3
- package/dist/{fair-guidance-CRYCRxQm.mjs → fair-guidance-BmO4PswW.mjs} +1 -1
- package/dist/{fair-guidance-CRYCRxQm.mjs.map → fair-guidance-BmO4PswW.mjs.map} +1 -1
- package/dist/{index-Tnqv-yKW.d.mts → index-Ciq7vHIs.d.mts} +80 -141
- package/dist/index-Ciq7vHIs.d.mts.map +1 -0
- package/dist/{index-nhrgbgO5.d.mts → index-D7wT6P3t.d.mts} +277 -60
- package/dist/index-D7wT6P3t.d.mts.map +1 -0
- package/dist/{index-l_EK4yK7.d.mts → index-WB5kfnz0.d.mts} +15 -71
- package/dist/index-WB5kfnz0.d.mts.map +1 -0
- package/dist/{index-x0qprHgK.d.mts → index-r5riv4md.d.mts} +33 -24
- package/dist/index-r5riv4md.d.mts.map +1 -0
- package/dist/{lifecycle-Bx0aq_4S.d.mts → lifecycle-Dm8u7QFh.d.mts} +34 -18
- package/dist/lifecycle-Dm8u7QFh.d.mts.map +1 -0
- package/dist/{memory-thread-repository-KWHQWFEW.mjs → memory-thread-repository-DAnOGIB3.mjs} +51 -27
- package/dist/memory-thread-repository-DAnOGIB3.mjs.map +1 -0
- package/dist/{protocol-BhzQbx81.mjs → protocol-CF4Bh_Ch.mjs} +118 -36
- package/dist/protocol-CF4Bh_Ch.mjs.map +1 -0
- package/dist/protocol.d.mts +4 -4
- package/dist/protocol.mjs +3 -3
- package/dist/{react-B3ybjJ-X.mjs → react-C_GY-Fsc.mjs} +146 -39
- package/dist/react-C_GY-Fsc.mjs.map +1 -0
- package/dist/react-integration.d.mts +2 -2
- package/dist/react-integration.mjs +2 -2
- package/dist/react.d.mts +3 -3
- package/dist/react.mjs +3 -3
- package/dist/references/assistant-operations.md +100 -31
- package/dist/references/core-rules.md +6 -3
- package/dist/references/embedded-forms.md +34 -14
- package/dist/references/expressions.md +220 -58
- package/dist/references/fair-by-design.md +9 -9
- package/dist/references/item-types.md +305 -71
- package/dist/references/response-variables.md +38 -0
- package/dist/references/source-material-surveys.md +4 -3
- package/dist/references/survey-data-model.md +23 -8
- package/dist/{request-context-CG4SWijt.mjs → request-context-Dg4jSwN1.mjs} +4 -10
- package/dist/request-context-Dg4jSwN1.mjs.map +1 -0
- package/dist/{request-guardrails-TmgQtqp4.mjs → request-guardrails-C0ugBOp0.mjs} +6 -5
- package/dist/request-guardrails-C0ugBOp0.mjs.map +1 -0
- package/dist/server-agent.d.mts +37 -39
- package/dist/server-agent.d.mts.map +1 -1
- package/dist/server-agent.mjs +45 -20
- package/dist/server-agent.mjs.map +1 -1
- package/dist/server-runtime.d.mts +23 -42
- package/dist/server-runtime.d.mts.map +1 -1
- package/dist/server-runtime.mjs +12 -12
- package/dist/server-runtime.mjs.map +1 -1
- package/dist/server-tasks.d.mts +2 -2
- package/dist/server-tasks.mjs +2 -313
- package/dist/server-tools.d.mts +1 -1
- package/dist/server-tools.mjs +1 -1
- package/dist/server.d.mts +13 -36
- package/dist/server.d.mts.map +1 -1
- package/dist/server.mjs +5 -5
- package/dist/server.mjs.map +1 -1
- package/dist/storage-postgres.d.mts +9 -25
- package/dist/storage-postgres.d.mts.map +1 -1
- package/dist/storage-postgres.mjs +5 -4
- package/dist/storage-postgres.mjs.map +1 -1
- package/dist/{tasks-CYqrcaRO.d.mts → tasks-B9h3LPam.d.mts} +18 -34
- package/dist/tasks-B9h3LPam.d.mts.map +1 -0
- package/dist/tasks-BZEiwgxA.mjs +396 -0
- package/dist/tasks-BZEiwgxA.mjs.map +1 -0
- package/dist/thread-documents-DrHbaXqP.d.mts +119 -0
- package/dist/thread-documents-DrHbaXqP.d.mts.map +1 -0
- package/dist/{thread-handlers-DBAaU7BA.d.mts → thread-handlers-Bef0td9N.d.mts} +8 -40
- package/dist/thread-handlers-Bef0td9N.d.mts.map +1 -0
- package/dist/{tools-BvSovehT.mjs → tools-CEsv-WoO.mjs} +296 -105
- package/dist/tools-CEsv-WoO.mjs.map +1 -0
- package/dist/ui.css +1 -1
- package/dist/ui.d.mts +15 -54
- package/dist/ui.d.mts.map +1 -1
- package/dist/ui.mjs +172 -137
- package/dist/ui.mjs.map +1 -1
- package/docs/integration.md +11 -1
- package/package.json +40 -35
- package/dist/authoring-references-BJQ_1nMX.mjs.map +0 -1
- package/dist/capabilities-1KNnNO1_.mjs.map +0 -1
- package/dist/capabilities-DVTfogjv.d.mts.map +0 -1
- package/dist/capabilities-default.mjs.map +0 -1
- package/dist/constants-Be91Gay9.d.mts.map +0 -1
- package/dist/controller-proxy-BH05e-C2.mjs.map +0 -1
- package/dist/controller-proxy-Bd0ZHBPF.d.mts +0 -85
- package/dist/controller-proxy-Bd0ZHBPF.d.mts.map +0 -1
- package/dist/digest-SJmoYVaK.d.mts.map +0 -1
- package/dist/engine-Bo85Oyj0.mjs.map +0 -1
- package/dist/index-Tnqv-yKW.d.mts.map +0 -1
- package/dist/index-l_EK4yK7.d.mts.map +0 -1
- package/dist/index-nhrgbgO5.d.mts.map +0 -1
- package/dist/index-x0qprHgK.d.mts.map +0 -1
- package/dist/lifecycle-Bx0aq_4S.d.mts.map +0 -1
- package/dist/memory-thread-repository-KWHQWFEW.mjs.map +0 -1
- package/dist/protocol-BhzQbx81.mjs.map +0 -1
- package/dist/react-B3ybjJ-X.mjs.map +0 -1
- package/dist/request-context-CG4SWijt.mjs.map +0 -1
- package/dist/request-guardrails-TmgQtqp4.mjs.map +0 -1
- package/dist/server-tasks.mjs.map +0 -1
- package/dist/tasks-CYqrcaRO.d.mts.map +0 -1
- package/dist/thread-documents-rmd6nyX9.d.mts +0 -107
- package/dist/thread-documents-rmd6nyX9.d.mts.map +0 -1
- package/dist/thread-handlers-DBAaU7BA.d.mts.map +0 -1
- package/dist/tools-BvSovehT.mjs.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { u as SurveyAssistantRequestError } from "./request-guardrails-
|
|
1
|
+
import { u as SurveyAssistantRequestError } from "./request-guardrails-C0ugBOp0.mjs";
|
|
2
2
|
import { createHash, randomUUID } from "node:crypto";
|
|
3
3
|
import { OBSERVATIONAL_MEMORY_TABLE_SCHEMA } from "@mastra/core/storage";
|
|
4
4
|
import { PostgresStore } from "@mastra/pg";
|
|
@@ -6,7 +6,7 @@ import { PostgresStore } from "@mastra/pg";
|
|
|
6
6
|
const OBSERVATIONAL_MEMORY_TABLE = "mastra_observational_memory";
|
|
7
7
|
const THREAD_DOCUMENTS_TABLE = "survey_assistant_thread_documents";
|
|
8
8
|
const SURVEY_ASSISTANT_DEFAULT_MAX_STORED_ATTACHMENTS_PER_THREAD = 100;
|
|
9
|
-
const SURVEY_ASSISTANT_DEFAULT_MAX_STORED_ATTACHMENT_BYTES_PER_THREAD =
|
|
9
|
+
const SURVEY_ASSISTANT_DEFAULT_MAX_STORED_ATTACHMENT_BYTES_PER_THREAD = 209715200;
|
|
10
10
|
const quoteIdentifier = (value) => `"${value.replaceAll("\"", "\"\"")}"`;
|
|
11
11
|
const postgresType = (column) => {
|
|
12
12
|
switch (column.type) {
|
|
@@ -91,7 +91,7 @@ const createSurveyAssistantPostgresThreadDocumentRepository = ({ getStorage, max
|
|
|
91
91
|
async put({ bytes, data, mediaType, name, resourceId, threadId }) {
|
|
92
92
|
const storage = await getStorage();
|
|
93
93
|
const contentHash = createHash("sha256").update(data).digest("hex");
|
|
94
|
-
|
|
94
|
+
const row = await storage.db.tx(async (transaction) => {
|
|
95
95
|
await transaction.none("SELECT pg_advisory_xact_lock(hashtext($1), hashtext($2))", [resourceId, threadId]);
|
|
96
96
|
const existing = await transaction.oneOrNone(`SELECT id, name, media_type, byte_size, content_hash, created_at FROM ${tableName}
|
|
97
97
|
WHERE resource_id = $1 AND thread_id = $2 AND content_hash = $3`, [
|
|
@@ -118,7 +118,8 @@ const createSurveyAssistantPostgresThreadDocumentRepository = ({ getStorage, max
|
|
|
118
118
|
contentHash,
|
|
119
119
|
data
|
|
120
120
|
]);
|
|
121
|
-
})
|
|
121
|
+
});
|
|
122
|
+
return toThreadDocument(row);
|
|
122
123
|
},
|
|
123
124
|
async list({ resourceId, threadId }) {
|
|
124
125
|
return (await (await getStorage()).db.manyOrNone(`SELECT id, name, media_type, byte_size, content_hash, created_at
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage-postgres.mjs","names":[],"sources":["../src/storage/postgres.ts"],"sourcesContent":["import { OBSERVATIONAL_MEMORY_TABLE_SCHEMA, type StorageColumn } from \"@mastra/core/storage\";\nimport { PostgresStore, type PostgresStoreConfig } from \"@mastra/pg\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport { SurveyAssistantRequestError } from \"../server/request-guardrails\";\nimport type {\n SurveyAssistantThreadDocument,\n SurveyAssistantThreadDocumentRepository,\n} from \"../server/thread-documents\";\n\nconst OBSERVATIONAL_MEMORY_TABLE = \"mastra_observational_memory\";\nconst THREAD_DOCUMENTS_TABLE = \"survey_assistant_thread_documents\";\nexport const SURVEY_ASSISTANT_DEFAULT_MAX_STORED_ATTACHMENTS_PER_THREAD = 100;\nexport const SURVEY_ASSISTANT_DEFAULT_MAX_STORED_ATTACHMENT_BYTES_PER_THREAD = 200 * 1024 * 1024;\n\nconst quoteIdentifier = (value: string) => `\"${value.replaceAll('\"', '\"\"')}\"`;\n\nconst postgresType = (column: StorageColumn) => {\n switch (column.type) {\n case \"text\":\n return \"TEXT\";\n case \"timestamp\":\n return \"TIMESTAMP\";\n case \"jsonb\":\n return \"JSONB\";\n case \"integer\":\n return \"INTEGER\";\n case \"float\":\n return \"DOUBLE PRECISION\";\n case \"bigint\":\n return \"BIGINT\";\n case \"boolean\":\n return \"BOOLEAN\";\n case \"uuid\":\n return \"UUID\";\n }\n};\n\nconst migrationDefault = (column: StorageColumn) => {\n switch (column.type) {\n case \"boolean\":\n return \"FALSE\";\n case \"integer\":\n case \"bigint\":\n case \"float\":\n return \"0\";\n case \"jsonb\":\n return \"'{}'::jsonb\";\n case \"timestamp\":\n return \"NOW()\";\n case \"text\":\n return \"''\";\n case \"uuid\":\n return \"gen_random_uuid()\";\n }\n};\n\nconst columnSql = (name: string, column: StorageColumn, includeMigrationDefault = false) => {\n const constraints = [\n column.primaryKey ? \"PRIMARY KEY\" : undefined,\n column.nullable === false ? \"NOT NULL\" : undefined,\n includeMigrationDefault && column.nullable === false\n ? `DEFAULT ${migrationDefault(column)}`\n : undefined,\n ]\n .filter(Boolean)\n .join(\" \");\n return `${quoteIdentifier(name)} ${postgresType(column)}${constraints ? ` ${constraints}` : \"\"}`;\n};\n\nconst ensureObservationalMemorySchema = async (storage: PostgresStore, schemaName: string) => {\n const tableSchema = OBSERVATIONAL_MEMORY_TABLE_SCHEMA[OBSERVATIONAL_MEMORY_TABLE];\n if (!tableSchema) throw new Error(\"Mastra observational memory schema is unavailable.\");\n\n const tableName = `${quoteIdentifier(schemaName)}.${quoteIdentifier(OBSERVATIONAL_MEMORY_TABLE)}`;\n const columns = Object.entries(tableSchema).flatMap(([name, column]) => [\n columnSql(name, column),\n ...(column.type === \"timestamp\"\n ? [`${quoteIdentifier(`${name}Z`)} TIMESTAMPTZ DEFAULT NOW()`]\n : []),\n ]);\n\n await storage.db.none(`CREATE TABLE IF NOT EXISTS ${tableName} (${columns.join(\", \")})`);\n for (const [name, column] of Object.entries(tableSchema)) {\n await storage.db.none(\n `ALTER TABLE ${tableName} ADD COLUMN IF NOT EXISTS ${columnSql(name, column, true)}`,\n );\n if (column.type === \"timestamp\") {\n await storage.db.none(\n `ALTER TABLE ${tableName} ADD COLUMN IF NOT EXISTS ${quoteIdentifier(`${name}Z`)} TIMESTAMPTZ DEFAULT NOW()`,\n );\n }\n }\n await storage.db.none(\n `CREATE INDEX IF NOT EXISTS idx_om_lookup_key ON ${tableName} (${quoteIdentifier(\"lookupKey\")})`,\n );\n};\n\nconst ensureThreadDocumentsSchema = async (storage: PostgresStore, schemaName: string) => {\n const tableName = `${quoteIdentifier(schemaName)}.${quoteIdentifier(THREAD_DOCUMENTS_TABLE)}`;\n await storage.db.none(`\n CREATE TABLE IF NOT EXISTS ${tableName} (\n id TEXT PRIMARY KEY,\n resource_id TEXT NOT NULL,\n thread_id TEXT NOT NULL,\n name TEXT NOT NULL,\n media_type TEXT NOT NULL,\n byte_size INTEGER NOT NULL,\n content_hash TEXT NOT NULL,\n data BYTEA NOT NULL,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n UNIQUE (resource_id, thread_id, content_hash)\n )\n `);\n await storage.db.none(\n `CREATE INDEX IF NOT EXISTS idx_survey_assistant_thread_documents_thread ON ${tableName} (resource_id, thread_id, created_at)`,\n );\n};\n\nexport interface InitializeSurveyAssistantPostgresStorageOptions {\n ensureObservationalMemory?: boolean;\n schemaName?: string;\n}\n\nexport const initializeSurveyAssistantPostgresStorage = async (\n storage: PostgresStore,\n {\n ensureObservationalMemory = true,\n schemaName = \"public\",\n }: InitializeSurveyAssistantPostgresStorageOptions = {},\n) => {\n await storage.init();\n if (ensureObservationalMemory) await ensureObservationalMemorySchema(storage, schemaName);\n await ensureThreadDocumentsSchema(storage, schemaName);\n return storage;\n};\n\nconst toThreadDocument = (row: {\n id: string;\n name: string;\n media_type: string;\n byte_size: number;\n content_hash: string;\n created_at: Date | string;\n}): SurveyAssistantThreadDocument => ({\n id: row.id,\n name: row.name,\n mediaType: row.media_type,\n bytes: row.byte_size,\n contentHash: row.content_hash,\n createdAt: new Date(row.created_at).toISOString(),\n});\n\nexport interface CreateSurveyAssistantPostgresThreadDocumentRepositoryOptions {\n getStorage(): PostgresStore | Promise<PostgresStore>;\n maxStoredAttachmentsPerThread?: number;\n maxStoredBytesPerThread?: number;\n schemaName?: string;\n}\n\nexport const createSurveyAssistantPostgresThreadDocumentRepository = ({\n getStorage,\n maxStoredAttachmentsPerThread = SURVEY_ASSISTANT_DEFAULT_MAX_STORED_ATTACHMENTS_PER_THREAD,\n maxStoredBytesPerThread = SURVEY_ASSISTANT_DEFAULT_MAX_STORED_ATTACHMENT_BYTES_PER_THREAD,\n schemaName = \"public\",\n}: CreateSurveyAssistantPostgresThreadDocumentRepositoryOptions): SurveyAssistantThreadDocumentRepository => {\n if (\n !Number.isSafeInteger(maxStoredAttachmentsPerThread) ||\n maxStoredAttachmentsPerThread <= 0 ||\n !Number.isSafeInteger(maxStoredBytesPerThread) ||\n maxStoredBytesPerThread <= 0\n ) {\n throw new Error(\"Survey assistant stored attachment limits must be positive integers.\");\n }\n const tableName = `${quoteIdentifier(schemaName)}.${quoteIdentifier(THREAD_DOCUMENTS_TABLE)}`;\n return {\n async put({ bytes, data, mediaType, name, resourceId, threadId }) {\n const storage = await getStorage();\n const contentHash = createHash(\"sha256\").update(data).digest(\"hex\");\n const row = await storage.db.tx(async (transaction) => {\n await transaction.none(\"SELECT pg_advisory_xact_lock(hashtext($1), hashtext($2))\", [\n resourceId,\n threadId,\n ]);\n const existing = await transaction.oneOrNone(\n `SELECT id, name, media_type, byte_size, content_hash, created_at FROM ${tableName}\n WHERE resource_id = $1 AND thread_id = $2 AND content_hash = $3`,\n [resourceId, threadId, contentHash],\n );\n if (existing) return existing;\n\n const usage = await transaction.one<{ count: string; bytes: string }>(\n `SELECT COUNT(*) AS count, COALESCE(SUM(byte_size), 0) AS bytes FROM ${tableName}\n WHERE resource_id = $1 AND thread_id = $2`,\n [resourceId, threadId],\n );\n if (Number(usage.count) >= maxStoredAttachmentsPerThread) {\n throw new SurveyAssistantRequestError(\n `A chat can contain up to ${maxStoredAttachmentsPerThread} stored attachments.`,\n );\n }\n if (Number(usage.bytes) + bytes > maxStoredBytesPerThread) {\n throw new SurveyAssistantRequestError(\n \"The attachments stored for this chat are too large in total.\",\n );\n }\n\n return transaction.one(\n `INSERT INTO ${tableName}\n (id, resource_id, thread_id, name, media_type, byte_size, content_hash, data)\n VALUES\n ($1, $2, $3, $4, $5, $6, $7, decode($8, 'base64'))\n RETURNING id, name, media_type, byte_size, content_hash, created_at`,\n [randomUUID(), resourceId, threadId, name, mediaType, bytes, contentHash, data],\n );\n });\n return toThreadDocument(row);\n },\n async list({ resourceId, threadId }) {\n const storage = await getStorage();\n const rows = await storage.db.manyOrNone(\n `SELECT id, name, media_type, byte_size, content_hash, created_at\n FROM ${tableName}\n WHERE resource_id = $1 AND thread_id = $2\n ORDER BY created_at ASC`,\n [resourceId, threadId],\n );\n return rows.map(toThreadDocument);\n },\n async read({ documentId, resourceId, threadId }) {\n const storage = await getStorage();\n const row = await storage.db.oneOrNone(\n `SELECT id, name, media_type, byte_size, content_hash, created_at,\n encode(data, 'base64') AS data\n FROM ${tableName}\n WHERE id = $1 AND resource_id = $2 AND thread_id = $3`,\n [documentId, resourceId, threadId],\n );\n return row\n ? {\n ...toThreadDocument(row),\n // PostgreSQL wraps encoded BYTEA output at 76 characters.\n data: String(row.data).replace(/\\s+/g, \"\"),\n }\n : null;\n },\n async deleteThread({ resourceId, threadId }) {\n const storage = await getStorage();\n await storage.db.none(`DELETE FROM ${tableName} WHERE resource_id = $1 AND thread_id = $2`, [\n resourceId,\n threadId,\n ]);\n },\n };\n};\n\nexport interface CreateSurveyAssistantPostgresRuntimeOptions {\n ensureObservationalMemory?: boolean;\n maxStoredAttachmentsPerThread?: number;\n maxStoredBytesPerThread?: number;\n schemaName?: string;\n store: PostgresStore | PostgresStoreConfig;\n}\n\nexport const createSurveyAssistantPostgresRuntime = ({\n ensureObservationalMemory,\n maxStoredAttachmentsPerThread,\n maxStoredBytesPerThread,\n schemaName = \"public\",\n store: storeOrConfig,\n}: CreateSurveyAssistantPostgresRuntimeOptions) => {\n const storage =\n storeOrConfig instanceof PostgresStore ? storeOrConfig : new PostgresStore(storeOrConfig);\n let initialization: Promise<PostgresStore> | undefined;\n const getStorage = () => {\n initialization ??= initializeSurveyAssistantPostgresStorage(storage, {\n ensureObservationalMemory,\n schemaName,\n }).catch((error: unknown) => {\n initialization = undefined;\n throw error;\n });\n return initialization;\n };\n\n return {\n storage,\n getStorage,\n threadDocuments: createSurveyAssistantPostgresThreadDocumentRepository({\n getStorage,\n maxStoredAttachmentsPerThread,\n maxStoredBytesPerThread,\n schemaName,\n }),\n };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"storage-postgres.mjs","names":[],"sources":["../src/storage/postgres.ts"],"sourcesContent":["import { OBSERVATIONAL_MEMORY_TABLE_SCHEMA, type StorageColumn } from \"@mastra/core/storage\";\nimport { PostgresStore, type PostgresStoreConfig } from \"@mastra/pg\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport { SurveyAssistantRequestError } from \"../server/request-guardrails\";\nimport type {\n SurveyAssistantThreadDocument,\n SurveyAssistantThreadDocumentRepository,\n} from \"../server/thread-documents\";\n\nconst OBSERVATIONAL_MEMORY_TABLE = \"mastra_observational_memory\";\nconst THREAD_DOCUMENTS_TABLE = \"survey_assistant_thread_documents\";\nexport const SURVEY_ASSISTANT_DEFAULT_MAX_STORED_ATTACHMENTS_PER_THREAD = 100;\nexport const SURVEY_ASSISTANT_DEFAULT_MAX_STORED_ATTACHMENT_BYTES_PER_THREAD = 200 * 1024 * 1024;\n\nconst quoteIdentifier = (value: string) => `\"${value.replaceAll('\"', '\"\"')}\"`;\n\nconst postgresType = (column: StorageColumn) => {\n switch (column.type) {\n case \"text\":\n return \"TEXT\";\n case \"timestamp\":\n return \"TIMESTAMP\";\n case \"jsonb\":\n return \"JSONB\";\n case \"integer\":\n return \"INTEGER\";\n case \"float\":\n return \"DOUBLE PRECISION\";\n case \"bigint\":\n return \"BIGINT\";\n case \"boolean\":\n return \"BOOLEAN\";\n case \"uuid\":\n return \"UUID\";\n }\n};\n\nconst migrationDefault = (column: StorageColumn) => {\n switch (column.type) {\n case \"boolean\":\n return \"FALSE\";\n case \"integer\":\n case \"bigint\":\n case \"float\":\n return \"0\";\n case \"jsonb\":\n return \"'{}'::jsonb\";\n case \"timestamp\":\n return \"NOW()\";\n case \"text\":\n return \"''\";\n case \"uuid\":\n return \"gen_random_uuid()\";\n }\n};\n\nconst columnSql = (name: string, column: StorageColumn, includeMigrationDefault = false) => {\n const constraints = [\n column.primaryKey ? \"PRIMARY KEY\" : undefined,\n column.nullable === false ? \"NOT NULL\" : undefined,\n includeMigrationDefault && column.nullable === false\n ? `DEFAULT ${migrationDefault(column)}`\n : undefined,\n ]\n .filter(Boolean)\n .join(\" \");\n return `${quoteIdentifier(name)} ${postgresType(column)}${constraints ? ` ${constraints}` : \"\"}`;\n};\n\nconst ensureObservationalMemorySchema = async (storage: PostgresStore, schemaName: string) => {\n const tableSchema = OBSERVATIONAL_MEMORY_TABLE_SCHEMA[OBSERVATIONAL_MEMORY_TABLE];\n if (!tableSchema) throw new Error(\"Mastra observational memory schema is unavailable.\");\n\n const tableName = `${quoteIdentifier(schemaName)}.${quoteIdentifier(OBSERVATIONAL_MEMORY_TABLE)}`;\n const columns = Object.entries(tableSchema).flatMap(([name, column]) => [\n columnSql(name, column),\n ...(column.type === \"timestamp\"\n ? [`${quoteIdentifier(`${name}Z`)} TIMESTAMPTZ DEFAULT NOW()`]\n : []),\n ]);\n\n await storage.db.none(`CREATE TABLE IF NOT EXISTS ${tableName} (${columns.join(\", \")})`);\n for (const [name, column] of Object.entries(tableSchema)) {\n await storage.db.none(\n `ALTER TABLE ${tableName} ADD COLUMN IF NOT EXISTS ${columnSql(name, column, true)}`,\n );\n if (column.type === \"timestamp\") {\n await storage.db.none(\n `ALTER TABLE ${tableName} ADD COLUMN IF NOT EXISTS ${quoteIdentifier(`${name}Z`)} TIMESTAMPTZ DEFAULT NOW()`,\n );\n }\n }\n await storage.db.none(\n `CREATE INDEX IF NOT EXISTS idx_om_lookup_key ON ${tableName} (${quoteIdentifier(\"lookupKey\")})`,\n );\n};\n\nconst ensureThreadDocumentsSchema = async (storage: PostgresStore, schemaName: string) => {\n const tableName = `${quoteIdentifier(schemaName)}.${quoteIdentifier(THREAD_DOCUMENTS_TABLE)}`;\n await storage.db.none(`\n CREATE TABLE IF NOT EXISTS ${tableName} (\n id TEXT PRIMARY KEY,\n resource_id TEXT NOT NULL,\n thread_id TEXT NOT NULL,\n name TEXT NOT NULL,\n media_type TEXT NOT NULL,\n byte_size INTEGER NOT NULL,\n content_hash TEXT NOT NULL,\n data BYTEA NOT NULL,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n UNIQUE (resource_id, thread_id, content_hash)\n )\n `);\n await storage.db.none(\n `CREATE INDEX IF NOT EXISTS idx_survey_assistant_thread_documents_thread ON ${tableName} (resource_id, thread_id, created_at)`,\n );\n};\n\nexport interface InitializeSurveyAssistantPostgresStorageOptions {\n ensureObservationalMemory?: boolean;\n schemaName?: string;\n}\n\nexport const initializeSurveyAssistantPostgresStorage = async (\n storage: PostgresStore,\n {\n ensureObservationalMemory = true,\n schemaName = \"public\",\n }: InitializeSurveyAssistantPostgresStorageOptions = {},\n) => {\n await storage.init();\n if (ensureObservationalMemory) await ensureObservationalMemorySchema(storage, schemaName);\n await ensureThreadDocumentsSchema(storage, schemaName);\n return storage;\n};\n\nconst toThreadDocument = (row: {\n id: string;\n name: string;\n media_type: string;\n byte_size: number;\n content_hash: string;\n created_at: Date | string;\n}): SurveyAssistantThreadDocument => ({\n id: row.id,\n name: row.name,\n mediaType: row.media_type,\n bytes: row.byte_size,\n contentHash: row.content_hash,\n createdAt: new Date(row.created_at).toISOString(),\n});\n\nexport interface CreateSurveyAssistantPostgresThreadDocumentRepositoryOptions {\n getStorage(): PostgresStore | Promise<PostgresStore>;\n maxStoredAttachmentsPerThread?: number;\n maxStoredBytesPerThread?: number;\n schemaName?: string;\n}\n\nexport const createSurveyAssistantPostgresThreadDocumentRepository = ({\n getStorage,\n maxStoredAttachmentsPerThread = SURVEY_ASSISTANT_DEFAULT_MAX_STORED_ATTACHMENTS_PER_THREAD,\n maxStoredBytesPerThread = SURVEY_ASSISTANT_DEFAULT_MAX_STORED_ATTACHMENT_BYTES_PER_THREAD,\n schemaName = \"public\",\n}: CreateSurveyAssistantPostgresThreadDocumentRepositoryOptions): SurveyAssistantThreadDocumentRepository => {\n if (\n !Number.isSafeInteger(maxStoredAttachmentsPerThread) ||\n maxStoredAttachmentsPerThread <= 0 ||\n !Number.isSafeInteger(maxStoredBytesPerThread) ||\n maxStoredBytesPerThread <= 0\n ) {\n throw new Error(\"Survey assistant stored attachment limits must be positive integers.\");\n }\n const tableName = `${quoteIdentifier(schemaName)}.${quoteIdentifier(THREAD_DOCUMENTS_TABLE)}`;\n return {\n async put({ bytes, data, mediaType, name, resourceId, threadId }) {\n const storage = await getStorage();\n const contentHash = createHash(\"sha256\").update(data).digest(\"hex\");\n const row = await storage.db.tx(async (transaction) => {\n await transaction.none(\"SELECT pg_advisory_xact_lock(hashtext($1), hashtext($2))\", [\n resourceId,\n threadId,\n ]);\n const existing = await transaction.oneOrNone(\n `SELECT id, name, media_type, byte_size, content_hash, created_at FROM ${tableName}\n WHERE resource_id = $1 AND thread_id = $2 AND content_hash = $3`,\n [resourceId, threadId, contentHash],\n );\n if (existing) return existing;\n\n const usage = await transaction.one<{ count: string; bytes: string }>(\n `SELECT COUNT(*) AS count, COALESCE(SUM(byte_size), 0) AS bytes FROM ${tableName}\n WHERE resource_id = $1 AND thread_id = $2`,\n [resourceId, threadId],\n );\n if (Number(usage.count) >= maxStoredAttachmentsPerThread) {\n throw new SurveyAssistantRequestError(\n `A chat can contain up to ${maxStoredAttachmentsPerThread} stored attachments.`,\n );\n }\n if (Number(usage.bytes) + bytes > maxStoredBytesPerThread) {\n throw new SurveyAssistantRequestError(\n \"The attachments stored for this chat are too large in total.\",\n );\n }\n\n return transaction.one(\n `INSERT INTO ${tableName}\n (id, resource_id, thread_id, name, media_type, byte_size, content_hash, data)\n VALUES\n ($1, $2, $3, $4, $5, $6, $7, decode($8, 'base64'))\n RETURNING id, name, media_type, byte_size, content_hash, created_at`,\n [randomUUID(), resourceId, threadId, name, mediaType, bytes, contentHash, data],\n );\n });\n return toThreadDocument(row);\n },\n async list({ resourceId, threadId }) {\n const storage = await getStorage();\n const rows = await storage.db.manyOrNone(\n `SELECT id, name, media_type, byte_size, content_hash, created_at\n FROM ${tableName}\n WHERE resource_id = $1 AND thread_id = $2\n ORDER BY created_at ASC`,\n [resourceId, threadId],\n );\n return rows.map(toThreadDocument);\n },\n async read({ documentId, resourceId, threadId }) {\n const storage = await getStorage();\n const row = await storage.db.oneOrNone(\n `SELECT id, name, media_type, byte_size, content_hash, created_at,\n encode(data, 'base64') AS data\n FROM ${tableName}\n WHERE id = $1 AND resource_id = $2 AND thread_id = $3`,\n [documentId, resourceId, threadId],\n );\n return row\n ? {\n ...toThreadDocument(row),\n // PostgreSQL wraps encoded BYTEA output at 76 characters.\n data: String(row.data).replace(/\\s+/g, \"\"),\n }\n : null;\n },\n async deleteThread({ resourceId, threadId }) {\n const storage = await getStorage();\n await storage.db.none(`DELETE FROM ${tableName} WHERE resource_id = $1 AND thread_id = $2`, [\n resourceId,\n threadId,\n ]);\n },\n };\n};\n\nexport interface CreateSurveyAssistantPostgresRuntimeOptions {\n ensureObservationalMemory?: boolean;\n maxStoredAttachmentsPerThread?: number;\n maxStoredBytesPerThread?: number;\n schemaName?: string;\n store: PostgresStore | PostgresStoreConfig;\n}\n\nexport const createSurveyAssistantPostgresRuntime = ({\n ensureObservationalMemory,\n maxStoredAttachmentsPerThread,\n maxStoredBytesPerThread,\n schemaName = \"public\",\n store: storeOrConfig,\n}: CreateSurveyAssistantPostgresRuntimeOptions) => {\n const storage =\n storeOrConfig instanceof PostgresStore ? storeOrConfig : new PostgresStore(storeOrConfig);\n let initialization: Promise<PostgresStore> | undefined;\n const getStorage = () => {\n initialization ??= initializeSurveyAssistantPostgresStorage(storage, {\n ensureObservationalMemory,\n schemaName,\n }).catch((error: unknown) => {\n initialization = undefined;\n throw error;\n });\n return initialization;\n };\n\n return {\n storage,\n getStorage,\n threadDocuments: createSurveyAssistantPostgresThreadDocumentRepository({\n getStorage,\n maxStoredAttachmentsPerThread,\n maxStoredBytesPerThread,\n schemaName,\n }),\n };\n};\n"],"mappings":"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"}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import { c as
|
|
2
|
-
import { a as SurveyAssistantModelSelection, n as SURVEY_ASSISTANT_TASK_AGENT_ID, o as SurveyAssistantRequestContext } from "./constants-
|
|
1
|
+
import { b as SurveyAssistantTranslateContentInput, c as SurveyAssistantSuggestItemLabelInput, d as SurveyAssistantSuggestResponseNameResult, f as SurveyAssistantSuggestResponseSettingsInput, g as SurveyAssistantTaskInvocationMode, h as SurveyAssistantTaskContext, l as SurveyAssistantSuggestItemLabelResult, u as SurveyAssistantSuggestResponseNameInput, x as SurveyAssistantTranslateContentResult } from "./index-D7wT6P3t.mjs";
|
|
2
|
+
import { a as SurveyAssistantModelSelection, n as SURVEY_ASSISTANT_TASK_AGENT_ID, o as SurveyAssistantRequestContext } from "./constants-B6HzpEsx.mjs";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { RequestContext } from "@mastra/core/request-context";
|
|
5
|
-
import * as _$_mastra_core_agent0 from "@mastra/core/agent";
|
|
6
5
|
import { Agent, AgentConfig, AgentExecutionOptions } from "@mastra/core/agent";
|
|
7
|
-
|
|
8
6
|
//#region src/server/authoring-references.d.ts
|
|
9
|
-
declare const surveyAuthoringReferenceTopics: readonly ["core-rules", "survey-data-model", "source-material-surveys", "localization", "rich-text-content", "item-types", "embedded-forms", "expressions", "assistant-operations", "fair-by-design", "survey-quality"];
|
|
7
|
+
declare const surveyAuthoringReferenceTopics: readonly ["core-rules", "survey-data-model", "response-variables", "source-material-surveys", "localization", "rich-text-content", "item-types", "embedded-forms", "expressions", "assistant-operations", "fair-by-design", "survey-quality"];
|
|
10
8
|
type SurveyAuthoringReferenceTopic = (typeof surveyAuthoringReferenceTopics)[number];
|
|
11
9
|
declare const surveyAuthoringReferences: {
|
|
12
10
|
readonly "core-rules": {
|
|
@@ -14,6 +12,11 @@ declare const surveyAuthoringReferences: {
|
|
|
14
12
|
readonly content: string;
|
|
15
13
|
readonly loadWhen: "Load when the task depends on canonical tool choice, validation/apply semantics, identifier handling, or another cross-cutting authoring rule that the current turn has not already established.";
|
|
16
14
|
};
|
|
15
|
+
readonly "response-variables": {
|
|
16
|
+
readonly title: "Response variables and coding";
|
|
17
|
+
readonly content: string;
|
|
18
|
+
readonly loadWhen: "Load before creating response inputs, changing variable names, option codes, matrix coding components, or export settings. Inspect current response slots for exact IDs and domains.";
|
|
19
|
+
};
|
|
17
20
|
readonly "survey-data-model": {
|
|
18
21
|
readonly title: "Survey data model";
|
|
19
22
|
readonly content: string;
|
|
@@ -72,9 +75,7 @@ type SurveyAssistantTaskAgentConfig = AgentConfig<typeof SURVEY_ASSISTANT_TASK_A
|
|
|
72
75
|
interface CreateSurveyAssistantTaskAgentOptions {
|
|
73
76
|
model: SurveyAssistantTaskAgentConfig["model"];
|
|
74
77
|
}
|
|
75
|
-
declare const createSurveyAssistantTaskAgent: ({
|
|
76
|
-
model
|
|
77
|
-
}: CreateSurveyAssistantTaskAgentOptions) => Agent<"survey-assistant-task-agent", Record<string, never>, undefined, SurveyAssistantRequestContext, _$_mastra_core_agent0.AgentEditorConfig | undefined>;
|
|
78
|
+
declare const createSurveyAssistantTaskAgent: ({ model }: CreateSurveyAssistantTaskAgentOptions) => Agent<"survey-assistant-task-agent", Record<string, never>, undefined, SurveyAssistantRequestContext, import("@mastra/core/agent").AgentEditorConfig | undefined>;
|
|
78
79
|
interface RunSurveyAssistantTaskOptions<TSchema extends z.ZodType> {
|
|
79
80
|
agent: Pick<Agent, "generate">;
|
|
80
81
|
taskId: string;
|
|
@@ -87,18 +88,7 @@ interface RunSurveyAssistantTaskOptions<TSchema extends z.ZodType> {
|
|
|
87
88
|
providerOptions?: AgentExecutionOptions["providerOptions"];
|
|
88
89
|
abortSignal?: AbortSignal;
|
|
89
90
|
}
|
|
90
|
-
declare const runSurveyAssistantTask: <TSchema extends z.ZodType>({
|
|
91
|
-
agent,
|
|
92
|
-
taskId,
|
|
93
|
-
input,
|
|
94
|
-
context,
|
|
95
|
-
schema,
|
|
96
|
-
instructions,
|
|
97
|
-
referenceTopics,
|
|
98
|
-
requestContext,
|
|
99
|
-
providerOptions,
|
|
100
|
-
abortSignal
|
|
101
|
-
}: RunSurveyAssistantTaskOptions<TSchema>) => Promise<z.output<TSchema>>;
|
|
91
|
+
declare const runSurveyAssistantTask: <TSchema extends z.ZodType>({ agent, taskId, input, context, schema, instructions, referenceTopics, requestContext, providerOptions, abortSignal }: RunSurveyAssistantTaskOptions<TSchema>) => Promise<z.output<TSchema>>;
|
|
102
92
|
interface RunSurveyAssistantBuiltInTaskOptions {
|
|
103
93
|
agent: Pick<Agent, "generate">;
|
|
104
94
|
context: SurveyAssistantTaskContext;
|
|
@@ -107,7 +97,11 @@ interface RunSurveyAssistantBuiltInTaskOptions {
|
|
|
107
97
|
providerOptions?: AgentExecutionOptions["providerOptions"];
|
|
108
98
|
abortSignal?: AbortSignal;
|
|
109
99
|
}
|
|
110
|
-
declare const
|
|
100
|
+
declare const runSurveyAssistantSuggestResponseNameTask: (input: SurveyAssistantSuggestResponseNameInput, options: RunSurveyAssistantBuiltInTaskOptions) => Promise<SurveyAssistantSuggestResponseNameResult>;
|
|
101
|
+
declare const runSurveyAssistantSuggestResponseSettingsTask: (input: SurveyAssistantSuggestResponseSettingsInput, options: RunSurveyAssistantBuiltInTaskOptions) => Promise<{
|
|
102
|
+
naming: import("@case-framework/survey-core/editor").ResponseNamingEdit[];
|
|
103
|
+
settings: import("@case-framework/survey-core/editor").ResponseSettingsEdit[];
|
|
104
|
+
}>;
|
|
111
105
|
declare const runSurveyAssistantSuggestItemLabelTask: (input: SurveyAssistantSuggestItemLabelInput, options: RunSurveyAssistantBuiltInTaskOptions) => Promise<SurveyAssistantSuggestItemLabelResult>;
|
|
112
106
|
declare const runSurveyAssistantTranslateContentTask: (input: SurveyAssistantTranslateContentInput, options: RunSurveyAssistantBuiltInTaskOptions) => Promise<SurveyAssistantTranslateContentResult>;
|
|
113
107
|
interface SurveyAssistantTaskAuthorizedIdentity {
|
|
@@ -140,17 +134,7 @@ interface CreateSurveyAssistantTaskHandlerOptions {
|
|
|
140
134
|
providerOptions?: AgentExecutionOptions["providerOptions"];
|
|
141
135
|
resolveModel?(input: SurveyAssistantTaskModelResolutionInput): SurveyAssistantModelSelection | null | undefined | Promise<SurveyAssistantModelSelection | null | undefined>;
|
|
142
136
|
}
|
|
143
|
-
declare const createSurveyAssistantTaskHandler: ({
|
|
144
|
-
agent,
|
|
145
|
-
authorize,
|
|
146
|
-
getReadinessError,
|
|
147
|
-
isEnabled,
|
|
148
|
-
maxConcurrentRequests,
|
|
149
|
-
maxDurationMs,
|
|
150
|
-
onTaskMetrics,
|
|
151
|
-
providerOptions,
|
|
152
|
-
resolveModel
|
|
153
|
-
}: CreateSurveyAssistantTaskHandlerOptions) => (request: Request) => Promise<Response>;
|
|
137
|
+
declare const createSurveyAssistantTaskHandler: ({ agent, authorize, getReadinessError, isEnabled, maxConcurrentRequests, maxDurationMs, onTaskMetrics, providerOptions, resolveModel }: CreateSurveyAssistantTaskHandlerOptions) => (request: Request) => Promise<Response>;
|
|
154
138
|
//#endregion
|
|
155
|
-
export {
|
|
156
|
-
//# sourceMappingURL=tasks-
|
|
139
|
+
export { surveyAuthoringReferenceTopics as _, SurveyAssistantTaskAuthorizationInput as a, createSurveyAssistantTaskAgent as c, runSurveyAssistantSuggestResponseNameTask as d, runSurveyAssistantSuggestResponseSettingsTask as f, formatSurveyAuthoringReferenceRoutingInstructions as g, SurveyAuthoringReferenceTopic as h, RunSurveyAssistantTaskOptions as i, createSurveyAssistantTaskHandler as l, runSurveyAssistantTranslateContentTask as m, CreateSurveyAssistantTaskHandlerOptions as n, SurveyAssistantTaskAuthorizedIdentity as o, runSurveyAssistantTask as p, RunSurveyAssistantBuiltInTaskOptions as r, SurveyAssistantTaskModelResolutionInput as s, CreateSurveyAssistantTaskAgentOptions as t, runSurveyAssistantSuggestItemLabelTask as u, surveyAuthoringReferences as v };
|
|
140
|
+
//# sourceMappingURL=tasks-B9h3LPam.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tasks-B9h3LPam.d.mts","names":[],"sources":["../src/server/authoring-references.ts","../src/server/tasks.ts"],"mappings":";;;;;;cAMa;KAeD,wCAAwC;cAsCvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA2EA;;;KCrGR,iCAAiC,mBAC7B,gCACP,kCAEA;UAGe;EACf,OAAO;;cAGI,mCAAkC,SAAW,0CAAqC,qCAAA,kCAAA,4DAAA;UAc9E,8BAA8B,gBAAgB,EAAE;EAC/D,OAAO,KAAK;EACZ;EACA;EACA,SAAS;EACT,QAAQ;EACR;EACA,2BAA2B;EAC3B,iBAAiB,eAAe;EAChC,kBAAkB;EAClB,cAAc;;cAUH,yBAAgC,gBAAgB,EAAE,WAAS,OAAA,QAAA,OAAA,SAAA,QAAA,cAAA,iBAAA,gBAAA,iBAAA,eAWrE,8BAA8B,aAAW,QAAQ,EAAE,OAAO;UA6E5C;EACf,OAAO,KAAK;EACZ,SAAS;EACT,OAAO;EACP,iBAAiB,eAAe;EAChC,kBAAkB;EAClB,cAAc;;cA8CH,4CACX,OAAO,yCACP,SAAS,yCACR,QAAQ;cAkEE,gDACX,OAAO,6CACP,SAAS,yCAAoC;;;;cAuDlC,yCACX,OAAO,sCACP,SAAS,yCACR,QAAQ;cA6CE,yCACX,OAAO,sCACP,SAAS,yCACR,QAAQ;UA6DM;EACf;EACA;;UAGe;EACf,SAAS;EACT;EACA;;UAGe;EACf,UAAU;EACV,SAAS;EACT;;UAGe;EACf,OACI,KAAK,4BACE,KAAK,qBAAqB,QAAQ,KAAK;EAClD,UACE,OAAO,wCACN,wCAAwC,QAAQ;EACnD,mBAAmB,SAAS,0BAA0B;EACtD,WAAW,SAAS,oBAAoB;EACxC;EACA;EACA,eAAe;IACb;IACA,UAAU;IACV;IACA;;EAEF,kBAAkB;EAClB,cACE,OAAO,0CAEL,mDAGA,QAAQ;;cAmCD,qCAAoC,OAAA,WAAA,mBAAA,WAAA,uBAAA,eAAA,eAAA,iBAAA,gBAU9C,6CAWa,SAAS,YAAU,QAAQ"}
|
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
import { D as surveyAssistantTaskRequestSchema, S as surveyAssistantSuggestResponseSettingsResultSchema, jt as responseExportSettingsSchema } from "./protocol-CF4Bh_Ch.mjs";
|
|
2
|
+
import { n as SURVEY_ASSISTANT_TASK_AGENT_ID } from "./constants-Ct-vv9cu.mjs";
|
|
3
|
+
import { d as readSurveyAssistantRequestJson, u as SurveyAssistantRequestError } from "./request-guardrails-C0ugBOp0.mjs";
|
|
4
|
+
import { a as surveyAuthoringReferences } from "./authoring-references-DbuOKerg.mjs";
|
|
5
|
+
import { n as FAIR_ITEM_KEY_RULES_TEXT } from "./fair-guidance-BmO4PswW.mjs";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { isValidResponseVariableName } from "@case-framework/survey-core";
|
|
8
|
+
import { RequestContext } from "@mastra/core/request-context";
|
|
9
|
+
import { Agent } from "@mastra/core/agent";
|
|
10
|
+
//#region src/server/response-settings-output.ts
|
|
11
|
+
const optionalEntries = z.array(z.object({
|
|
12
|
+
id: z.string().min(1),
|
|
13
|
+
value: z.string()
|
|
14
|
+
}).strict()).nullable().optional();
|
|
15
|
+
const exportSettings = responseExportSettingsSchema.omit({
|
|
16
|
+
optionCodes: true,
|
|
17
|
+
optionLabels: true
|
|
18
|
+
}).extend({
|
|
19
|
+
optionCodes: optionalEntries,
|
|
20
|
+
optionLabels: optionalEntries
|
|
21
|
+
});
|
|
22
|
+
const createResponseSettingsGenerationSchema = (compact = false) => {
|
|
23
|
+
const name = compact ? z.string().min(2).max(5).regex(/^[a-z][a-z0-9]{1,4}$/) : z.string().min(1);
|
|
24
|
+
const components = z.array(z.object({
|
|
25
|
+
id: z.string().min(1),
|
|
26
|
+
value: name
|
|
27
|
+
}).strict()).nullable().optional();
|
|
28
|
+
return z.object({
|
|
29
|
+
naming: z.array(z.union([z.object({
|
|
30
|
+
slotId: z.string().min(1),
|
|
31
|
+
variableName: name
|
|
32
|
+
}).strict(), z.object({
|
|
33
|
+
primitiveId: z.string().min(1),
|
|
34
|
+
prefix: name.nullable().optional(),
|
|
35
|
+
rows: components,
|
|
36
|
+
columns: components
|
|
37
|
+
}).strict()])).max(200),
|
|
38
|
+
settings: z.array(z.object({
|
|
39
|
+
slotId: z.string().min(1),
|
|
40
|
+
optionCodes: optionalEntries,
|
|
41
|
+
exportSettings: exportSettings.nullable().optional()
|
|
42
|
+
}).strict()).max(200)
|
|
43
|
+
}).strict();
|
|
44
|
+
};
|
|
45
|
+
createResponseSettingsGenerationSchema();
|
|
46
|
+
const entryMap = (values) => {
|
|
47
|
+
if (!values) return void 0;
|
|
48
|
+
if (new Set(values.map((entry) => entry.id)).size !== values.length) throw new Error("A coding suggestion repeats the same component or option ID.");
|
|
49
|
+
return Object.fromEntries(values.map(({ id, value }) => [id, value]));
|
|
50
|
+
};
|
|
51
|
+
const decodeResponseSettingsGeneration = (result) => surveyAssistantSuggestResponseSettingsResultSchema.parse({
|
|
52
|
+
naming: result.naming.map((edit) => "slotId" in edit ? edit : {
|
|
53
|
+
primitiveId: edit.primitiveId,
|
|
54
|
+
prefix: edit.prefix ?? void 0,
|
|
55
|
+
rows: entryMap(edit.rows),
|
|
56
|
+
columns: entryMap(edit.columns)
|
|
57
|
+
}),
|
|
58
|
+
settings: result.settings.map((edit) => ({
|
|
59
|
+
slotId: edit.slotId,
|
|
60
|
+
optionCodes: entryMap(edit.optionCodes),
|
|
61
|
+
exportSettings: edit.exportSettings ? {
|
|
62
|
+
...edit.exportSettings,
|
|
63
|
+
optionCodes: entryMap(edit.exportSettings.optionCodes),
|
|
64
|
+
optionLabels: entryMap(edit.exportSettings.optionLabels)
|
|
65
|
+
} : void 0
|
|
66
|
+
}))
|
|
67
|
+
});
|
|
68
|
+
//#endregion
|
|
69
|
+
//#region src/server/tasks.ts
|
|
70
|
+
const createSurveyAssistantTaskAgent = ({ model }) => new Agent({
|
|
71
|
+
id: SURVEY_ASSISTANT_TASK_AGENT_ID,
|
|
72
|
+
name: "Survey Assistant Tasks",
|
|
73
|
+
instructions: "Perform the supplied bounded survey-authoring task and return only its requested structured output.",
|
|
74
|
+
model
|
|
75
|
+
});
|
|
76
|
+
const BASE_TASK_INSTRUCTIONS = [
|
|
77
|
+
"You perform one bounded CASE Survey Editor AI task and return only the requested structured result.",
|
|
78
|
+
"The supplied task context was derived from the current canonical survey document snapshot. Use it as current survey context, but do not mutate it and do not claim that any editor change was applied.",
|
|
79
|
+
"Follow the supplied CASE authoring references where relevant. Do not invent item types, locales, ids, or capabilities that are absent from the task input and context.",
|
|
80
|
+
"Keep generated respondent-facing wording neutral, concise, and consistent with the requested locale and surrounding survey content."
|
|
81
|
+
];
|
|
82
|
+
const runSurveyAssistantTask = async ({ agent, taskId, input, context, schema, instructions, referenceTopics = [], requestContext, providerOptions, abortSignal }) => {
|
|
83
|
+
const references = referenceTopics.map((topic) => ({
|
|
84
|
+
topic,
|
|
85
|
+
title: surveyAuthoringReferences[topic].title,
|
|
86
|
+
content: surveyAuthoringReferences[topic].content
|
|
87
|
+
}));
|
|
88
|
+
const response = await agent.generate(JSON.stringify({
|
|
89
|
+
taskId,
|
|
90
|
+
input,
|
|
91
|
+
currentSurveyContext: context
|
|
92
|
+
}), {
|
|
93
|
+
instructions: [
|
|
94
|
+
...BASE_TASK_INSTRUCTIONS,
|
|
95
|
+
...instructions,
|
|
96
|
+
...references.map((entry) => `Reference: ${entry.title}\n${entry.content}`)
|
|
97
|
+
],
|
|
98
|
+
activeTools: [],
|
|
99
|
+
inputProcessors: [],
|
|
100
|
+
maxSteps: 1,
|
|
101
|
+
structuredOutput: {
|
|
102
|
+
schema,
|
|
103
|
+
errorStrategy: "strict"
|
|
104
|
+
},
|
|
105
|
+
...requestContext ? { requestContext } : {},
|
|
106
|
+
...providerOptions ? { providerOptions } : {},
|
|
107
|
+
...abortSignal ? { abortSignal } : {}
|
|
108
|
+
});
|
|
109
|
+
return schema.parse(response.object);
|
|
110
|
+
};
|
|
111
|
+
const keySuggestionValueSchema = z.object({
|
|
112
|
+
text: z.string().min(1),
|
|
113
|
+
rationale: z.string().optional()
|
|
114
|
+
});
|
|
115
|
+
const keySuggestionCategorySchema = z.enum([
|
|
116
|
+
"survey-pattern",
|
|
117
|
+
"compact",
|
|
118
|
+
"preferred",
|
|
119
|
+
"fair"
|
|
120
|
+
]);
|
|
121
|
+
const keySuggestionSchema = z.object({
|
|
122
|
+
surveyPattern: keySuggestionValueSchema.describe("A key that follows the strongest naming and numbering pattern in the current survey."),
|
|
123
|
+
compact: keySuggestionValueSchema.extend({ text: z.string().min(2).max(5).regex(/^[a-z][a-z0-9]{1,4}$/).describe("A recognizable 2-5 character lowercase abbreviation.") }),
|
|
124
|
+
preferred: keySuggestionValueSchema.describe("The key the model considers clearest without a category-specific naming constraint."),
|
|
125
|
+
fair: keySuggestionValueSchema.extend({ text: z.string().min(1).max(32).regex(/^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$/).describe("A FAIR-by-design lowercase snake_case variable name.") }),
|
|
126
|
+
alternatives: z.array(keySuggestionValueSchema.extend({ category: keySuggestionCategorySchema })).max(2)
|
|
127
|
+
});
|
|
128
|
+
const labelSuggestionSchema = z.object({ suggestions: z.array(z.object({ text: z.string().min(1) })) });
|
|
129
|
+
const clampLimit = (value, fallback, maximum) => Math.min(Math.max(1, value ?? fallback), maximum);
|
|
130
|
+
const sanitizeCodingKey = (value) => value.trim().replace(/[.\s-]+/g, "_").replace(/[^A-Za-z0-9_]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
|
|
131
|
+
const getCodingKeyIdentity = (value) => sanitizeCodingKey(value).toLocaleLowerCase();
|
|
132
|
+
const getNumberedKeyParts = (value) => {
|
|
133
|
+
const match = /^(.+?)_(\d+)$/.exec(getCodingKeyIdentity(value));
|
|
134
|
+
return match ? {
|
|
135
|
+
stem: match[1],
|
|
136
|
+
number: Number(match[2])
|
|
137
|
+
} : void 0;
|
|
138
|
+
};
|
|
139
|
+
const isUnsupportedFocusedItemSequence = (candidate, input, context) => {
|
|
140
|
+
const candidateParts = getNumberedKeyParts(candidate);
|
|
141
|
+
const focusItem = context.focus?.item;
|
|
142
|
+
if (!candidateParts || !focusItem) return false;
|
|
143
|
+
if (![input.currentName, input.draftName].map(getNumberedKeyParts).find((parts) => parts?.stem === candidateParts.stem && parts.number < candidateParts.number)) return false;
|
|
144
|
+
return !context.naming?.keyExamples.some((example) => {
|
|
145
|
+
const exampleParts = getNumberedKeyParts(example.key);
|
|
146
|
+
return example.itemType === focusItem.itemType && exampleParts?.stem === candidateParts.stem;
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
const invocationInstruction = (mode) => mode === "background" ? "This is a background invocation. Be efficient, but do not relax task conventions, completeness, or output quality." : "This is an explicit user invocation. Prioritize quality and useful variation.";
|
|
150
|
+
const runSurveyAssistantSuggestResponseNameTask = async (input, options) => {
|
|
151
|
+
const slot = options.context.responseVariables?.find((slot) => slot.slotId === input.slotId && slot.itemId === input.itemId);
|
|
152
|
+
if (!slot) throw new Error("The selected response variable is absent from task context.");
|
|
153
|
+
if (slot.naming?.kind === "matrix") throw new Error("Matrix names are derived; request response-settings suggestions for the matrix family.");
|
|
154
|
+
const maxSuggestions = Math.min(Math.max(4, input.maxSuggestions ?? 6), 6);
|
|
155
|
+
const result = await runSurveyAssistantTask({
|
|
156
|
+
...options,
|
|
157
|
+
taskId: "suggest-response-name",
|
|
158
|
+
input,
|
|
159
|
+
schema: keySuggestionSchema,
|
|
160
|
+
instructions: [
|
|
161
|
+
"Suggest names for the exact response variable identified by input.slotId. Item editor names are separate from response variable names.",
|
|
162
|
+
"Use currentSurveyContext.responseVariables and responseOwners for the slot's field label, help, units, value type, raw/computed status, matrix row/column meaning, option codes, and supplied instrument metadata. Do not name every field after its composite item's dominant topic.",
|
|
163
|
+
"Infer conventions only from other variables in naming.keyExamples and existingVariableNames. The focused variable's current or draft placeholder is not evidence of a pattern.",
|
|
164
|
+
"Return surveyPattern, compact, preferred and fair alternatives. All must use valid lowercase snake_case, start with a letter and have at most 32 characters. Compact means a recognizable 2-5 character lowercase abbreviation.",
|
|
165
|
+
`FAIR direction: ${FAIR_ITEM_KEY_RULES_TEXT}. These are project conventions supporting FAIR, not syntax mandated by FAIR. If no meaningful survey convention exists, prefer this direction. Preserve supplied instrument codes without inventing standards.`,
|
|
166
|
+
"Matrix variables are derived. Do not propose per-cell names; use suggest-response-settings for matrix coding components.",
|
|
167
|
+
`You may return up to ${Math.max(0, maxSuggestions - 4)} genuinely useful alternatives when another category has more than one strong candidate. Tag each alternative with its category.`,
|
|
168
|
+
"Never repeat the same suggestion, including case-only or punctuation-only variations.",
|
|
169
|
+
"Do not return the current name, an existing variable name, or a semantically unrelated abbreviation.",
|
|
170
|
+
`Return between 4 and ${maxSuggestions} distinct suggestions in total.`,
|
|
171
|
+
invocationInstruction(options.mode)
|
|
172
|
+
]
|
|
173
|
+
});
|
|
174
|
+
const excluded = new Set([
|
|
175
|
+
input.currentName,
|
|
176
|
+
...input.existingNames,
|
|
177
|
+
...options.context.existingVariableNames ?? []
|
|
178
|
+
].map(getCodingKeyIdentity).filter(Boolean));
|
|
179
|
+
const seen = /* @__PURE__ */ new Set();
|
|
180
|
+
return { suggestions: [
|
|
181
|
+
{
|
|
182
|
+
...result.surveyPattern,
|
|
183
|
+
category: "survey-pattern"
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
...result.compact,
|
|
187
|
+
category: "compact"
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
...result.preferred,
|
|
191
|
+
category: "preferred"
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
...result.fair,
|
|
195
|
+
category: "fair"
|
|
196
|
+
},
|
|
197
|
+
...result.alternatives
|
|
198
|
+
].flatMap((suggestion) => {
|
|
199
|
+
const text = suggestion.text.trim();
|
|
200
|
+
const identity = getCodingKeyIdentity(text);
|
|
201
|
+
if (!isValidResponseVariableName(text) || excluded.has(identity) || seen.has(identity) || isUnsupportedFocusedItemSequence(text, input, options.context)) return [];
|
|
202
|
+
seen.add(identity);
|
|
203
|
+
return [{
|
|
204
|
+
...suggestion,
|
|
205
|
+
text
|
|
206
|
+
}];
|
|
207
|
+
}).slice(0, maxSuggestions) };
|
|
208
|
+
};
|
|
209
|
+
const runSurveyAssistantSuggestResponseSettingsTask = async (input, options) => {
|
|
210
|
+
const slots = options.context.responseVariables ?? [];
|
|
211
|
+
const selected = slots.filter((slot) => input.slotIds.includes(String(slot.slotId)));
|
|
212
|
+
if (input.slotIds.some((id) => !selected.some((slot) => slot.slotId === id))) throw new Error("The selected response variables are absent from task context.");
|
|
213
|
+
const matrixIds = new Set(selected.filter((slot) => slot.naming?.kind === "matrix").map((slot) => slot.primitiveId));
|
|
214
|
+
const allowed = new Set(slots.filter((slot) => input.slotIds.includes(String(slot.slotId)) || matrixIds.has(slot.primitiveId)).map((slot) => slot.slotId));
|
|
215
|
+
const generated = await runSurveyAssistantTask({
|
|
216
|
+
...options,
|
|
217
|
+
taskId: "suggest-response-settings",
|
|
218
|
+
input,
|
|
219
|
+
schema: createResponseSettingsGenerationSchema(input.category === "compact"),
|
|
220
|
+
instructions: [
|
|
221
|
+
"Suggest coherent response coding for the selected persistent slot IDs. Return naming and settings edits only; nothing has been applied.",
|
|
222
|
+
"In this structured result, rows, columns, optionCodes and optionLabels are arrays of {id,value} entries, not dictionaries. Use exact existing row/column/option IDs. Omit unchanged fields (or use null); use empty arrays for no edits. The authoring references show the canonical operation format; the application converts these entry arrays to that format.",
|
|
223
|
+
`The requested style is ${input.category}. For compact, use recognizable 2-5 character lowercase abbreviations for scalar names and each matrix prefix/row/column component; derived full matrix names may be longer. For survey-pattern, follow other authored variable names; fall back to FAIR only when no pattern exists. For preferred, prioritize clear useful names. FAIR direction: ${FAIR_ITEM_KEY_RULES_TEXT}. All categories obey the shared syntax and 32-character full-name limit.`,
|
|
224
|
+
"Use the exact slot-specific field/row/column content, value type, units, option labels, and supplied instrument metadata. Never infer a value's meaning from the composite item's dominant topic alone. Existing names from other variables provide naming-pattern evidence; the selected variable's placeholder does not.",
|
|
225
|
+
"For scalars use {slotId,variableName}. For matrices use {primitiveId,prefix,rows,columns} with the exact row and column IDs. Matrix cells and computed names are derived; never rename an individual matrix slot. Preview all affected family names mentally against existingVariableNames, including names of unselected variables. A selected matrix member permits component edits affecting that entire matrix family.",
|
|
226
|
+
"Option IDs, export codes, labels, and numeric scores are separate. Suggest stable meaningful optionCodes indexed by existing option IDs, preserve supplied instrument/standard codes and scores, and never invent IDs or standards. Shared matrix/dropdown option codes affect all slots that reuse those options.",
|
|
227
|
+
"Only change selected scalars or selected matrix families. Use settings for optionCodes and explicit export settings when needed, otherwise leave settings empty. Do not invent missing-value sentinels. Preserve false, zero, empty selection and missing as distinct values.",
|
|
228
|
+
"Avoid changing already meaningful codes without a clear reason. The editor validates survey-wide uniqueness and projected headers and presents a before/after preview before applying one undoable change."
|
|
229
|
+
],
|
|
230
|
+
referenceTopics: ["fair-by-design", "response-variables"]
|
|
231
|
+
});
|
|
232
|
+
const result = decodeResponseSettingsGeneration(generated);
|
|
233
|
+
if (result.naming.some((edit) => "slotId" in edit ? !allowed.has(edit.slotId) || (slots.find((slot) => slot.slotId === edit.slotId)?.naming)?.kind === "matrix" : !matrixIds.has(edit.primitiveId)) || result.settings.some((edit) => !allowed.has(edit.slotId))) throw new Error("The suggestion targets variables outside the requested scope or directly names a derived matrix cell.");
|
|
234
|
+
return result;
|
|
235
|
+
};
|
|
236
|
+
const runSurveyAssistantSuggestItemLabelTask = async (input, options) => {
|
|
237
|
+
const maxSuggestions = clampLimit(input.maxSuggestions, 3, 10);
|
|
238
|
+
const locale = input.locale ?? options.context.survey.selectedLocale;
|
|
239
|
+
const result = await runSurveyAssistantTask({
|
|
240
|
+
...options,
|
|
241
|
+
taskId: "suggest-item-label",
|
|
242
|
+
input,
|
|
243
|
+
schema: labelSuggestionSchema,
|
|
244
|
+
instructions: [
|
|
245
|
+
"Suggest concise editor-only item labels, not coding keys and not respondent-facing question text.",
|
|
246
|
+
"Infer the item's purpose from its type, draft label, visible content, hierarchy, siblings, and descendants.",
|
|
247
|
+
"Use currentSurveyContext.naming.labelExamples to infer established capitalization, prefixes, abbreviations, delimiters, and typical length.",
|
|
248
|
+
"If existing label examples are available, the first suggestion must follow their dominant pattern while still describing the focused item accurately. Other suggestions may offer useful concise alternatives.",
|
|
249
|
+
"Do not copy the subject matter of an unrelated example merely to imitate its style, and do not return an existing unrelated label verbatim.",
|
|
250
|
+
`Write labels in locale ${locale}.`,
|
|
251
|
+
`Return at most ${maxSuggestions} distinct, specific, human-readable suggestions.`,
|
|
252
|
+
invocationInstruction(options.mode)
|
|
253
|
+
],
|
|
254
|
+
referenceTopics: ["core-rules"]
|
|
255
|
+
});
|
|
256
|
+
const normalizeLabel = (value) => value.trim().toLocaleLowerCase();
|
|
257
|
+
const excludedLabels = /* @__PURE__ */ new Set([normalizeLabel(input.draftLabel), ...(options.context.naming?.labelExamples ?? []).map((example) => normalizeLabel(example.label))]);
|
|
258
|
+
const seen = /* @__PURE__ */ new Set();
|
|
259
|
+
return { suggestions: result.suggestions.flatMap((suggestion) => {
|
|
260
|
+
const text = suggestion.text.trim();
|
|
261
|
+
const normalizedText = normalizeLabel(text);
|
|
262
|
+
if (!text || excludedLabels.has(normalizedText) || seen.has(normalizedText)) return [];
|
|
263
|
+
seen.add(normalizedText);
|
|
264
|
+
return [{
|
|
265
|
+
...suggestion,
|
|
266
|
+
text
|
|
267
|
+
}];
|
|
268
|
+
}).slice(0, maxSuggestions) };
|
|
269
|
+
};
|
|
270
|
+
const getProtectedTokens = (value) => value.match(/\{\{[^{}]+\}\}|\{[^{}]+\}|\$\{[^{}]+\}|%\d*\$?[a-z]/gi) ?? [];
|
|
271
|
+
const runSurveyAssistantTranslateContentTask = async (input, options) => {
|
|
272
|
+
const result = await runSurveyAssistantTask({
|
|
273
|
+
...options,
|
|
274
|
+
taskId: "translate-content",
|
|
275
|
+
input,
|
|
276
|
+
schema: z.object({ translations: z.object(Object.fromEntries(input.targetLocales.map((locale) => [locale, z.object(Object.fromEntries(input.entries.map((entry) => [entry.id, z.string()]))).strict()]))).strict() }).strict(),
|
|
277
|
+
instructions: [
|
|
278
|
+
"Translate every supplied survey-content entry faithfully without adding explanations or omitting content.",
|
|
279
|
+
`Translate from ${input.sourceLocale} into exactly these target locales: ${input.targetLocales.join(", ")}.`,
|
|
280
|
+
"Use currentSurveyContext.translation.examples as a terminology and style reference when relevant. The supplied source entries remain authoritative.",
|
|
281
|
+
"Preserve meaning, register, survey terminology, punctuation, line breaks, response semantics, placeholders, interpolation tokens, URLs, and identifiers that should not be translated.",
|
|
282
|
+
"Keep repeated terminology consistent across all entries and target locales. Do not use a neighboring translation when its meaning differs.",
|
|
283
|
+
"Return plain strings keyed first by target locale and then by the original entry id. Return every requested locale and entry exactly once.",
|
|
284
|
+
invocationInstruction(options.mode)
|
|
285
|
+
],
|
|
286
|
+
referenceTopics: ["localization"]
|
|
287
|
+
});
|
|
288
|
+
const translations = {};
|
|
289
|
+
for (const locale of input.targetLocales) {
|
|
290
|
+
const entries = result.translations[locale];
|
|
291
|
+
if (!entries) throw new Error(`Translation result is missing locale ${locale}.`);
|
|
292
|
+
const translatedEntries = {};
|
|
293
|
+
for (const entry of input.entries) {
|
|
294
|
+
const translation = entries[entry.id];
|
|
295
|
+
if (typeof translation !== "string" || translation.trim().length === 0) throw new Error(`Translation result is missing entry ${entry.id} for locale ${locale}.`);
|
|
296
|
+
const sourceTokens = getProtectedTokens(entry.text).sort();
|
|
297
|
+
const translatedTokens = getProtectedTokens(translation).sort();
|
|
298
|
+
if (sourceTokens.join("\0") !== translatedTokens.join("\0")) throw new Error(`Translation result changed protected tokens in entry ${entry.id} for locale ${locale}.`);
|
|
299
|
+
translatedEntries[entry.id] = translation;
|
|
300
|
+
}
|
|
301
|
+
translations[locale] = translatedEntries;
|
|
302
|
+
}
|
|
303
|
+
return { translations };
|
|
304
|
+
};
|
|
305
|
+
const MAX_TASK_REQUEST_BYTES = 524288;
|
|
306
|
+
const DEFAULT_MAX_TASK_CONCURRENT_REQUESTS = 4;
|
|
307
|
+
const DEFAULT_MAX_TASK_DURATION_MS = 6e4;
|
|
308
|
+
const taskErrorResponse = (error) => {
|
|
309
|
+
const status = typeof error === "object" && error !== null && "status" in error && typeof error.status === "number" ? error.status : error instanceof SyntaxError || error instanceof Error && error.name === "ZodError" ? 400 : 500;
|
|
310
|
+
const message = error instanceof SurveyAssistantRequestError ? error.message : status === 401 || status === 403 || status === 404 ? status === 404 ? "Survey assistant task not found." : "The survey assistant task is not authorized." : status >= 500 ? "The survey assistant task could not be completed." : "The survey assistant task request is invalid.";
|
|
311
|
+
return Response.json({
|
|
312
|
+
code: status === 401 || status === 403 || status === 404 ? "authorization_failed" : "request_failed",
|
|
313
|
+
error: message
|
|
314
|
+
}, { status });
|
|
315
|
+
};
|
|
316
|
+
const createSurveyAssistantTaskHandler = ({ agent, authorize, getReadinessError, isEnabled, maxConcurrentRequests = DEFAULT_MAX_TASK_CONCURRENT_REQUESTS, maxDurationMs = DEFAULT_MAX_TASK_DURATION_MS, onTaskMetrics, providerOptions, resolveModel }) => {
|
|
317
|
+
const concurrency = Number.isInteger(maxConcurrentRequests) && maxConcurrentRequests > 0 ? maxConcurrentRequests : DEFAULT_MAX_TASK_CONCURRENT_REQUESTS;
|
|
318
|
+
const duration = Number.isFinite(maxDurationMs) && maxDurationMs > 0 ? maxDurationMs : DEFAULT_MAX_TASK_DURATION_MS;
|
|
319
|
+
let activeRequests = 0;
|
|
320
|
+
return async (request) => {
|
|
321
|
+
if (activeRequests >= concurrency) return Response.json({
|
|
322
|
+
code: "rate_limited",
|
|
323
|
+
error: "The survey assistant is handling too many tasks. Try again shortly."
|
|
324
|
+
}, { status: 429 });
|
|
325
|
+
activeRequests += 1;
|
|
326
|
+
const startedAt = Date.now();
|
|
327
|
+
let metricsIdentity;
|
|
328
|
+
let metricsTaskId = "unknown";
|
|
329
|
+
let completed = false;
|
|
330
|
+
try {
|
|
331
|
+
if (isEnabled && !await isEnabled(request)) return Response.json({
|
|
332
|
+
code: "request_failed",
|
|
333
|
+
error: "Survey assistant is disabled."
|
|
334
|
+
}, { status: 404 });
|
|
335
|
+
const task = surveyAssistantTaskRequestSchema.parse(await readSurveyAssistantRequestJson(request, MAX_TASK_REQUEST_BYTES));
|
|
336
|
+
metricsTaskId = task.taskId;
|
|
337
|
+
const identity = await authorize({
|
|
338
|
+
request,
|
|
339
|
+
requestedResourceId: task.resourceId,
|
|
340
|
+
requestedSurveyId: task.surveyId
|
|
341
|
+
});
|
|
342
|
+
if (!identity?.resourceId?.trim() || identity.resourceId.trim().length > 240 || identity.surveyId !== void 0 && (!identity.surveyId.trim() || identity.surveyId.trim().length > 240)) throw Object.assign(/* @__PURE__ */ new Error("The host returned an invalid task identity."), { status: 500 });
|
|
343
|
+
metricsIdentity = {
|
|
344
|
+
resourceId: identity.resourceId.trim(),
|
|
345
|
+
...identity.surveyId?.trim() ? { surveyId: identity.surveyId.trim() } : {}
|
|
346
|
+
};
|
|
347
|
+
const readinessError = await getReadinessError?.(request);
|
|
348
|
+
if (readinessError) return Response.json({
|
|
349
|
+
code: "request_failed",
|
|
350
|
+
error: readinessError
|
|
351
|
+
}, { status: 503 });
|
|
352
|
+
const requestContext = new RequestContext();
|
|
353
|
+
let modelSelection;
|
|
354
|
+
if (resolveModel) {
|
|
355
|
+
modelSelection = await resolveModel({
|
|
356
|
+
identity: metricsIdentity,
|
|
357
|
+
request,
|
|
358
|
+
requestedModelId: task.modelId
|
|
359
|
+
}) ?? void 0;
|
|
360
|
+
if (!modelSelection) return Response.json({
|
|
361
|
+
code: "request_failed",
|
|
362
|
+
error: "The selected survey assistant model is unavailable."
|
|
363
|
+
}, { status: 400 });
|
|
364
|
+
requestContext.set("modelSelection", modelSelection);
|
|
365
|
+
}
|
|
366
|
+
const resolvedAgent = typeof agent === "function" ? await agent() : agent;
|
|
367
|
+
const runtime = {
|
|
368
|
+
abortSignal: AbortSignal.any([request.signal, AbortSignal.timeout(duration)]),
|
|
369
|
+
agent: resolvedAgent,
|
|
370
|
+
context: task.context,
|
|
371
|
+
mode: task.mode,
|
|
372
|
+
requestContext,
|
|
373
|
+
providerOptions: modelSelection?.providerOptions ?? providerOptions
|
|
374
|
+
};
|
|
375
|
+
const result = task.taskId === "suggest-response-settings" ? await runSurveyAssistantSuggestResponseSettingsTask(task.input, runtime) : task.taskId === "suggest-response-name" ? await runSurveyAssistantSuggestResponseNameTask(task.input, runtime) : task.taskId === "suggest-item-label" ? await runSurveyAssistantSuggestItemLabelTask(task.input, runtime) : await runSurveyAssistantTranslateContentTask(task.input, runtime);
|
|
376
|
+
completed = true;
|
|
377
|
+
return Response.json(result);
|
|
378
|
+
} catch (error) {
|
|
379
|
+
return taskErrorResponse(error);
|
|
380
|
+
} finally {
|
|
381
|
+
activeRequests = Math.max(0, activeRequests - 1);
|
|
382
|
+
if (metricsIdentity) try {
|
|
383
|
+
onTaskMetrics?.({
|
|
384
|
+
durationMs: Date.now() - startedAt,
|
|
385
|
+
identity: metricsIdentity,
|
|
386
|
+
status: completed ? "completed" : "failed",
|
|
387
|
+
taskId: metricsTaskId
|
|
388
|
+
});
|
|
389
|
+
} catch {}
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
};
|
|
393
|
+
//#endregion
|
|
394
|
+
export { runSurveyAssistantSuggestResponseSettingsTask as a, runSurveyAssistantSuggestResponseNameTask as i, createSurveyAssistantTaskHandler as n, runSurveyAssistantTask as o, runSurveyAssistantSuggestItemLabelTask as r, runSurveyAssistantTranslateContentTask as s, createSurveyAssistantTaskAgent as t };
|
|
395
|
+
|
|
396
|
+
//# sourceMappingURL=tasks-BZEiwgxA.mjs.map
|