quidproquo-actionprocessor-awslambda 0.1.1 → 0.1.2
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/lib/commonjs/getActionProcessor/core/ai/getAiPromptActionProcessor.js +11 -4
- package/lib/commonjs/getActionProcessor/core/ai/getAiPromptStreamActionProcessor.js +11 -4
- package/lib/commonjs/getActionProcessor/core/ai/logic/aiStreamPartMappers.js +8 -8
- package/lib/commonjs/getActionProcessor/core/ai/logic/createDriveFileResolver.d.ts +3 -0
- package/lib/commonjs/getActionProcessor/core/ai/logic/createDriveFileResolver.js +27 -0
- package/lib/commonjs/getActionProcessor/core/ai/logic/index.d.ts +1 -0
- package/lib/commonjs/getActionProcessor/core/ai/logic/index.js +1 -0
- package/lib/commonjs/getActionProcessor/core/ai/logic/toSdkMessages.d.ts +3 -2
- package/lib/commonjs/getActionProcessor/core/ai/logic/toSdkMessages.js +41 -24
- package/lib/commonjs/getActionProcessor/core/eventBus/getEventBusSendMessagesActionProcessor.js +1 -1
- package/lib/commonjs/getActionProcessor/core/file/getFileStreamOpenActionProcessor.js +1 -3
- package/lib/commonjs/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertActionProcessor.js +2 -0
- package/lib/commonjs/lambdaHandlers/helpers/logger.js +1 -3
- package/lib/commonjs/logic/cognito/utils/transformCognitoResponse.js +1 -1
- package/lib/commonjs/logic/dynamo/putItem.d.ts +1 -0
- package/lib/commonjs/logic/dynamo/putItem.js +6 -4
- package/lib/commonjs/logic/dynamo/qpqDynamoOrm/buildDynamoUpdate.js +1 -5
- package/lib/commonjs/logic/s3/generatePresignedUploadUrl.js +4 -4
- package/lib/esm/getActionProcessor/core/ai/getAiPromptActionProcessor.js +11 -4
- package/lib/esm/getActionProcessor/core/ai/getAiPromptStreamActionProcessor.js +11 -4
- package/lib/esm/getActionProcessor/core/ai/logic/aiStreamPartMappers.js +8 -8
- package/lib/esm/getActionProcessor/core/ai/logic/createDriveFileResolver.d.ts +3 -0
- package/lib/esm/getActionProcessor/core/ai/logic/createDriveFileResolver.js +14 -0
- package/lib/esm/getActionProcessor/core/ai/logic/index.d.ts +1 -0
- package/lib/esm/getActionProcessor/core/ai/logic/index.js +1 -0
- package/lib/esm/getActionProcessor/core/ai/logic/toSdkMessages.d.ts +3 -2
- package/lib/esm/getActionProcessor/core/ai/logic/toSdkMessages.js +27 -20
- package/lib/esm/getActionProcessor/core/apiKeyValidation/getApiKeyValidationValidateActionProcessor.js +1 -1
- package/lib/esm/getActionProcessor/core/eventBus/getEventBusSendMessagesActionProcessor.js +1 -1
- package/lib/esm/getActionProcessor/core/file/getFileDeleteActionProcessor.js +1 -1
- package/lib/esm/getActionProcessor/core/file/getFileGenerateTemporarySecureUrlActionProcessor.js +1 -1
- package/lib/esm/getActionProcessor/core/file/getFileGenerateTemporaryUploadSecureUrlActionProcessor.js +1 -1
- package/lib/esm/getActionProcessor/core/file/getFileStreamOpenActionProcessor.js +1 -3
- package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreDeleteActionProcessor.js +1 -1
- package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreGetActionProcessor.js +1 -1
- package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreGetAllActionProcessor.js +1 -1
- package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreQueryActionProcessor.js +1 -1
- package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreScanActionProcessor.js +1 -1
- package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreUpdateActionProcessor.js +1 -1
- package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertActionProcessor.js +2 -0
- package/lib/esm/getActionProcessor/core/queue/getQueueSendMessageActionProcessor.js +1 -1
- package/lib/esm/lambdaHandlers/helpers/logger.js +1 -3
- package/lib/esm/logic/cognito/utils/transformCognitoResponse.js +2 -2
- package/lib/esm/logic/dynamo/putItem.d.ts +1 -0
- package/lib/esm/logic/dynamo/putItem.js +6 -0
- package/lib/esm/logic/dynamo/qpqDynamoOrm/buildDynamoUpdate.js +1 -5
- package/lib/esm/logic/s3/generatePresignedUploadUrl.js +5 -5
- package/package.json +7 -7
|
@@ -15,15 +15,22 @@ const ai_1 = require("ai");
|
|
|
15
15
|
const logic_1 = require("./logic");
|
|
16
16
|
const getProcessAiPrompt = (qpqConfig) => {
|
|
17
17
|
return (payload, session, actionProcessorList, logger, updateSession, dynamicModuleLoader, streamRegistry) => __awaiter(void 0, void 0, void 0, function* () {
|
|
18
|
+
var _a;
|
|
18
19
|
const prepared = (0, logic_1.prepareAiPromptCall)(qpqConfig, payload, session, actionProcessorList, logger, dynamicModuleLoader, streamRegistry);
|
|
19
20
|
if ('error' in prepared) {
|
|
20
21
|
return (0, quidproquo_core_1.actionResultError)(prepared.error.type, prepared.error.message);
|
|
21
22
|
}
|
|
22
|
-
const promptOrMessages = payload.messages
|
|
23
|
-
? { messages: (0, logic_1.toSdkMessages)(payload.messages) }
|
|
24
|
-
: { prompt: payload.prompt };
|
|
25
23
|
try {
|
|
26
|
-
const
|
|
24
|
+
const promptOrMessages = payload.messages
|
|
25
|
+
? {
|
|
26
|
+
messages: yield (0, logic_1.toSdkMessages)(payload.messages, (0, logic_1.createDriveFileResolver)(qpqConfig, session, actionProcessorList, logger, dynamicModuleLoader, streamRegistry)),
|
|
27
|
+
}
|
|
28
|
+
: { prompt: payload.prompt };
|
|
29
|
+
// Extended thinking — the model reasons before answering, within the token budget.
|
|
30
|
+
const providerOptions = payload.reasoning
|
|
31
|
+
? { bedrock: { reasoningConfig: { type: 'enabled', budgetTokens: (_a = payload.reasoning.budgetTokens) !== null && _a !== void 0 ? _a : 4096 } } }
|
|
32
|
+
: undefined;
|
|
33
|
+
const result = yield (0, ai_1.generateText)(Object.assign(Object.assign({ model: prepared.model, system: payload.system }, promptOrMessages), { tools: prepared.tools, providerOptions, stopWhen: (0, ai_1.stepCountIs)(10) }));
|
|
27
34
|
return (0, quidproquo_core_1.actionResult)({ text: result.text });
|
|
28
35
|
}
|
|
29
36
|
catch (error) {
|
|
@@ -36,15 +36,22 @@ const awsLambdaUtils_1 = require("../../../awsLambdaUtils");
|
|
|
36
36
|
const logic_1 = require("./logic");
|
|
37
37
|
const getProcessAiPromptStream = (qpqConfig) => {
|
|
38
38
|
return (payload, session, actionProcessorList, logger, updateSession, dynamicModuleLoader, streamRegistry) => __awaiter(void 0, void 0, void 0, function* () {
|
|
39
|
+
var _a;
|
|
39
40
|
const prepared = (0, logic_1.prepareAiPromptCall)(qpqConfig, payload, session, actionProcessorList, logger, dynamicModuleLoader, streamRegistry);
|
|
40
41
|
if ('error' in prepared) {
|
|
41
42
|
return (0, quidproquo_core_1.actionResultError)(prepared.error.type, prepared.error.message);
|
|
42
43
|
}
|
|
43
|
-
const promptOrMessages = payload.messages
|
|
44
|
-
? { messages: (0, logic_1.toSdkMessages)(payload.messages) }
|
|
45
|
-
: { prompt: payload.prompt };
|
|
46
44
|
try {
|
|
47
|
-
const
|
|
45
|
+
const promptOrMessages = payload.messages
|
|
46
|
+
? {
|
|
47
|
+
messages: yield (0, logic_1.toSdkMessages)(payload.messages, (0, logic_1.createDriveFileResolver)(qpqConfig, session, actionProcessorList, logger, dynamicModuleLoader, streamRegistry)),
|
|
48
|
+
}
|
|
49
|
+
: { prompt: payload.prompt };
|
|
50
|
+
// Extended thinking — thinking progress streams out as Reasoning* parts.
|
|
51
|
+
const providerOptions = payload.reasoning
|
|
52
|
+
? { bedrock: { reasoningConfig: { type: 'enabled', budgetTokens: (_a = payload.reasoning.budgetTokens) !== null && _a !== void 0 ? _a : 4096 } } }
|
|
53
|
+
: undefined;
|
|
54
|
+
const { fullStream } = (0, ai_1.streamText)(Object.assign(Object.assign({ model: prepared.model, system: payload.system }, promptOrMessages), { tools: prepared.tools, providerOptions, stopWhen: (0, ai_1.stepCountIs)(10),
|
|
48
55
|
// streamText swallows errors by default to keep the server alive — surface them to CloudWatch.
|
|
49
56
|
onError: ({ error }) => {
|
|
50
57
|
// todo yeild this out
|
|
@@ -28,17 +28,17 @@ const mapAiStreamToolInputStart_1 = require("./mapAiStreamToolInputStart");
|
|
|
28
28
|
const mapAiStreamToolOutputDenied_1 = require("./mapAiStreamToolOutputDenied");
|
|
29
29
|
const mapAiStreamToolResult_1 = require("./mapAiStreamToolResult");
|
|
30
30
|
exports.aiStreamPartMappers = {
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
start: mapAiStreamStart_1.mapAiStreamStart,
|
|
32
|
+
finish: mapAiStreamFinish_1.mapAiStreamFinish,
|
|
33
33
|
'start-step': mapAiStreamStartStep_1.mapAiStreamStartStep,
|
|
34
34
|
'finish-step': mapAiStreamFinishStep_1.mapAiStreamFinishStep,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
abort: mapAiStreamAbort_1.mapAiStreamAbort,
|
|
36
|
+
error: mapAiStreamError_1.mapAiStreamError,
|
|
37
|
+
raw: mapAiStreamRaw_1.mapAiStreamRaw,
|
|
38
|
+
source: mapAiStreamSourcePart_1.mapAiStreamSourcePart,
|
|
39
|
+
file: mapAiStreamFilePart_1.mapAiStreamFilePart,
|
|
40
40
|
'reasoning-file': mapAiStreamReasoningFilePart_1.mapAiStreamReasoningFilePart,
|
|
41
|
-
|
|
41
|
+
custom: mapAiStreamCustom_1.mapAiStreamCustom,
|
|
42
42
|
'text-start': mapAiStreamTextStart_1.mapAiStreamTextStart,
|
|
43
43
|
'text-end': mapAiStreamTextEnd_1.mapAiStreamTextEnd,
|
|
44
44
|
'text-delta': mapAiStreamTextDelta_1.mapAiStreamTextDelta,
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { ActionProcessorList, DynamicModuleLoader, QPQConfig, QpqLogger, StorySession, StreamRegistry } from 'quidproquo-core';
|
|
2
|
+
import { AiDriveFileResolver } from './toSdkMessages';
|
|
3
|
+
export declare const createDriveFileResolver: (qpqConfig: QPQConfig, session: StorySession, actionProcessorList: ActionProcessorList, logger: QpqLogger, dynamicModuleLoader: DynamicModuleLoader, streamRegistry?: StreamRegistry) => AiDriveFileResolver;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.createDriveFileResolver = void 0;
|
|
13
|
+
const quidproquo_core_1 = require("quidproquo-core");
|
|
14
|
+
const awsLambdaUtils_1 = require("../../../../awsLambdaUtils");
|
|
15
|
+
// Reads drive-referenced files through the active file action processor, so the
|
|
16
|
+
// same message works against S3 in production and local disk under the dev server.
|
|
17
|
+
const createDriveFileResolver = (qpqConfig, session, actionProcessorList, logger, dynamicModuleLoader, streamRegistry) => {
|
|
18
|
+
return (drive, filepath) => __awaiter(void 0, void 0, void 0, function* () {
|
|
19
|
+
const resolveStory = (0, quidproquo_core_1.createImplementationRuntime)(qpqConfig, [`AI File: ${drive}/${filepath}`], () => new Date().toISOString(), awsLambdaUtils_1.randomGuid, session, actionProcessorList, logger, dynamicModuleLoader, streamRegistry);
|
|
20
|
+
const storyResult = yield resolveStory(quidproquo_core_1.askFileReadBinaryContents, [drive, filepath]);
|
|
21
|
+
if (storyResult.error) {
|
|
22
|
+
throw new Error(storyResult.error.errorText);
|
|
23
|
+
}
|
|
24
|
+
return storyResult.result;
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
exports.createDriveFileResolver = createDriveFileResolver;
|
|
@@ -15,6 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./aiStreamPartMappers"), exports);
|
|
18
|
+
__exportStar(require("./createDriveFileResolver"), exports);
|
|
18
19
|
__exportStar(require("./mapAiStreamAbort"), exports);
|
|
19
20
|
__exportStar(require("./mapAiStreamCustom"), exports);
|
|
20
21
|
__exportStar(require("./mapAiStreamError"), exports);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import { AiMessage } from 'quidproquo-core';
|
|
1
|
+
import { AiMessage, QPQBinaryData } from 'quidproquo-core';
|
|
2
2
|
import type { ModelMessage } from 'ai';
|
|
3
|
-
export
|
|
3
|
+
export type AiDriveFileResolver = (drive: string, filepath: string) => Promise<QPQBinaryData>;
|
|
4
|
+
export declare const toSdkMessages: (messages: AiMessage[], resolveDriveFile: AiDriveFileResolver) => Promise<ModelMessage[]>;
|
|
@@ -1,28 +1,45 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
2
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
12
|
exports.toSdkMessages = void 0;
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
13
|
+
const mapFilePart = (part, resolveDriveFile) => __awaiter(void 0, void 0, void 0, function* () {
|
|
14
|
+
var _a;
|
|
15
|
+
if ('url' in part) {
|
|
16
|
+
return {
|
|
17
|
+
type: 'file',
|
|
18
|
+
data: new URL(part.url),
|
|
19
|
+
mediaType: part.mediaType,
|
|
20
|
+
filename: part.filename,
|
|
21
|
+
};
|
|
7
22
|
}
|
|
23
|
+
const binary = yield resolveDriveFile(part.drive, part.filepath);
|
|
8
24
|
return {
|
|
9
25
|
type: 'file',
|
|
10
|
-
data:
|
|
26
|
+
data: binary.base64Data,
|
|
11
27
|
mediaType: part.mediaType,
|
|
12
|
-
filename: part.filename,
|
|
28
|
+
filename: (_a = part.filename) !== null && _a !== void 0 ? _a : binary.filename,
|
|
13
29
|
};
|
|
14
|
-
};
|
|
15
|
-
const
|
|
30
|
+
});
|
|
31
|
+
const mapUserPart = (part, resolveDriveFile) => __awaiter(void 0, void 0, void 0, function* () {
|
|
32
|
+
if (part.type === 'text') {
|
|
33
|
+
return { type: 'text', text: part.text };
|
|
34
|
+
}
|
|
35
|
+
return mapFilePart(part, resolveDriveFile);
|
|
36
|
+
});
|
|
37
|
+
const mapAssistantPart = (part, resolveDriveFile) => __awaiter(void 0, void 0, void 0, function* () {
|
|
16
38
|
switch (part.type) {
|
|
17
39
|
case 'text':
|
|
18
40
|
return { type: 'text', text: part.text };
|
|
19
41
|
case 'file':
|
|
20
|
-
return
|
|
21
|
-
type: 'file',
|
|
22
|
-
data: new URL(part.url),
|
|
23
|
-
mediaType: part.mediaType,
|
|
24
|
-
filename: part.filename,
|
|
25
|
-
};
|
|
42
|
+
return mapFilePart(part, resolveDriveFile);
|
|
26
43
|
case 'tool-call':
|
|
27
44
|
return {
|
|
28
45
|
type: 'tool-call',
|
|
@@ -37,7 +54,7 @@ const mapAssistantPart = (part) => {
|
|
|
37
54
|
providerOptions: part.providerOptions,
|
|
38
55
|
};
|
|
39
56
|
}
|
|
40
|
-
};
|
|
57
|
+
});
|
|
41
58
|
const mapToolResult = (part) => {
|
|
42
59
|
const output = part.isError
|
|
43
60
|
? typeof part.output === 'string'
|
|
@@ -53,30 +70,30 @@ const mapToolResult = (part) => {
|
|
|
53
70
|
output,
|
|
54
71
|
};
|
|
55
72
|
};
|
|
56
|
-
const mapUserMessage = (message) => {
|
|
73
|
+
const mapUserMessage = (message, resolveDriveFile) => __awaiter(void 0, void 0, void 0, function* () {
|
|
57
74
|
if (typeof message.content === 'string') {
|
|
58
75
|
return { role: 'user', content: message.content };
|
|
59
76
|
}
|
|
60
|
-
return { role: 'user', content: message.content.map(mapUserPart) };
|
|
61
|
-
};
|
|
62
|
-
const mapAssistantMessage = (message) => {
|
|
77
|
+
return { role: 'user', content: yield Promise.all(message.content.map((part) => mapUserPart(part, resolveDriveFile))) };
|
|
78
|
+
});
|
|
79
|
+
const mapAssistantMessage = (message, resolveDriveFile) => __awaiter(void 0, void 0, void 0, function* () {
|
|
63
80
|
if (typeof message.content === 'string') {
|
|
64
81
|
return { role: 'assistant', content: message.content };
|
|
65
82
|
}
|
|
66
|
-
return { role: 'assistant', content: message.content.map(mapAssistantPart) };
|
|
67
|
-
};
|
|
83
|
+
return { role: 'assistant', content: yield Promise.all(message.content.map((part) => mapAssistantPart(part, resolveDriveFile))) };
|
|
84
|
+
});
|
|
68
85
|
const mapToolMessage = (message) => ({
|
|
69
86
|
role: 'tool',
|
|
70
87
|
content: message.content.map(mapToolResult),
|
|
71
88
|
});
|
|
72
|
-
const toSdkMessages = (messages) => messages.map((message) => {
|
|
89
|
+
const toSdkMessages = (messages, resolveDriveFile) => Promise.all(messages.map((message) => {
|
|
73
90
|
switch (message.role) {
|
|
74
91
|
case 'user':
|
|
75
|
-
return mapUserMessage(message);
|
|
92
|
+
return mapUserMessage(message, resolveDriveFile);
|
|
76
93
|
case 'assistant':
|
|
77
|
-
return mapAssistantMessage(message);
|
|
94
|
+
return mapAssistantMessage(message, resolveDriveFile);
|
|
78
95
|
case 'tool':
|
|
79
96
|
return mapToolMessage(message);
|
|
80
97
|
}
|
|
81
|
-
});
|
|
98
|
+
}));
|
|
82
99
|
exports.toSdkMessages = toSdkMessages;
|
package/lib/commonjs/getActionProcessor/core/eventBus/getEventBusSendMessagesActionProcessor.js
CHANGED
|
@@ -15,7 +15,7 @@ const quidproquo_core_1 = require("quidproquo-core");
|
|
|
15
15
|
const awsNamingUtils_1 = require("../../../awsNamingUtils");
|
|
16
16
|
const publishMessage_1 = require("../../../logic/sns/publishMessage");
|
|
17
17
|
const getProcessEventBusSendMessage = (qpqConfig) => {
|
|
18
|
-
return (_a, session_1) => __awaiter(void 0, [_a, session_1], void 0, function* ({ eventBusName, eventBusMessages
|
|
18
|
+
return (_a, session_1) => __awaiter(void 0, [_a, session_1], void 0, function* ({ eventBusName, eventBusMessages }, session) {
|
|
19
19
|
var _b, _c, _d, _e, _f;
|
|
20
20
|
const region = quidproquo_config_aws_1.qpqConfigAwsUtils.getApplicationModuleDeployRegion(qpqConfig);
|
|
21
21
|
const eventBusConfig = quidproquo_core_1.qpqCoreUtils.getEventBusConfigByName(eventBusName, qpqConfig);
|
|
@@ -81,9 +81,7 @@ const getProcessFileStreamOpen = (qpqConfig) => {
|
|
|
81
81
|
const streamId = `s3-${(0, awsLambdaUtils_1.randomGuid)()}`;
|
|
82
82
|
const isText = encoding === 'text';
|
|
83
83
|
const size = chunkSize !== null && chunkSize !== void 0 ? chunkSize : 65536;
|
|
84
|
-
const transform = isText
|
|
85
|
-
? (buf) => buf.toString('utf8')
|
|
86
|
-
: (buf) => buf.toString('base64');
|
|
84
|
+
const transform = isText ? (buf) => buf.toString('utf8') : (buf) => buf.toString('base64');
|
|
87
85
|
const iterator = chunkedReadableIterator(response.Body, size, transform);
|
|
88
86
|
streamRegistry.register(streamId, iterator);
|
|
89
87
|
return (0, quidproquo_core_1.actionResult)({ id: streamId, encoding });
|
package/lib/commonjs/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertActionProcessor.js
CHANGED
|
@@ -32,6 +32,7 @@ const getProcessKeyValueStoreUpsert = (qpqConfig) => {
|
|
|
32
32
|
try {
|
|
33
33
|
yield (0, dynamo_1.putItem)(dynamoTableName, item, keys, {
|
|
34
34
|
expires: options === null || options === void 0 ? void 0 : options.ttlInSeconds,
|
|
35
|
+
ifNotExistsAttribute: (options === null || options === void 0 ? void 0 : options.ifNotExists) ? storeConfig.partitionKey.key : undefined,
|
|
35
36
|
}, region);
|
|
36
37
|
return (0, quidproquo_core_1.actionResult)(void 0);
|
|
37
38
|
}
|
|
@@ -39,6 +40,7 @@ const getProcessKeyValueStoreUpsert = (qpqConfig) => {
|
|
|
39
40
|
return (0, quidproquo_core_1.actionResultErrorFromCaughtError)(error, {
|
|
40
41
|
InternalServerError: () => (0, quidproquo_core_1.actionResultError)(quidproquo_core_2.KeyValueStoreUpsertErrorTypeEnum.ServiceUnavailable, 'KVS Service Unavailable'),
|
|
41
42
|
ResourceNotFoundException: () => (0, quidproquo_core_1.actionResultError)(quidproquo_core_2.KeyValueStoreUpsertErrorTypeEnum.ResourceNotFound, 'KVS Resource Not Found'),
|
|
43
|
+
ConditionalCheckFailedException: () => (0, quidproquo_core_1.actionResultError)(quidproquo_core_2.KeyValueStoreUpsertErrorTypeEnum.Conflict, 'KVS item already exists'),
|
|
42
44
|
});
|
|
43
45
|
}
|
|
44
46
|
});
|
|
@@ -94,9 +94,7 @@ const createBufferedLogger = (bucketName, regionForBucket, ship) => {
|
|
|
94
94
|
let disabledLogCorrelations = [];
|
|
95
95
|
return {
|
|
96
96
|
enableLogs: (enable, reason, correlation) => __awaiter(void 0, void 0, void 0, function* () {
|
|
97
|
-
disabledLogCorrelations = enable
|
|
98
|
-
? disabledLogCorrelations.filter((dlc) => dlc !== correlation)
|
|
99
|
-
: [...disabledLogCorrelations, correlation];
|
|
97
|
+
disabledLogCorrelations = enable ? disabledLogCorrelations.filter((dlc) => dlc !== correlation) : [...disabledLogCorrelations, correlation];
|
|
100
98
|
}),
|
|
101
99
|
log: (result) => {
|
|
102
100
|
// Defer all work (including JSON.stringify) to a microtask so the caller is not blocked.
|
|
@@ -7,7 +7,7 @@ const cognitoAuthenticationResultTypeToQpqAuthenticationInfo = (authResult, issu
|
|
|
7
7
|
// Parse the issueDateTime and add the expiresIn to get the expiration date
|
|
8
8
|
let issueDate = new Date(issueDateTime);
|
|
9
9
|
issueDate.setSeconds(issueDate.getSeconds() + (authResult.ExpiresIn || 0));
|
|
10
|
-
const expiresAt =
|
|
10
|
+
const expiresAt = (0, quidproquo_core_1.getQpqIsoDateTimeFromDate)(issueDate);
|
|
11
11
|
return {
|
|
12
12
|
accessToken: authResult.AccessToken,
|
|
13
13
|
idToken: authResult.IdToken,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { KvsKey } from 'quidproquo-core';
|
|
2
2
|
export interface PutItemOptions {
|
|
3
3
|
expires?: number;
|
|
4
|
+
ifNotExistsAttribute?: string;
|
|
4
5
|
}
|
|
5
6
|
export declare function putItem<Item, T extends object = any>(tableName: string, item: Item, attributes: KvsKey[], options: PutItemOptions, region: string): Promise<void>;
|
|
@@ -39,9 +39,11 @@ const convertObjectToDynamoItem = (obj) => {
|
|
|
39
39
|
function putItem(tableName, item, attributes, options, region) {
|
|
40
40
|
return __awaiter(this, void 0, void 0, function* () {
|
|
41
41
|
const dynamoDBClient = (0, createAwsClient_1.createAwsClient)(client_dynamodb_1.DynamoDBClient, { region });
|
|
42
|
-
yield dynamoDBClient.send(new client_dynamodb_1.PutItemCommand({
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
yield dynamoDBClient.send(new client_dynamodb_1.PutItemCommand(Object.assign({ TableName: tableName, Item: convertObjectToDynamoItem(item) }, (options.ifNotExistsAttribute
|
|
43
|
+
? {
|
|
44
|
+
ConditionExpression: 'attribute_not_exists(#ineAttr)',
|
|
45
|
+
ExpressionAttributeNames: { '#ineAttr': options.ifNotExistsAttribute },
|
|
46
|
+
}
|
|
47
|
+
: {}))));
|
|
46
48
|
});
|
|
47
49
|
}
|
|
@@ -120,11 +120,7 @@ const buildDynamoUpdateExpressionPart = (update, updateIndex) => {
|
|
|
120
120
|
}
|
|
121
121
|
};
|
|
122
122
|
// Types that generate SET expressions
|
|
123
|
-
const SET_ACTION_TYPES = [
|
|
124
|
-
quidproquo_core_1.KvsUpdateActionType.Set,
|
|
125
|
-
quidproquo_core_1.KvsUpdateActionType.SetIfNotExists,
|
|
126
|
-
quidproquo_core_1.KvsUpdateActionType.Increment
|
|
127
|
-
];
|
|
123
|
+
const SET_ACTION_TYPES = [quidproquo_core_1.KvsUpdateActionType.Set, quidproquo_core_1.KvsUpdateActionType.SetIfNotExists, quidproquo_core_1.KvsUpdateActionType.Increment];
|
|
128
124
|
const buildDynamoUpdateExpressionForClause = (clause, actionTypes, kvsUpdate) => {
|
|
129
125
|
const actions = kvsUpdate.filter((update) => actionTypes.includes(update.action));
|
|
130
126
|
if (actions.length === 0) {
|
|
@@ -15,9 +15,9 @@ const s3_request_presigner_1 = require("@aws-sdk/s3-request-presigner");
|
|
|
15
15
|
// import { createPresignedPost } from '@aws-sdk/s3-presigned-post';
|
|
16
16
|
const createAwsClient_1 = require("../createAwsClient");
|
|
17
17
|
// export const generatePresignedUploadFormUrl = async (
|
|
18
|
-
// bucketName: string,
|
|
19
|
-
// objectKey: string,
|
|
20
|
-
// region: string,
|
|
18
|
+
// bucketName: string,
|
|
19
|
+
// objectKey: string,
|
|
20
|
+
// region: string,
|
|
21
21
|
// expirationMs: number,
|
|
22
22
|
// contentType: string | undefined,
|
|
23
23
|
// maxSizeBytes?: number | undefined
|
|
@@ -56,7 +56,7 @@ const generatePresignedUploadUrl = (bucketName, objectKey, region, expirationMs,
|
|
|
56
56
|
Bucket: bucketName,
|
|
57
57
|
Key: objectKey,
|
|
58
58
|
ContentType: contentType,
|
|
59
|
-
Metadata: Object.assign(Object.assign({}, (correlationId && { 'correlation-id': correlationId })), { 'upload-timestamp': Date.now().toString() })
|
|
59
|
+
Metadata: Object.assign(Object.assign({}, (correlationId && { 'correlation-id': correlationId })), { 'upload-timestamp': Date.now().toString() }),
|
|
60
60
|
});
|
|
61
61
|
const url = yield (0, s3_request_presigner_1.getSignedUrl)(s3Client, putObjectCommand, {
|
|
62
62
|
expiresIn: expirationMs / 1000,
|
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
import { actionResult, actionResultError, AiActionType, ErrorTypeEnum, } from 'quidproquo-core';
|
|
2
2
|
import { generateText, stepCountIs } from 'ai';
|
|
3
|
-
import { prepareAiPromptCall, toSdkMessages } from './logic';
|
|
3
|
+
import { createDriveFileResolver, prepareAiPromptCall, toSdkMessages } from './logic';
|
|
4
4
|
const getProcessAiPrompt = (qpqConfig) => {
|
|
5
5
|
return async (payload, session, actionProcessorList, logger, updateSession, dynamicModuleLoader, streamRegistry) => {
|
|
6
6
|
const prepared = prepareAiPromptCall(qpqConfig, payload, session, actionProcessorList, logger, dynamicModuleLoader, streamRegistry);
|
|
7
7
|
if ('error' in prepared) {
|
|
8
8
|
return actionResultError(prepared.error.type, prepared.error.message);
|
|
9
9
|
}
|
|
10
|
-
const promptOrMessages = payload.messages
|
|
11
|
-
? { messages: toSdkMessages(payload.messages) }
|
|
12
|
-
: { prompt: payload.prompt };
|
|
13
10
|
try {
|
|
11
|
+
const promptOrMessages = payload.messages
|
|
12
|
+
? {
|
|
13
|
+
messages: await toSdkMessages(payload.messages, createDriveFileResolver(qpqConfig, session, actionProcessorList, logger, dynamicModuleLoader, streamRegistry)),
|
|
14
|
+
}
|
|
15
|
+
: { prompt: payload.prompt };
|
|
16
|
+
// Extended thinking — the model reasons before answering, within the token budget.
|
|
17
|
+
const providerOptions = payload.reasoning
|
|
18
|
+
? { bedrock: { reasoningConfig: { type: 'enabled', budgetTokens: payload.reasoning.budgetTokens ?? 4096 } } }
|
|
19
|
+
: undefined;
|
|
14
20
|
const result = await generateText({
|
|
15
21
|
model: prepared.model,
|
|
16
22
|
system: payload.system,
|
|
17
23
|
...promptOrMessages,
|
|
18
24
|
tools: prepared.tools,
|
|
25
|
+
providerOptions,
|
|
19
26
|
stopWhen: stepCountIs(10),
|
|
20
27
|
});
|
|
21
28
|
return actionResult({ text: result.text });
|
|
@@ -1,22 +1,29 @@
|
|
|
1
1
|
import { actionResult, actionResultError, AiActionType, ErrorTypeEnum, } from 'quidproquo-core';
|
|
2
2
|
import { stepCountIs, streamText } from 'ai';
|
|
3
3
|
import { randomGuid } from '../../../awsLambdaUtils';
|
|
4
|
-
import { mapAiStreamPart, prepareAiPromptCall, toSdkMessages } from './logic';
|
|
4
|
+
import { createDriveFileResolver, mapAiStreamPart, prepareAiPromptCall, toSdkMessages } from './logic';
|
|
5
5
|
const getProcessAiPromptStream = (qpqConfig) => {
|
|
6
6
|
return async (payload, session, actionProcessorList, logger, updateSession, dynamicModuleLoader, streamRegistry) => {
|
|
7
7
|
const prepared = prepareAiPromptCall(qpqConfig, payload, session, actionProcessorList, logger, dynamicModuleLoader, streamRegistry);
|
|
8
8
|
if ('error' in prepared) {
|
|
9
9
|
return actionResultError(prepared.error.type, prepared.error.message);
|
|
10
10
|
}
|
|
11
|
-
const promptOrMessages = payload.messages
|
|
12
|
-
? { messages: toSdkMessages(payload.messages) }
|
|
13
|
-
: { prompt: payload.prompt };
|
|
14
11
|
try {
|
|
12
|
+
const promptOrMessages = payload.messages
|
|
13
|
+
? {
|
|
14
|
+
messages: await toSdkMessages(payload.messages, createDriveFileResolver(qpqConfig, session, actionProcessorList, logger, dynamicModuleLoader, streamRegistry)),
|
|
15
|
+
}
|
|
16
|
+
: { prompt: payload.prompt };
|
|
17
|
+
// Extended thinking — thinking progress streams out as Reasoning* parts.
|
|
18
|
+
const providerOptions = payload.reasoning
|
|
19
|
+
? { bedrock: { reasoningConfig: { type: 'enabled', budgetTokens: payload.reasoning.budgetTokens ?? 4096 } } }
|
|
20
|
+
: undefined;
|
|
15
21
|
const { fullStream } = streamText({
|
|
16
22
|
model: prepared.model,
|
|
17
23
|
system: payload.system,
|
|
18
24
|
...promptOrMessages,
|
|
19
25
|
tools: prepared.tools,
|
|
26
|
+
providerOptions,
|
|
20
27
|
stopWhen: stepCountIs(10),
|
|
21
28
|
// streamText swallows errors by default to keep the server alive — surface them to CloudWatch.
|
|
22
29
|
onError: ({ error }) => {
|
|
@@ -25,17 +25,17 @@ import { mapAiStreamToolInputStart } from './mapAiStreamToolInputStart';
|
|
|
25
25
|
import { mapAiStreamToolOutputDenied } from './mapAiStreamToolOutputDenied';
|
|
26
26
|
import { mapAiStreamToolResult } from './mapAiStreamToolResult';
|
|
27
27
|
export const aiStreamPartMappers = {
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
start: mapAiStreamStart,
|
|
29
|
+
finish: mapAiStreamFinish,
|
|
30
30
|
'start-step': mapAiStreamStartStep,
|
|
31
31
|
'finish-step': mapAiStreamFinishStep,
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
abort: mapAiStreamAbort,
|
|
33
|
+
error: mapAiStreamError,
|
|
34
|
+
raw: mapAiStreamRaw,
|
|
35
|
+
source: mapAiStreamSourcePart,
|
|
36
|
+
file: mapAiStreamFilePart,
|
|
37
37
|
'reasoning-file': mapAiStreamReasoningFilePart,
|
|
38
|
-
|
|
38
|
+
custom: mapAiStreamCustom,
|
|
39
39
|
'text-start': mapAiStreamTextStart,
|
|
40
40
|
'text-end': mapAiStreamTextEnd,
|
|
41
41
|
'text-delta': mapAiStreamTextDelta,
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { ActionProcessorList, DynamicModuleLoader, QPQConfig, QpqLogger, StorySession, StreamRegistry } from 'quidproquo-core';
|
|
2
|
+
import { AiDriveFileResolver } from './toSdkMessages';
|
|
3
|
+
export declare const createDriveFileResolver: (qpqConfig: QPQConfig, session: StorySession, actionProcessorList: ActionProcessorList, logger: QpqLogger, dynamicModuleLoader: DynamicModuleLoader, streamRegistry?: StreamRegistry) => AiDriveFileResolver;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { askFileReadBinaryContents, createImplementationRuntime, } from 'quidproquo-core';
|
|
2
|
+
import { randomGuid } from '../../../../awsLambdaUtils';
|
|
3
|
+
// Reads drive-referenced files through the active file action processor, so the
|
|
4
|
+
// same message works against S3 in production and local disk under the dev server.
|
|
5
|
+
export const createDriveFileResolver = (qpqConfig, session, actionProcessorList, logger, dynamicModuleLoader, streamRegistry) => {
|
|
6
|
+
return async (drive, filepath) => {
|
|
7
|
+
const resolveStory = createImplementationRuntime(qpqConfig, [`AI File: ${drive}/${filepath}`], () => new Date().toISOString(), randomGuid, session, actionProcessorList, logger, dynamicModuleLoader, streamRegistry);
|
|
8
|
+
const storyResult = await resolveStory(askFileReadBinaryContents, [drive, filepath]);
|
|
9
|
+
if (storyResult.error) {
|
|
10
|
+
throw new Error(storyResult.error.errorText);
|
|
11
|
+
}
|
|
12
|
+
return storyResult.result;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import { AiMessage } from 'quidproquo-core';
|
|
1
|
+
import { AiMessage, QPQBinaryData } from 'quidproquo-core';
|
|
2
2
|
import type { ModelMessage } from 'ai';
|
|
3
|
-
export
|
|
3
|
+
export type AiDriveFileResolver = (drive: string, filepath: string) => Promise<QPQBinaryData>;
|
|
4
|
+
export declare const toSdkMessages: (messages: AiMessage[], resolveDriveFile: AiDriveFileResolver) => Promise<ModelMessage[]>;
|
|
@@ -1,25 +1,32 @@
|
|
|
1
|
-
const
|
|
2
|
-
if (
|
|
3
|
-
return {
|
|
1
|
+
const mapFilePart = async (part, resolveDriveFile) => {
|
|
2
|
+
if ('url' in part) {
|
|
3
|
+
return {
|
|
4
|
+
type: 'file',
|
|
5
|
+
data: new URL(part.url),
|
|
6
|
+
mediaType: part.mediaType,
|
|
7
|
+
filename: part.filename,
|
|
8
|
+
};
|
|
4
9
|
}
|
|
10
|
+
const binary = await resolveDriveFile(part.drive, part.filepath);
|
|
5
11
|
return {
|
|
6
12
|
type: 'file',
|
|
7
|
-
data:
|
|
13
|
+
data: binary.base64Data,
|
|
8
14
|
mediaType: part.mediaType,
|
|
9
|
-
filename: part.filename,
|
|
15
|
+
filename: part.filename ?? binary.filename,
|
|
10
16
|
};
|
|
11
17
|
};
|
|
12
|
-
const
|
|
18
|
+
const mapUserPart = async (part, resolveDriveFile) => {
|
|
19
|
+
if (part.type === 'text') {
|
|
20
|
+
return { type: 'text', text: part.text };
|
|
21
|
+
}
|
|
22
|
+
return mapFilePart(part, resolveDriveFile);
|
|
23
|
+
};
|
|
24
|
+
const mapAssistantPart = async (part, resolveDriveFile) => {
|
|
13
25
|
switch (part.type) {
|
|
14
26
|
case 'text':
|
|
15
27
|
return { type: 'text', text: part.text };
|
|
16
28
|
case 'file':
|
|
17
|
-
return
|
|
18
|
-
type: 'file',
|
|
19
|
-
data: new URL(part.url),
|
|
20
|
-
mediaType: part.mediaType,
|
|
21
|
-
filename: part.filename,
|
|
22
|
-
};
|
|
29
|
+
return mapFilePart(part, resolveDriveFile);
|
|
23
30
|
case 'tool-call':
|
|
24
31
|
return {
|
|
25
32
|
type: 'tool-call',
|
|
@@ -50,29 +57,29 @@ const mapToolResult = (part) => {
|
|
|
50
57
|
output,
|
|
51
58
|
};
|
|
52
59
|
};
|
|
53
|
-
const mapUserMessage = (message) => {
|
|
60
|
+
const mapUserMessage = async (message, resolveDriveFile) => {
|
|
54
61
|
if (typeof message.content === 'string') {
|
|
55
62
|
return { role: 'user', content: message.content };
|
|
56
63
|
}
|
|
57
|
-
return { role: 'user', content: message.content.map(mapUserPart) };
|
|
64
|
+
return { role: 'user', content: await Promise.all(message.content.map((part) => mapUserPart(part, resolveDriveFile))) };
|
|
58
65
|
};
|
|
59
|
-
const mapAssistantMessage = (message) => {
|
|
66
|
+
const mapAssistantMessage = async (message, resolveDriveFile) => {
|
|
60
67
|
if (typeof message.content === 'string') {
|
|
61
68
|
return { role: 'assistant', content: message.content };
|
|
62
69
|
}
|
|
63
|
-
return { role: 'assistant', content: message.content.map(mapAssistantPart) };
|
|
70
|
+
return { role: 'assistant', content: await Promise.all(message.content.map((part) => mapAssistantPart(part, resolveDriveFile))) };
|
|
64
71
|
};
|
|
65
72
|
const mapToolMessage = (message) => ({
|
|
66
73
|
role: 'tool',
|
|
67
74
|
content: message.content.map(mapToolResult),
|
|
68
75
|
});
|
|
69
|
-
export const toSdkMessages = (messages) => messages.map((message) => {
|
|
76
|
+
export const toSdkMessages = (messages, resolveDriveFile) => Promise.all(messages.map((message) => {
|
|
70
77
|
switch (message.role) {
|
|
71
78
|
case 'user':
|
|
72
|
-
return mapUserMessage(message);
|
|
79
|
+
return mapUserMessage(message, resolveDriveFile);
|
|
73
80
|
case 'assistant':
|
|
74
|
-
return mapAssistantMessage(message);
|
|
81
|
+
return mapAssistantMessage(message, resolveDriveFile);
|
|
75
82
|
case 'tool':
|
|
76
83
|
return mapToolMessage(message);
|
|
77
84
|
}
|
|
78
|
-
});
|
|
85
|
+
}));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { qpqConfigAwsUtils } from 'quidproquo-config-aws';
|
|
2
|
-
import { actionResult, qpqCoreUtils
|
|
2
|
+
import { actionResult, qpqCoreUtils } from 'quidproquo-core';
|
|
3
3
|
import { ApiKeyValidationActionType } from 'quidproquo-webserver';
|
|
4
4
|
import { timingSafeEqual } from 'crypto';
|
|
5
5
|
import { getConfigRuntimeResourceName } from '../../../awsNamingUtils';
|
|
@@ -3,7 +3,7 @@ import { actionResult, actionResultError, actionResultErrorFromCaughtError, Erro
|
|
|
3
3
|
import { getEventBusSnsTopicArn } from '../../../awsNamingUtils';
|
|
4
4
|
import { publishMessage } from '../../../logic/sns/publishMessage';
|
|
5
5
|
const getProcessEventBusSendMessage = (qpqConfig) => {
|
|
6
|
-
return async ({ eventBusName, eventBusMessages
|
|
6
|
+
return async ({ eventBusName, eventBusMessages }, session) => {
|
|
7
7
|
const region = qpqConfigAwsUtils.getApplicationModuleDeployRegion(qpqConfig);
|
|
8
8
|
const eventBusConfig = qpqCoreUtils.getEventBusConfigByName(eventBusName, qpqConfig);
|
|
9
9
|
if (!eventBusConfig) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { qpqConfigAwsUtils } from 'quidproquo-config-aws';
|
|
2
|
-
import { actionResult, actionResultError, actionResultErrorFromCaughtError, FileActionType, FileDeleteErrorTypeEnum } from 'quidproquo-core';
|
|
2
|
+
import { actionResult, actionResultError, actionResultErrorFromCaughtError, FileActionType, FileDeleteErrorTypeEnum, } from 'quidproquo-core';
|
|
3
3
|
import { deleteFiles } from '../../../logic/s3/s3Utils';
|
|
4
4
|
import { resolveStorageDriveBucketName } from './utils';
|
|
5
5
|
const getProcessFileDelete = (qpqConfig) => {
|
package/lib/esm/getActionProcessor/core/file/getFileGenerateTemporarySecureUrlActionProcessor.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { qpqConfigAwsUtils } from 'quidproquo-config-aws';
|
|
2
2
|
import { actionResultError, ErrorTypeEnum } from 'quidproquo-core';
|
|
3
|
-
import { actionResult, FileActionType, FileGenerateTemporarySecureUrlErrorTypeEnum } from 'quidproquo-core';
|
|
3
|
+
import { actionResult, FileActionType, FileGenerateTemporarySecureUrlErrorTypeEnum, } from 'quidproquo-core';
|
|
4
4
|
import { generatePresignedUrl } from '../../../logic/s3/generatePresignedUrl';
|
|
5
5
|
import { resolveStorageDriveBucketName } from './utils';
|
|
6
6
|
// SigV4 presigned URLs cannot expire more than 7 days in the future
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { qpqConfigAwsUtils } from 'quidproquo-config-aws';
|
|
2
2
|
import { actionResultError, ErrorTypeEnum } from 'quidproquo-core';
|
|
3
|
-
import { actionResult, FileActionType, FileGenerateTemporaryUploadSecureUrlErrorTypeEnum } from 'quidproquo-core';
|
|
3
|
+
import { actionResult, FileActionType, FileGenerateTemporaryUploadSecureUrlErrorTypeEnum, } from 'quidproquo-core';
|
|
4
4
|
import { generatePresignedUploadUrl } from '../../../logic/s3/generatePresignedUploadUrl';
|
|
5
5
|
import { resolveStorageDriveBucketName } from './utils';
|
|
6
6
|
// SigV4 presigned URLs cannot expire more than 7 days in the future
|
|
@@ -34,9 +34,7 @@ const getProcessFileStreamOpen = (qpqConfig) => {
|
|
|
34
34
|
const streamId = `s3-${randomGuid()}`;
|
|
35
35
|
const isText = encoding === 'text';
|
|
36
36
|
const size = chunkSize ?? 65536;
|
|
37
|
-
const transform = isText
|
|
38
|
-
? (buf) => buf.toString('utf8')
|
|
39
|
-
: (buf) => buf.toString('base64');
|
|
37
|
+
const transform = isText ? (buf) => buf.toString('utf8') : (buf) => buf.toString('base64');
|
|
40
38
|
const iterator = chunkedReadableIterator(response.Body, size, transform);
|
|
41
39
|
streamRegistry.register(streamId, iterator);
|
|
42
40
|
return actionResult({ id: streamId, encoding });
|
package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreDeleteActionProcessor.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { qpqConfigAwsUtils } from 'quidproquo-config-aws';
|
|
2
2
|
import { qpqCoreUtils } from 'quidproquo-core';
|
|
3
|
-
import { actionResult, actionResultError, actionResultErrorFromCaughtError, KeyValueStoreActionType, KeyValueStoreDeleteErrorTypeEnum } from 'quidproquo-core';
|
|
3
|
+
import { actionResult, actionResultError, actionResultErrorFromCaughtError, KeyValueStoreActionType, KeyValueStoreDeleteErrorTypeEnum, } from 'quidproquo-core';
|
|
4
4
|
import { getKvsDynamoTableNameFromConfig } from '../../../awsNamingUtils';
|
|
5
5
|
import { deleteItem } from '../../../logic/dynamo';
|
|
6
6
|
const getProcessKeyValueStoreDelete = (qpqConfig) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { qpqConfigAwsUtils } from 'quidproquo-config-aws';
|
|
2
|
-
import { actionResult, actionResultError, actionResultErrorFromCaughtError, KeyValueStoreActionType, KeyValueStoreGetErrorTypeEnum } from 'quidproquo-core';
|
|
2
|
+
import { actionResult, actionResultError, actionResultErrorFromCaughtError, KeyValueStoreActionType, KeyValueStoreGetErrorTypeEnum, } from 'quidproquo-core';
|
|
3
3
|
import { getKvsDynamoTableNameFromConfig } from '../../../awsNamingUtils';
|
|
4
4
|
import { getItem } from '../../../logic/dynamo';
|
|
5
5
|
const getProcessKeyValueStoreGet = (qpqConfig) => {
|
package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreGetAllActionProcessor.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { qpqConfigAwsUtils } from 'quidproquo-config-aws';
|
|
2
|
-
import { actionResult, actionResultError, actionResultErrorFromCaughtError, KeyValueStoreActionType, KeyValueStoreGetAllErrorTypeEnum } from 'quidproquo-core';
|
|
2
|
+
import { actionResult, actionResultError, actionResultErrorFromCaughtError, KeyValueStoreActionType, KeyValueStoreGetAllErrorTypeEnum, } from 'quidproquo-core';
|
|
3
3
|
import { getKvsDynamoTableNameFromConfig } from '../../../awsNamingUtils';
|
|
4
4
|
import { getAllItems } from '../../../logic/dynamo';
|
|
5
5
|
const getProcessKeyValueStoreGetAll = (qpqConfig) => {
|
package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreQueryActionProcessor.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { qpqConfigAwsUtils } from 'quidproquo-config-aws';
|
|
2
|
-
import { actionResultError, actionResultErrorFromCaughtError, ErrorTypeEnum, qpqCoreUtils } from 'quidproquo-core';
|
|
2
|
+
import { actionResultError, actionResultErrorFromCaughtError, ErrorTypeEnum, qpqCoreUtils, } from 'quidproquo-core';
|
|
3
3
|
import { actionResult, KeyValueStoreActionType, KeyValueStoreQueryErrorTypeEnum } from 'quidproquo-core';
|
|
4
4
|
import { getKvsDynamoTableNameFromConfig } from '../../../awsNamingUtils';
|
|
5
5
|
import { query } from '../../../logic/dynamo';
|
package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreScanActionProcessor.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { qpqConfigAwsUtils } from 'quidproquo-config-aws';
|
|
2
|
-
import { actionResultError, actionResultErrorFromCaughtError } from 'quidproquo-core';
|
|
2
|
+
import { actionResultError, actionResultErrorFromCaughtError, } from 'quidproquo-core';
|
|
3
3
|
import { actionResult, KeyValueStoreActionType, KeyValueStoreScanErrorTypeEnum } from 'quidproquo-core';
|
|
4
4
|
import { getKvsDynamoTableNameFromConfig } from '../../../awsNamingUtils';
|
|
5
5
|
import { scan } from '../../../logic/dynamo/scan';
|
package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreUpdateActionProcessor.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { qpqConfigAwsUtils } from 'quidproquo-config-aws';
|
|
2
2
|
import { qpqCoreUtils } from 'quidproquo-core';
|
|
3
|
-
import { actionResult, actionResultError, actionResultErrorFromCaughtError, KeyValueStoreActionType, KeyValueStoreUpdateErrorTypeEnum } from 'quidproquo-core';
|
|
3
|
+
import { actionResult, actionResultError, actionResultErrorFromCaughtError, KeyValueStoreActionType, KeyValueStoreUpdateErrorTypeEnum, } from 'quidproquo-core';
|
|
4
4
|
import { getKvsDynamoTableNameFromConfig } from '../../../awsNamingUtils';
|
|
5
5
|
import { updateItem } from '../../../logic/dynamo';
|
|
6
6
|
const getProcessKeyValueStoreUpdate = (qpqConfig) => {
|
package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertActionProcessor.js
CHANGED
|
@@ -20,6 +20,7 @@ const getProcessKeyValueStoreUpsert = (qpqConfig) => {
|
|
|
20
20
|
try {
|
|
21
21
|
await putItem(dynamoTableName, item, keys, {
|
|
22
22
|
expires: options?.ttlInSeconds,
|
|
23
|
+
ifNotExistsAttribute: options?.ifNotExists ? storeConfig.partitionKey.key : undefined,
|
|
23
24
|
}, region);
|
|
24
25
|
return actionResult(void 0);
|
|
25
26
|
}
|
|
@@ -27,6 +28,7 @@ const getProcessKeyValueStoreUpsert = (qpqConfig) => {
|
|
|
27
28
|
return actionResultErrorFromCaughtError(error, {
|
|
28
29
|
InternalServerError: () => actionResultError(KeyValueStoreUpsertErrorTypeEnum.ServiceUnavailable, 'KVS Service Unavailable'),
|
|
29
30
|
ResourceNotFoundException: () => actionResultError(KeyValueStoreUpsertErrorTypeEnum.ResourceNotFound, 'KVS Resource Not Found'),
|
|
31
|
+
ConditionalCheckFailedException: () => actionResultError(KeyValueStoreUpsertErrorTypeEnum.Conflict, 'KVS item already exists'),
|
|
30
32
|
});
|
|
31
33
|
}
|
|
32
34
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { qpqConfigAwsUtils } from 'quidproquo-config-aws';
|
|
2
|
-
import { actionResult, actionResultError, actionResultErrorFromCaughtError, QueueActionType, QueueSendMessagesErrorTypeEnum, toCrossServiceSession } from 'quidproquo-core';
|
|
2
|
+
import { actionResult, actionResultError, actionResultErrorFromCaughtError, QueueActionType, QueueSendMessagesErrorTypeEnum, toCrossServiceSession, } from 'quidproquo-core';
|
|
3
3
|
import { sendMessages } from '../../../logic/sqs/sendMessages';
|
|
4
4
|
import { resolveResourceName } from '../../../runtimeConfig/qpqAwsLambdaRuntimeConfigUtils';
|
|
5
5
|
const getProcessQueueSendMessage = (qpqConfig) => {
|
|
@@ -76,9 +76,7 @@ const createBufferedLogger = (bucketName, regionForBucket, ship) => {
|
|
|
76
76
|
let disabledLogCorrelations = [];
|
|
77
77
|
return {
|
|
78
78
|
enableLogs: async (enable, reason, correlation) => {
|
|
79
|
-
disabledLogCorrelations = enable
|
|
80
|
-
? disabledLogCorrelations.filter((dlc) => dlc !== correlation)
|
|
81
|
-
: [...disabledLogCorrelations, correlation];
|
|
79
|
+
disabledLogCorrelations = enable ? disabledLogCorrelations.filter((dlc) => dlc !== correlation) : [...disabledLogCorrelations, correlation];
|
|
82
80
|
},
|
|
83
81
|
log: (result) => {
|
|
84
82
|
// Defer all work (including JSON.stringify) to a microtask so the caller is not blocked.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { AuthenticateUserChallenge } from 'quidproquo-core';
|
|
1
|
+
import { AuthenticateUserChallenge, getQpqIsoDateTimeFromDate } from 'quidproquo-core';
|
|
2
2
|
import { ChallengeNameType } from '@aws-sdk/client-cognito-identity-provider';
|
|
3
3
|
export const cognitoAuthenticationResultTypeToQpqAuthenticationInfo = (authResult, issueDateTime) => {
|
|
4
4
|
// Parse the issueDateTime and add the expiresIn to get the expiration date
|
|
5
5
|
let issueDate = new Date(issueDateTime);
|
|
6
6
|
issueDate.setSeconds(issueDate.getSeconds() + (authResult.ExpiresIn || 0));
|
|
7
|
-
const expiresAt = issueDate
|
|
7
|
+
const expiresAt = getQpqIsoDateTimeFromDate(issueDate);
|
|
8
8
|
return {
|
|
9
9
|
accessToken: authResult.AccessToken,
|
|
10
10
|
idToken: authResult.IdToken,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { KvsKey } from 'quidproquo-core';
|
|
2
2
|
export interface PutItemOptions {
|
|
3
3
|
expires?: number;
|
|
4
|
+
ifNotExistsAttribute?: string;
|
|
4
5
|
}
|
|
5
6
|
export declare function putItem<Item, T extends object = any>(tableName: string, item: Item, attributes: KvsKey[], options: PutItemOptions, region: string): Promise<void>;
|
|
@@ -29,5 +29,11 @@ export async function putItem(tableName, item, attributes, options, region) {
|
|
|
29
29
|
await dynamoDBClient.send(new PutItemCommand({
|
|
30
30
|
TableName: tableName,
|
|
31
31
|
Item: convertObjectToDynamoItem(item),
|
|
32
|
+
...(options.ifNotExistsAttribute
|
|
33
|
+
? {
|
|
34
|
+
ConditionExpression: 'attribute_not_exists(#ineAttr)',
|
|
35
|
+
ExpressionAttributeNames: { '#ineAttr': options.ifNotExistsAttribute },
|
|
36
|
+
}
|
|
37
|
+
: {}),
|
|
32
38
|
}));
|
|
33
39
|
}
|
|
@@ -115,11 +115,7 @@ const buildDynamoUpdateExpressionPart = (update, updateIndex) => {
|
|
|
115
115
|
}
|
|
116
116
|
};
|
|
117
117
|
// Types that generate SET expressions
|
|
118
|
-
const SET_ACTION_TYPES = [
|
|
119
|
-
KvsUpdateActionType.Set,
|
|
120
|
-
KvsUpdateActionType.SetIfNotExists,
|
|
121
|
-
KvsUpdateActionType.Increment
|
|
122
|
-
];
|
|
118
|
+
const SET_ACTION_TYPES = [KvsUpdateActionType.Set, KvsUpdateActionType.SetIfNotExists, KvsUpdateActionType.Increment];
|
|
123
119
|
const buildDynamoUpdateExpressionForClause = (clause, actionTypes, kvsUpdate) => {
|
|
124
120
|
const actions = kvsUpdate.filter((update) => actionTypes.includes(update.action));
|
|
125
121
|
if (actions.length === 0) {
|
|
@@ -3,9 +3,9 @@ import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
|
3
3
|
// import { createPresignedPost } from '@aws-sdk/s3-presigned-post';
|
|
4
4
|
import { createAwsClient } from '../createAwsClient';
|
|
5
5
|
// export const generatePresignedUploadFormUrl = async (
|
|
6
|
-
// bucketName: string,
|
|
7
|
-
// objectKey: string,
|
|
8
|
-
// region: string,
|
|
6
|
+
// bucketName: string,
|
|
7
|
+
// objectKey: string,
|
|
8
|
+
// region: string,
|
|
9
9
|
// expirationMs: number,
|
|
10
10
|
// contentType: string | undefined,
|
|
11
11
|
// maxSizeBytes?: number | undefined
|
|
@@ -46,8 +46,8 @@ export const generatePresignedUploadUrl = async (bucketName, objectKey, region,
|
|
|
46
46
|
ContentType: contentType,
|
|
47
47
|
Metadata: {
|
|
48
48
|
...(correlationId && { 'correlation-id': correlationId }),
|
|
49
|
-
'upload-timestamp': Date.now().toString()
|
|
50
|
-
}
|
|
49
|
+
'upload-timestamp': Date.now().toString(),
|
|
50
|
+
},
|
|
51
51
|
});
|
|
52
52
|
const url = await getSignedUrl(s3Client, putObjectCommand, {
|
|
53
53
|
expiresIn: expirationMs / 1000,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "quidproquo-actionprocessor-awslambda",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./lib/commonjs/index.js",
|
|
6
6
|
"module": "./lib/esm/index.js",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
},
|
|
34
34
|
"homepage": "https://github.com/joe-coady/quidproquo#readme",
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@ai-sdk/amazon-bedrock": "^
|
|
36
|
+
"@ai-sdk/amazon-bedrock": "^5.0.11",
|
|
37
37
|
"@aws-sdk/client-acm": "^3.744.0",
|
|
38
38
|
"@aws-sdk/client-api-gateway": "^3.744.0",
|
|
39
39
|
"@aws-sdk/client-apigatewaymanagementapi": "^3.744.0",
|
|
@@ -59,10 +59,10 @@
|
|
|
59
59
|
"lodash": "^4.17.21",
|
|
60
60
|
"node-cache": "^5.1.2",
|
|
61
61
|
"node-match-path": "^0.6.3",
|
|
62
|
-
"quidproquo-actionprocessor-js": "0.1.
|
|
63
|
-
"quidproquo-config-aws": "0.1.
|
|
64
|
-
"quidproquo-core": "0.1.
|
|
65
|
-
"quidproquo-webserver": "0.1.
|
|
62
|
+
"quidproquo-actionprocessor-js": "0.1.2",
|
|
63
|
+
"quidproquo-config-aws": "0.1.2",
|
|
64
|
+
"quidproquo-core": "0.1.2",
|
|
65
|
+
"quidproquo-webserver": "0.1.2"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
68
|
"@types/busboy": "^1.5.0",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"@types/lodash": "^4.14.194",
|
|
71
71
|
"@types/node": "^22.13.13",
|
|
72
72
|
"esbuild": "^0.25.8",
|
|
73
|
-
"quidproquo-tsconfig": "0.1.
|
|
73
|
+
"quidproquo-tsconfig": "0.1.2",
|
|
74
74
|
"typescript": "^5.8.2"
|
|
75
75
|
}
|
|
76
76
|
}
|