@aj-archipelago/cortex 1.3.62 → 1.3.63
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/.github/workflows/cortex-file-handler-test.yml +61 -0
- package/README.md +31 -7
- package/config/default.example.json +15 -0
- package/config.js +133 -12
- package/helper-apps/cortex-autogen2/DigiCertGlobalRootCA.crt.pem +22 -0
- package/helper-apps/cortex-autogen2/Dockerfile +31 -0
- package/helper-apps/cortex-autogen2/Dockerfile.worker +41 -0
- package/helper-apps/cortex-autogen2/README.md +183 -0
- package/helper-apps/cortex-autogen2/__init__.py +1 -0
- package/helper-apps/cortex-autogen2/agents.py +131 -0
- package/helper-apps/cortex-autogen2/docker-compose.yml +20 -0
- package/helper-apps/cortex-autogen2/function_app.py +55 -0
- package/helper-apps/cortex-autogen2/host.json +15 -0
- package/helper-apps/cortex-autogen2/main.py +126 -0
- package/helper-apps/cortex-autogen2/poetry.lock +3652 -0
- package/helper-apps/cortex-autogen2/pyproject.toml +36 -0
- package/helper-apps/cortex-autogen2/requirements.txt +20 -0
- package/helper-apps/cortex-autogen2/send_task.py +105 -0
- package/helper-apps/cortex-autogen2/services/__init__.py +1 -0
- package/helper-apps/cortex-autogen2/services/azure_queue.py +85 -0
- package/helper-apps/cortex-autogen2/services/redis_publisher.py +153 -0
- package/helper-apps/cortex-autogen2/task_processor.py +488 -0
- package/helper-apps/cortex-autogen2/tools/__init__.py +24 -0
- package/helper-apps/cortex-autogen2/tools/azure_blob_tools.py +175 -0
- package/helper-apps/cortex-autogen2/tools/azure_foundry_agents.py +601 -0
- package/helper-apps/cortex-autogen2/tools/coding_tools.py +72 -0
- package/helper-apps/cortex-autogen2/tools/download_tools.py +48 -0
- package/helper-apps/cortex-autogen2/tools/file_tools.py +545 -0
- package/helper-apps/cortex-autogen2/tools/search_tools.py +646 -0
- package/helper-apps/cortex-azure-cleaner/README.md +36 -0
- package/helper-apps/cortex-file-converter/README.md +93 -0
- package/helper-apps/cortex-file-converter/key_to_pdf.py +104 -0
- package/helper-apps/cortex-file-converter/list_blob_extensions.py +89 -0
- package/helper-apps/cortex-file-converter/process_azure_keynotes.py +181 -0
- package/helper-apps/cortex-file-converter/requirements.txt +1 -0
- package/helper-apps/cortex-file-handler/.env.test.azure.ci +7 -0
- package/helper-apps/cortex-file-handler/.env.test.azure.sample +1 -1
- package/helper-apps/cortex-file-handler/.env.test.gcs.ci +10 -0
- package/helper-apps/cortex-file-handler/.env.test.gcs.sample +2 -2
- package/helper-apps/cortex-file-handler/INTERFACE.md +41 -0
- package/helper-apps/cortex-file-handler/package.json +1 -1
- package/helper-apps/cortex-file-handler/scripts/setup-azure-container.js +41 -17
- package/helper-apps/cortex-file-handler/scripts/setup-test-containers.js +30 -15
- package/helper-apps/cortex-file-handler/scripts/test-azure.sh +32 -6
- package/helper-apps/cortex-file-handler/scripts/test-gcs.sh +24 -2
- package/helper-apps/cortex-file-handler/scripts/validate-env.js +128 -0
- package/helper-apps/cortex-file-handler/src/blobHandler.js +161 -51
- package/helper-apps/cortex-file-handler/src/constants.js +3 -0
- package/helper-apps/cortex-file-handler/src/fileChunker.js +10 -8
- package/helper-apps/cortex-file-handler/src/index.js +116 -9
- package/helper-apps/cortex-file-handler/src/redis.js +61 -1
- package/helper-apps/cortex-file-handler/src/services/ConversionService.js +11 -8
- package/helper-apps/cortex-file-handler/src/services/FileConversionService.js +2 -2
- package/helper-apps/cortex-file-handler/src/services/storage/AzureStorageProvider.js +88 -6
- package/helper-apps/cortex-file-handler/src/services/storage/GCSStorageProvider.js +58 -0
- package/helper-apps/cortex-file-handler/src/services/storage/StorageFactory.js +25 -5
- package/helper-apps/cortex-file-handler/src/services/storage/StorageProvider.js +9 -0
- package/helper-apps/cortex-file-handler/src/services/storage/StorageService.js +120 -16
- package/helper-apps/cortex-file-handler/src/start.js +27 -17
- package/helper-apps/cortex-file-handler/tests/FileConversionService.test.js +52 -1
- package/helper-apps/cortex-file-handler/tests/blobHandler.test.js +40 -0
- package/helper-apps/cortex-file-handler/tests/checkHashShortLived.test.js +553 -0
- package/helper-apps/cortex-file-handler/tests/cleanup.test.js +46 -52
- package/helper-apps/cortex-file-handler/tests/containerConversionFlow.test.js +451 -0
- package/helper-apps/cortex-file-handler/tests/containerNameParsing.test.js +229 -0
- package/helper-apps/cortex-file-handler/tests/containerParameterFlow.test.js +392 -0
- package/helper-apps/cortex-file-handler/tests/conversionResilience.test.js +7 -2
- package/helper-apps/cortex-file-handler/tests/deleteOperations.test.js +348 -0
- package/helper-apps/cortex-file-handler/tests/fileChunker.test.js +23 -2
- package/helper-apps/cortex-file-handler/tests/fileUpload.test.js +11 -5
- package/helper-apps/cortex-file-handler/tests/getOperations.test.js +58 -24
- package/helper-apps/cortex-file-handler/tests/postOperations.test.js +11 -4
- package/helper-apps/cortex-file-handler/tests/shortLivedUrlConversion.test.js +225 -0
- package/helper-apps/cortex-file-handler/tests/start.test.js +8 -12
- package/helper-apps/cortex-file-handler/tests/storage/StorageFactory.test.js +80 -0
- package/helper-apps/cortex-file-handler/tests/storage/StorageService.test.js +388 -22
- package/helper-apps/cortex-file-handler/tests/testUtils.helper.js +74 -0
- package/lib/cortexResponse.js +153 -0
- package/lib/entityConstants.js +21 -3
- package/lib/logger.js +21 -4
- package/lib/pathwayTools.js +28 -9
- package/lib/util.js +49 -0
- package/package.json +1 -1
- package/pathways/basePathway.js +1 -0
- package/pathways/bing_afagent.js +54 -1
- package/pathways/call_tools.js +2 -3
- package/pathways/chat_jarvis.js +1 -1
- package/pathways/google_cse.js +27 -0
- package/pathways/grok_live_search.js +18 -0
- package/pathways/system/entity/memory/sys_memory_lookup_required.js +1 -0
- package/pathways/system/entity/memory/sys_memory_required.js +1 -0
- package/pathways/system/entity/memory/sys_search_memory.js +1 -0
- package/pathways/system/entity/sys_entity_agent.js +56 -4
- package/pathways/system/entity/sys_generator_quick.js +1 -0
- package/pathways/system/entity/tools/sys_tool_bing_search_afagent.js +26 -0
- package/pathways/system/entity/tools/sys_tool_google_search.js +141 -0
- package/pathways/system/entity/tools/sys_tool_grok_x_search.js +237 -0
- package/pathways/system/entity/tools/sys_tool_image.js +1 -1
- package/pathways/system/rest_streaming/sys_claude_37_sonnet.js +21 -0
- package/pathways/system/rest_streaming/sys_claude_41_opus.js +21 -0
- package/pathways/system/rest_streaming/sys_claude_4_sonnet.js +21 -0
- package/pathways/system/rest_streaming/sys_google_gemini_25_flash.js +25 -0
- package/pathways/system/rest_streaming/{sys_google_gemini_chat.js → sys_google_gemini_25_pro.js} +6 -4
- package/pathways/system/rest_streaming/sys_grok_4.js +23 -0
- package/pathways/system/rest_streaming/sys_grok_4_fast_non_reasoning.js +23 -0
- package/pathways/system/rest_streaming/sys_grok_4_fast_reasoning.js +23 -0
- package/pathways/system/rest_streaming/sys_openai_chat.js +3 -0
- package/pathways/system/rest_streaming/sys_openai_chat_gpt41.js +22 -0
- package/pathways/system/rest_streaming/sys_openai_chat_gpt41_mini.js +21 -0
- package/pathways/system/rest_streaming/sys_openai_chat_gpt41_nano.js +21 -0
- package/pathways/system/rest_streaming/{sys_claude_35_sonnet.js → sys_openai_chat_gpt4_omni.js} +6 -4
- package/pathways/system/rest_streaming/sys_openai_chat_gpt4_omni_mini.js +21 -0
- package/pathways/system/rest_streaming/{sys_claude_3_haiku.js → sys_openai_chat_gpt5.js} +7 -5
- package/pathways/system/rest_streaming/sys_openai_chat_gpt5_chat.js +21 -0
- package/pathways/system/rest_streaming/sys_openai_chat_gpt5_mini.js +21 -0
- package/pathways/system/rest_streaming/sys_openai_chat_gpt5_nano.js +21 -0
- package/pathways/system/rest_streaming/{sys_openai_chat_o1.js → sys_openai_chat_o3.js} +6 -3
- package/pathways/system/rest_streaming/sys_openai_chat_o3_mini.js +3 -0
- package/pathways/system/workspaces/run_workspace_prompt.js +99 -0
- package/pathways/vision.js +1 -1
- package/server/graphql.js +1 -1
- package/server/modelExecutor.js +8 -0
- package/server/pathwayResolver.js +166 -16
- package/server/pathwayResponseParser.js +16 -8
- package/server/plugins/azureFoundryAgentsPlugin.js +1 -1
- package/server/plugins/claude3VertexPlugin.js +193 -45
- package/server/plugins/gemini15ChatPlugin.js +21 -0
- package/server/plugins/gemini15VisionPlugin.js +360 -0
- package/server/plugins/googleCsePlugin.js +94 -0
- package/server/plugins/grokVisionPlugin.js +365 -0
- package/server/plugins/modelPlugin.js +3 -1
- package/server/plugins/openAiChatPlugin.js +106 -13
- package/server/plugins/openAiVisionPlugin.js +42 -30
- package/server/resolver.js +28 -4
- package/server/rest.js +270 -53
- package/server/typeDef.js +1 -0
- package/tests/{mocks.js → helpers/mocks.js} +5 -2
- package/tests/{server.js → helpers/server.js} +2 -2
- package/tests/helpers/sseAssert.js +23 -0
- package/tests/helpers/sseClient.js +73 -0
- package/tests/helpers/subscriptionAssert.js +11 -0
- package/tests/helpers/subscriptions.js +113 -0
- package/tests/{sublong.srt → integration/features/translate/sublong.srt} +4543 -4543
- package/tests/integration/features/translate/translate_chunking_stream.test.js +100 -0
- package/tests/{translate_srt.test.js → integration/features/translate/translate_srt.test.js} +2 -2
- package/tests/integration/graphql/async/stream/agentic.test.js +477 -0
- package/tests/integration/graphql/async/stream/subscription_streaming.test.js +62 -0
- package/tests/integration/graphql/async/stream/sys_entity_start_streaming.test.js +71 -0
- package/tests/integration/graphql/async/stream/vendors/claude_streaming.test.js +56 -0
- package/tests/integration/graphql/async/stream/vendors/gemini_streaming.test.js +66 -0
- package/tests/integration/graphql/async/stream/vendors/grok_streaming.test.js +56 -0
- package/tests/integration/graphql/async/stream/vendors/openai_streaming.test.js +72 -0
- package/tests/integration/graphql/features/google/sysToolGoogleSearch.test.js +96 -0
- package/tests/integration/graphql/features/grok/grok.test.js +688 -0
- package/tests/integration/graphql/features/grok/grok_x_search_tool.test.js +354 -0
- package/tests/{main.test.js → integration/graphql/features/main.test.js} +1 -1
- package/tests/{call_tools.test.js → integration/graphql/features/tools/call_tools.test.js} +2 -2
- package/tests/{vision.test.js → integration/graphql/features/vision/vision.test.js} +1 -1
- package/tests/integration/graphql/subscriptions/connection.test.js +26 -0
- package/tests/{openai_api.test.js → integration/rest/oai/openai_api.test.js} +63 -238
- package/tests/integration/rest/oai/tool_calling_api.test.js +343 -0
- package/tests/integration/rest/oai/tool_calling_streaming.test.js +85 -0
- package/tests/integration/rest/vendors/claude_streaming.test.js +47 -0
- package/tests/integration/rest/vendors/claude_tool_calling_streaming.test.js +75 -0
- package/tests/integration/rest/vendors/gemini_streaming.test.js +47 -0
- package/tests/integration/rest/vendors/gemini_tool_calling_streaming.test.js +75 -0
- package/tests/integration/rest/vendors/grok_streaming.test.js +55 -0
- package/tests/integration/rest/vendors/grok_tool_calling_streaming.test.js +75 -0
- package/tests/{azureAuthTokenHelper.test.js → unit/core/azureAuthTokenHelper.test.js} +1 -1
- package/tests/{chunkfunction.test.js → unit/core/chunkfunction.test.js} +2 -2
- package/tests/{config.test.js → unit/core/config.test.js} +3 -3
- package/tests/{encodeCache.test.js → unit/core/encodeCache.test.js} +1 -1
- package/tests/{fastLruCache.test.js → unit/core/fastLruCache.test.js} +1 -1
- package/tests/{handleBars.test.js → unit/core/handleBars.test.js} +1 -1
- package/tests/{memoryfunction.test.js → unit/core/memoryfunction.test.js} +2 -2
- package/tests/unit/core/mergeResolver.test.js +952 -0
- package/tests/{parser.test.js → unit/core/parser.test.js} +3 -3
- package/tests/unit/core/pathwayResolver.test.js +187 -0
- package/tests/{requestMonitor.test.js → unit/core/requestMonitor.test.js} +1 -1
- package/tests/{requestMonitorDurationEstimator.test.js → unit/core/requestMonitorDurationEstimator.test.js} +1 -1
- package/tests/{truncateMessages.test.js → unit/core/truncateMessages.test.js} +3 -3
- package/tests/{util.test.js → unit/core/util.test.js} +1 -1
- package/tests/{apptekTranslatePlugin.test.js → unit/plugins/apptekTranslatePlugin.test.js} +3 -3
- package/tests/{azureFoundryAgents.test.js → unit/plugins/azureFoundryAgents.test.js} +136 -1
- package/tests/{claude3VertexPlugin.test.js → unit/plugins/claude3VertexPlugin.test.js} +32 -10
- package/tests/{claude3VertexToolConversion.test.js → unit/plugins/claude3VertexToolConversion.test.js} +3 -3
- package/tests/unit/plugins/googleCsePlugin.test.js +111 -0
- package/tests/unit/plugins/grokVisionPlugin.test.js +1392 -0
- package/tests/{modelPlugin.test.js → unit/plugins/modelPlugin.test.js} +3 -3
- package/tests/{multimodal_conversion.test.js → unit/plugins/multimodal_conversion.test.js} +4 -4
- package/tests/{openAiChatPlugin.test.js → unit/plugins/openAiChatPlugin.test.js} +13 -4
- package/tests/{openAiToolPlugin.test.js → unit/plugins/openAiToolPlugin.test.js} +35 -27
- package/tests/{tokenHandlingTests.test.js → unit/plugins/tokenHandlingTests.test.js} +5 -5
- package/tests/{translate_apptek.test.js → unit/plugins/translate_apptek.test.js} +3 -3
- package/tests/{streaming.test.js → unit/plugins.streaming/plugin_stream_events.test.js} +19 -58
- package/helper-apps/mogrt-handler/tests/test-files/test.gif +0 -1
- package/helper-apps/mogrt-handler/tests/test-files/test.mogrt +0 -1
- package/helper-apps/mogrt-handler/tests/test-files/test.mp4 +0 -1
- package/pathways/system/rest_streaming/sys_openai_chat_gpt4.js +0 -19
- package/pathways/system/rest_streaming/sys_openai_chat_gpt4_32.js +0 -19
- package/pathways/system/rest_streaming/sys_openai_chat_gpt4_turbo.js +0 -19
- package/pathways/system/workspaces/run_claude35_sonnet.js +0 -21
- package/pathways/system/workspaces/run_claude3_haiku.js +0 -20
- package/pathways/system/workspaces/run_gpt35turbo.js +0 -20
- package/pathways/system/workspaces/run_gpt4.js +0 -20
- package/pathways/system/workspaces/run_gpt4_32.js +0 -20
- package/tests/agentic.test.js +0 -256
- package/tests/pathwayResolver.test.js +0 -78
- package/tests/subscription.test.js +0 -387
- /package/tests/{subchunk.srt → integration/features/translate/subchunk.srt} +0 -0
- /package/tests/{subhorizontal.srt → integration/features/translate/subhorizontal.srt} +0 -0
package/server/resolver.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { fulfillWithTimeout } from '../lib/promiser.js';
|
|
2
2
|
import { PathwayResolver } from './pathwayResolver.js';
|
|
3
|
+
import CortexResponse from '../lib/cortexResponse.js';
|
|
4
|
+
import { withRequestLoggingDisabled } from '../lib/logger.js';
|
|
3
5
|
|
|
4
6
|
// This resolver uses standard parameters required by Apollo server:
|
|
5
7
|
// (parent, args, contextValue, info)
|
|
@@ -19,18 +21,40 @@ const rootResolver = async (parent, args, contextValue, info) => {
|
|
|
19
21
|
let result = null;
|
|
20
22
|
|
|
21
23
|
try {
|
|
22
|
-
|
|
24
|
+
const execWithTimeout = () => fulfillWithTimeout(pathway.resolver(parent, args, contextValue, info), pathway.timeout);
|
|
25
|
+
if (pathway.requestLoggingDisabled === true) {
|
|
26
|
+
result = await withRequestLoggingDisabled(() => execWithTimeout());
|
|
27
|
+
} else {
|
|
28
|
+
result = await execWithTimeout();
|
|
29
|
+
}
|
|
23
30
|
} catch (error) {
|
|
24
31
|
pathwayResolver.logError(error);
|
|
25
32
|
result = error.message || error.toString();
|
|
26
33
|
}
|
|
34
|
+
|
|
35
|
+
if (result instanceof CortexResponse) {
|
|
36
|
+
// Use the smart mergeResultData method that handles CortexResponse objects
|
|
37
|
+
pathwayResolver.pathwayResultData = pathwayResolver.mergeResultData(result);
|
|
38
|
+
result = result.output_text;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let resultData = pathwayResolver.pathwayResultData ? JSON.stringify(pathwayResolver.pathwayResultData) : null;
|
|
27
42
|
|
|
28
|
-
const { warnings, errors, previousResult, savedContextId, tool } = pathwayResolver;
|
|
29
|
-
|
|
43
|
+
const { warnings, errors, previousResult, savedContextId, tool } = pathwayResolver;
|
|
44
|
+
|
|
30
45
|
// Add request parameters back as debug
|
|
31
46
|
const debug = pathwayResolver.prompts.map(prompt => prompt.debugInfo || '').join('\n').trim();
|
|
32
47
|
|
|
33
|
-
return {
|
|
48
|
+
return {
|
|
49
|
+
debug,
|
|
50
|
+
result,
|
|
51
|
+
resultData,
|
|
52
|
+
warnings,
|
|
53
|
+
errors,
|
|
54
|
+
previousResult,
|
|
55
|
+
tool,
|
|
56
|
+
contextId: savedContextId
|
|
57
|
+
}
|
|
34
58
|
}
|
|
35
59
|
|
|
36
60
|
// This resolver is used by the root resolver to process the request
|
package/server/rest.js
CHANGED
|
@@ -36,6 +36,101 @@ const chunkTextIntoTokens = (() => {
|
|
|
36
36
|
};
|
|
37
37
|
})();
|
|
38
38
|
|
|
39
|
+
// Helper functions to reduce code duplication
|
|
40
|
+
const resolveModelName = (modelName, openAIChatModels, openAICompletionModels, isChat = false) => {
|
|
41
|
+
if (modelName.startsWith('ollama-')) {
|
|
42
|
+
const pathwayName = isChat ? 'sys_ollama_chat' : 'sys_ollama_completion';
|
|
43
|
+
return { pathwayName, isOllama: true };
|
|
44
|
+
} else {
|
|
45
|
+
const modelMap = isChat ? openAIChatModels : openAICompletionModels;
|
|
46
|
+
const pathwayName = modelMap[modelName] || modelMap['*'];
|
|
47
|
+
return { pathwayName, isOllama: false };
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const handleModelNotFound = (res, modelName) => {
|
|
52
|
+
res.status(404).json({
|
|
53
|
+
error: `Model ${modelName} not found.`,
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const extractResponseData = (pathwayResponse) => {
|
|
58
|
+
if (typeof pathwayResponse === 'string') {
|
|
59
|
+
return { resultText: pathwayResponse, resultData: null };
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
resultText: pathwayResponse.result || "",
|
|
63
|
+
resultData: pathwayResponse.resultData || null
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const parseToolCalls = (resultData, resultText) => {
|
|
68
|
+
let messageContent = resultText;
|
|
69
|
+
let toolCalls = null;
|
|
70
|
+
let functionCall = null;
|
|
71
|
+
let finishReason = 'stop';
|
|
72
|
+
|
|
73
|
+
// First check if we have structured response data from the pathway response
|
|
74
|
+
if (resultData) {
|
|
75
|
+
try {
|
|
76
|
+
const parsedResultData = typeof resultData === 'string' ? JSON.parse(resultData) : resultData;
|
|
77
|
+
|
|
78
|
+
// resultData contains the full CortexResponse object
|
|
79
|
+
if (parsedResultData && parsedResultData.toolCalls) {
|
|
80
|
+
toolCalls = parsedResultData.toolCalls;
|
|
81
|
+
finishReason = 'tool_calls';
|
|
82
|
+
} else if (parsedResultData && parsedResultData.functionCall) {
|
|
83
|
+
functionCall = parsedResultData.functionCall;
|
|
84
|
+
finishReason = 'function_call';
|
|
85
|
+
}
|
|
86
|
+
} catch (e) {
|
|
87
|
+
// If parsing structured response fails, continue with regular parsing
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// If no tool data found, try parsing the result text as before for backward compatibility
|
|
92
|
+
if (!toolCalls && !functionCall) {
|
|
93
|
+
try {
|
|
94
|
+
const parsedResponse = JSON.parse(resultText);
|
|
95
|
+
|
|
96
|
+
// Check if this is a tool calls response
|
|
97
|
+
if (parsedResponse.role === 'assistant' && parsedResponse.hasOwnProperty('tool_calls')) {
|
|
98
|
+
if (parsedResponse.tool_calls) {
|
|
99
|
+
toolCalls = parsedResponse.tool_calls;
|
|
100
|
+
messageContent = parsedResponse.content || "";
|
|
101
|
+
finishReason = 'tool_calls';
|
|
102
|
+
}
|
|
103
|
+
} else if (parsedResponse.tool_calls) {
|
|
104
|
+
toolCalls = parsedResponse.tool_calls;
|
|
105
|
+
messageContent = parsedResponse.content || "";
|
|
106
|
+
finishReason = 'tool_calls';
|
|
107
|
+
}
|
|
108
|
+
// Check if this is a legacy function call response
|
|
109
|
+
else if (parsedResponse.role === 'assistant' && parsedResponse.hasOwnProperty('function_call')) {
|
|
110
|
+
if (parsedResponse.function_call) {
|
|
111
|
+
functionCall = parsedResponse.function_call;
|
|
112
|
+
messageContent = parsedResponse.content || "";
|
|
113
|
+
finishReason = 'function_call';
|
|
114
|
+
}
|
|
115
|
+
} else if (parsedResponse.function_call) {
|
|
116
|
+
functionCall = parsedResponse.function_call;
|
|
117
|
+
messageContent = parsedResponse.content || "";
|
|
118
|
+
finishReason = 'function_call';
|
|
119
|
+
}
|
|
120
|
+
} catch (e) {
|
|
121
|
+
// If parsing fails, treat as regular text response
|
|
122
|
+
messageContent = resultText;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return { messageContent, toolCalls, functionCall, finishReason };
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const generateResponseId = (prefix) => {
|
|
130
|
+
const requestId = uuidv4();
|
|
131
|
+
return `${prefix}-${requestId}`;
|
|
132
|
+
};
|
|
133
|
+
|
|
39
134
|
const processRestRequest = async (server, req, pathway, name, parameterMap = {}) => {
|
|
40
135
|
const fieldVariableDefs = pathway.typeDef(pathway).restDefinition || [];
|
|
41
136
|
|
|
@@ -51,6 +146,8 @@ const processRestRequest = async (server, req, pathway, name, parameterMap = {})
|
|
|
51
146
|
msg.content.map(item => JSON.stringify(item)) :
|
|
52
147
|
msg.content
|
|
53
148
|
}));
|
|
149
|
+
} else if (type === '[String]' && Array.isArray(value)) {
|
|
150
|
+
return value;
|
|
54
151
|
} else {
|
|
55
152
|
return value;
|
|
56
153
|
}
|
|
@@ -67,6 +164,20 @@ const processRestRequest = async (server, req, pathway, name, parameterMap = {})
|
|
|
67
164
|
return acc;
|
|
68
165
|
}, {});
|
|
69
166
|
|
|
167
|
+
// Add tools to variables if they exist in the request
|
|
168
|
+
if (req.body.tools) {
|
|
169
|
+
variables.tools = JSON.stringify(req.body.tools);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (req.body.tool_choice) {
|
|
173
|
+
variables.tool_choice = typeof req.body.tool_choice === 'string' ? req.body.tool_choice : JSON.stringify(req.body.tool_choice);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Add functions to variables if they exist in the request (legacy function calling)
|
|
177
|
+
if (req.body.functions) {
|
|
178
|
+
variables.functions = JSON.stringify(req.body.functions);
|
|
179
|
+
}
|
|
180
|
+
|
|
70
181
|
const variableParams = fieldVariableDefs.map(({ name, type }) => `$${name}: ${type}`).join(', ');
|
|
71
182
|
const queryArgs = fieldVariableDefs.map(({ name }) => `${name}: $${name}`).join(', ');
|
|
72
183
|
|
|
@@ -76,10 +187,19 @@ const processRestRequest = async (server, req, pathway, name, parameterMap = {})
|
|
|
76
187
|
contextId
|
|
77
188
|
previousResult
|
|
78
189
|
result
|
|
190
|
+
resultData
|
|
191
|
+
tool
|
|
192
|
+
warnings
|
|
193
|
+
errors
|
|
194
|
+
debug
|
|
79
195
|
}
|
|
80
196
|
}
|
|
81
197
|
`;
|
|
82
198
|
|
|
199
|
+
// Debug: Log the variables being passed
|
|
200
|
+
logger.debug(`REST endpoint variables: ${JSON.stringify(variables, null, 2)}`);
|
|
201
|
+
logger.debug(`REST endpoint query: ${query}`);
|
|
202
|
+
|
|
83
203
|
const result = await server.executeOperation({ query, variables });
|
|
84
204
|
|
|
85
205
|
// if we're streaming and there are errors, we return a standard error code
|
|
@@ -89,9 +209,27 @@ const processRestRequest = async (server, req, pathway, name, parameterMap = {})
|
|
|
89
209
|
}
|
|
90
210
|
}
|
|
91
211
|
|
|
92
|
-
//
|
|
93
|
-
const
|
|
94
|
-
|
|
212
|
+
// For non-streaming, return both result and tool fields
|
|
213
|
+
const pathwayData = result?.body?.singleResult?.data?.[name];
|
|
214
|
+
if (pathwayData) {
|
|
215
|
+
return {
|
|
216
|
+
result: pathwayData.result || "",
|
|
217
|
+
resultData: pathwayData.resultData || null,
|
|
218
|
+
tool: pathwayData.tool || null,
|
|
219
|
+
errors: pathwayData.errors || null,
|
|
220
|
+
warnings: pathwayData.warnings || null
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// If no pathway data, return error message
|
|
225
|
+
const errorMessage = result?.body?.singleResult?.errors?.[0]?.message || "";
|
|
226
|
+
return {
|
|
227
|
+
result: errorMessage,
|
|
228
|
+
resultData: null,
|
|
229
|
+
tool: null,
|
|
230
|
+
errors: errorMessage ? [errorMessage] : null,
|
|
231
|
+
warnings: null
|
|
232
|
+
};
|
|
95
233
|
};
|
|
96
234
|
|
|
97
235
|
const processIncomingStream = (requestId, res, jsonResponse, pathway) => {
|
|
@@ -150,12 +288,30 @@ const processIncomingStream = (requestId, res, jsonResponse, pathway) => {
|
|
|
150
288
|
if (jsonResponse.object === 'text_completion') {
|
|
151
289
|
jsonResponse.choices[0].text = inputText;
|
|
152
290
|
} else {
|
|
291
|
+
// Ensure delta object exists
|
|
292
|
+
if (!jsonResponse.choices[0].delta) {
|
|
293
|
+
jsonResponse.choices[0].delta = {};
|
|
294
|
+
}
|
|
153
295
|
jsonResponse.choices[0].delta.content = inputText;
|
|
154
296
|
}
|
|
155
297
|
|
|
156
298
|
return jsonResponse;
|
|
157
299
|
}
|
|
158
300
|
|
|
301
|
+
const fillJsonResponseWithToolCalls = (jsonResponse, toolCalls, finishReason) => {
|
|
302
|
+
jsonResponse.choices[0].finish_reason = finishReason;
|
|
303
|
+
if (jsonResponse.object === 'text_completion') {
|
|
304
|
+
// Handle text completion tool calls if needed
|
|
305
|
+
} else {
|
|
306
|
+
// Ensure delta object exists
|
|
307
|
+
if (!jsonResponse.choices[0].delta) {
|
|
308
|
+
jsonResponse.choices[0].delta = {};
|
|
309
|
+
}
|
|
310
|
+
jsonResponse.choices[0].delta.tool_calls = toolCalls;
|
|
311
|
+
}
|
|
312
|
+
return jsonResponse;
|
|
313
|
+
}
|
|
314
|
+
|
|
159
315
|
startStream(res);
|
|
160
316
|
|
|
161
317
|
// If the requestId is an error message, we can't continue
|
|
@@ -191,6 +347,21 @@ const processIncomingStream = (requestId, res, jsonResponse, pathway) => {
|
|
|
191
347
|
return;
|
|
192
348
|
}
|
|
193
349
|
|
|
350
|
+
// Check if this is a tool call response
|
|
351
|
+
try {
|
|
352
|
+
const parsedData = JSON.parse(stringData);
|
|
353
|
+
if (parsedData.tool_calls) {
|
|
354
|
+
// Send tool calls as a single chunk
|
|
355
|
+
fillJsonResponseWithToolCalls(jsonResponse, parsedData.tool_calls, "tool_calls");
|
|
356
|
+
sendStreamData(jsonResponse);
|
|
357
|
+
safeUnsubscribe();
|
|
358
|
+
finishStream(res, jsonResponse);
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
} catch (e) {
|
|
362
|
+
// Not JSON, treat as regular text
|
|
363
|
+
}
|
|
364
|
+
|
|
194
365
|
chunkTextIntoTokens(stringData, false, useSingleTokenStream).forEach(token => {
|
|
195
366
|
fillJsonResponse(jsonResponse, token, null);
|
|
196
367
|
sendStreamData(jsonResponse);
|
|
@@ -224,6 +395,67 @@ const processIncomingStream = (requestId, res, jsonResponse, pathway) => {
|
|
|
224
395
|
return;
|
|
225
396
|
}
|
|
226
397
|
|
|
398
|
+
// Check if this is a streaming event with tool calls
|
|
399
|
+
if (messageJson.choices && messageJson.choices[0] && messageJson.choices[0].delta) {
|
|
400
|
+
const delta = messageJson.choices[0].delta;
|
|
401
|
+
const finishReason = messageJson.choices[0].finish_reason;
|
|
402
|
+
|
|
403
|
+
// Handle tool calls in streaming events
|
|
404
|
+
if (delta.tool_calls) {
|
|
405
|
+
fillJsonResponseWithToolCalls(jsonResponse, delta.tool_calls, finishReason || "tool_calls");
|
|
406
|
+
sendStreamData(jsonResponse);
|
|
407
|
+
|
|
408
|
+
if (finishReason === "tool_calls" || progress === 1) {
|
|
409
|
+
safeUnsubscribe();
|
|
410
|
+
finishStream(res, jsonResponse);
|
|
411
|
+
}
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Handle the case where we get an empty delta with finish_reason: "tool_calls"
|
|
416
|
+
if (finishReason === "tool_calls" && Object.keys(delta).length === 0) {
|
|
417
|
+
|
|
418
|
+
safeUnsubscribe();
|
|
419
|
+
finishStream(res, jsonResponse);
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Handle function calls in streaming events
|
|
424
|
+
if (delta.function_call) {
|
|
425
|
+
// Ensure delta object exists
|
|
426
|
+
if (!jsonResponse.choices[0].delta) {
|
|
427
|
+
jsonResponse.choices[0].delta = {};
|
|
428
|
+
}
|
|
429
|
+
jsonResponse.choices[0].delta.function_call = delta.function_call;
|
|
430
|
+
jsonResponse.choices[0].finish_reason = finishReason || "function_call";
|
|
431
|
+
sendStreamData(jsonResponse);
|
|
432
|
+
|
|
433
|
+
if (finishReason === "function_call") {
|
|
434
|
+
safeUnsubscribe();
|
|
435
|
+
finishStream(res, jsonResponse);
|
|
436
|
+
}
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Handle regular content in streaming events
|
|
441
|
+
if (delta.content !== undefined) {
|
|
442
|
+
if (delta.content === null) {
|
|
443
|
+
// Skip null content chunks
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
chunkTextIntoTokens(delta.content, false, useSingleTokenStream).forEach(token => {
|
|
447
|
+
fillJsonResponse(jsonResponse, token, null);
|
|
448
|
+
sendStreamData(jsonResponse);
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
if (finishReason === "stop") {
|
|
452
|
+
safeUnsubscribe();
|
|
453
|
+
finishStream(res, jsonResponse);
|
|
454
|
+
}
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
227
459
|
let content = '';
|
|
228
460
|
if (messageJson.choices) {
|
|
229
461
|
const { text, delta } = messageJson.choices[0];
|
|
@@ -232,6 +464,13 @@ const processIncomingStream = (requestId, res, jsonResponse, pathway) => {
|
|
|
232
464
|
content = messageJson.candidates[0].content.parts[0].text;
|
|
233
465
|
} else if (messageJson.content) {
|
|
234
466
|
content = messageJson.content?.[0]?.text || '';
|
|
467
|
+
} else if (messageJson.tool_calls) {
|
|
468
|
+
// Handle tool calls in streaming
|
|
469
|
+
fillJsonResponseWithToolCalls(jsonResponse, messageJson.tool_calls, "tool_calls");
|
|
470
|
+
sendStreamData(jsonResponse);
|
|
471
|
+
safeUnsubscribe();
|
|
472
|
+
finishStream(res, jsonResponse);
|
|
473
|
+
return;
|
|
235
474
|
} else {
|
|
236
475
|
content = messageJson;
|
|
237
476
|
}
|
|
@@ -289,7 +528,8 @@ function buildRestEndpoints(pathways, app, server, config) {
|
|
|
289
528
|
}
|
|
290
529
|
} else {
|
|
291
530
|
app.post(`/rest/${name}`, async (req, res) => {
|
|
292
|
-
const
|
|
531
|
+
const pathwayResponse = await processRestRequest(server, req, pathway, name);
|
|
532
|
+
const { resultText } = extractResponseData(pathwayResponse);
|
|
293
533
|
res.send(resultText);
|
|
294
534
|
});
|
|
295
535
|
}
|
|
@@ -298,29 +538,21 @@ function buildRestEndpoints(pathways, app, server, config) {
|
|
|
298
538
|
// Create OpenAI compatible endpoints
|
|
299
539
|
app.post('/v1/completions', async (req, res) => {
|
|
300
540
|
const modelName = req.body.model || 'gpt-3.5-turbo';
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
if (modelName.startsWith('ollama-')) {
|
|
304
|
-
pathwayName = 'sys_ollama_completion';
|
|
305
|
-
req.body.ollamaModel = modelName.replace('ollama-', '');
|
|
306
|
-
} else {
|
|
307
|
-
pathwayName = openAICompletionModels[modelName] || openAICompletionModels['*'];
|
|
308
|
-
}
|
|
541
|
+
const { pathwayName, isOllama } = resolveModelName(modelName, openAIChatModels, openAICompletionModels, false);
|
|
309
542
|
|
|
310
543
|
if (!pathwayName) {
|
|
311
|
-
res
|
|
312
|
-
error: `Model ${modelName} not found.`,
|
|
313
|
-
});
|
|
544
|
+
handleModelNotFound(res, modelName);
|
|
314
545
|
return;
|
|
315
546
|
}
|
|
316
547
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
text: 'prompt'
|
|
321
|
-
};
|
|
548
|
+
if (isOllama) {
|
|
549
|
+
req.body.ollamaModel = modelName.replace('ollama-', '');
|
|
550
|
+
}
|
|
322
551
|
|
|
323
|
-
const
|
|
552
|
+
const pathway = pathways[pathwayName];
|
|
553
|
+
const parameterMap = { text: 'prompt' };
|
|
554
|
+
const pathwayResponse = await processRestRequest(server, req, pathway, pathwayName, parameterMap);
|
|
555
|
+
const { resultText } = extractResponseData(pathwayResponse);
|
|
324
556
|
|
|
325
557
|
const jsonResponse = {
|
|
326
558
|
id: `cmpl`,
|
|
@@ -341,50 +573,46 @@ function buildRestEndpoints(pathways, app, server, config) {
|
|
|
341
573
|
if (Boolean(req.body.stream)) {
|
|
342
574
|
jsonResponse.id = `cmpl-${resultText}`;
|
|
343
575
|
jsonResponse.choices[0].finish_reason = null;
|
|
344
|
-
|
|
345
576
|
processIncomingStream(resultText, res, jsonResponse, pathway);
|
|
346
577
|
} else {
|
|
347
|
-
|
|
348
|
-
jsonResponse.id = `cmpl-${requestId}`;
|
|
578
|
+
jsonResponse.id = generateResponseId('cmpl');
|
|
349
579
|
res.json(jsonResponse);
|
|
350
580
|
}
|
|
351
581
|
});
|
|
352
582
|
|
|
353
583
|
app.post('/v1/chat/completions', async (req, res) => {
|
|
354
584
|
const modelName = req.body.model || 'gpt-3.5-turbo';
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
if (modelName.startsWith('ollama-')) {
|
|
358
|
-
pathwayName = 'sys_ollama_chat';
|
|
359
|
-
req.body.ollamaModel = modelName.replace('ollama-', '');
|
|
360
|
-
} else {
|
|
361
|
-
pathwayName = openAIChatModels[modelName] || openAIChatModels['*'];
|
|
362
|
-
}
|
|
585
|
+
const { pathwayName, isOllama } = resolveModelName(modelName, openAIChatModels, openAICompletionModels, true);
|
|
363
586
|
|
|
364
587
|
if (!pathwayName) {
|
|
365
|
-
res
|
|
366
|
-
error: `Model ${modelName} not found.`,
|
|
367
|
-
});
|
|
588
|
+
handleModelNotFound(res, modelName);
|
|
368
589
|
return;
|
|
369
590
|
}
|
|
370
591
|
|
|
371
|
-
|
|
592
|
+
if (isOllama) {
|
|
593
|
+
req.body.ollamaModel = modelName.replace('ollama-', '');
|
|
594
|
+
}
|
|
372
595
|
|
|
373
|
-
const
|
|
596
|
+
const pathway = pathways[pathwayName];
|
|
597
|
+
const pathwayResponse = await processRestRequest(server, req, pathway, pathwayName);
|
|
598
|
+
const { resultText, resultData } = extractResponseData(pathwayResponse);
|
|
599
|
+
const { messageContent, toolCalls, functionCall, finishReason } = parseToolCalls(resultData, resultText);
|
|
374
600
|
|
|
375
601
|
const jsonResponse = {
|
|
376
602
|
id: `chatcmpl`,
|
|
377
|
-
object: "chat.completion",
|
|
603
|
+
object: Boolean(req.body.stream) ? "chat.completion.chunk" : "chat.completion",
|
|
378
604
|
created: Date.now(),
|
|
379
605
|
model: req.body.model,
|
|
380
606
|
choices: [
|
|
381
607
|
{
|
|
382
608
|
message: {
|
|
383
609
|
role: "assistant",
|
|
384
|
-
content:
|
|
610
|
+
content: messageContent,
|
|
611
|
+
...(toolCalls && { tool_calls: toolCalls }),
|
|
612
|
+
...(functionCall && { function_call: functionCall })
|
|
385
613
|
},
|
|
386
614
|
index: 0,
|
|
387
|
-
finish_reason:
|
|
615
|
+
finish_reason: finishReason
|
|
388
616
|
}
|
|
389
617
|
],
|
|
390
618
|
};
|
|
@@ -392,23 +620,12 @@ function buildRestEndpoints(pathways, app, server, config) {
|
|
|
392
620
|
// eslint-disable-next-line no-extra-boolean-cast
|
|
393
621
|
if (Boolean(req.body.stream)) {
|
|
394
622
|
jsonResponse.id = `chatcmpl-${resultText}`;
|
|
395
|
-
jsonResponse.choices[0] =
|
|
396
|
-
delta: {
|
|
397
|
-
role: "assistant",
|
|
398
|
-
content: resultText
|
|
399
|
-
},
|
|
400
|
-
finish_reason: null
|
|
401
|
-
}
|
|
402
|
-
jsonResponse.object = "chat.completion.chunk";
|
|
403
|
-
|
|
623
|
+
jsonResponse.choices[0].finish_reason = null;
|
|
404
624
|
processIncomingStream(resultText, res, jsonResponse, pathway);
|
|
405
625
|
} else {
|
|
406
|
-
|
|
407
|
-
jsonResponse.id = `chatcmpl-${requestId}`;
|
|
408
|
-
|
|
626
|
+
jsonResponse.id = generateResponseId('chatcmpl');
|
|
409
627
|
res.json(jsonResponse);
|
|
410
628
|
}
|
|
411
|
-
|
|
412
629
|
});
|
|
413
630
|
|
|
414
631
|
app.get('/v1/models', async (req, res) => {
|
package/server/typeDef.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Prompt } from '
|
|
1
|
+
import { Prompt } from '../../server/prompt.js';
|
|
2
2
|
|
|
3
3
|
export const mockConfig = {
|
|
4
4
|
get: (key) => {
|
|
@@ -92,4 +92,7 @@ export const mockConfig = {
|
|
|
92
92
|
}),
|
|
93
93
|
};
|
|
94
94
|
|
|
95
|
-
export const mockModelEndpoints = {
|
|
95
|
+
export const mockModelEndpoints = {
|
|
96
|
+
testModel: { name: 'testModel', url: 'https://api.example.com/testModel', type: 'OPENAI-COMPLETION' },
|
|
97
|
+
anotherModel: { name: 'anotherModel', url: 'https://api.example.com/anotherModel', type: 'OPENAI-CHAT' }
|
|
98
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import 'dotenv/config'
|
|
2
2
|
import { ApolloServer } from '@apollo/server';
|
|
3
|
-
import { config } from '
|
|
4
|
-
import typeDefsresolversFactory from '
|
|
3
|
+
import { config } from '../../config.js';
|
|
4
|
+
import typeDefsresolversFactory from '../../index.js';
|
|
5
5
|
|
|
6
6
|
let typeDefs;
|
|
7
7
|
let resolvers;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { connectToSSEEndpoint } from './sseClient.js';
|
|
2
|
+
|
|
3
|
+
export async function collectSSEChunks(baseUrl, endpoint, payload) {
|
|
4
|
+
const chunks = [];
|
|
5
|
+
await connectToSSEEndpoint(baseUrl, endpoint, payload, (chunk) => {
|
|
6
|
+
chunks.push(chunk);
|
|
7
|
+
});
|
|
8
|
+
return chunks;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function assertOAIChatChunkBasics(t, chunk) {
|
|
12
|
+
t.truthy(chunk.id);
|
|
13
|
+
t.is(chunk.object, 'chat.completion.chunk');
|
|
14
|
+
t.truthy(chunk.choices && chunk.choices[0]);
|
|
15
|
+
const choice = chunk.choices[0];
|
|
16
|
+
t.true('delta' in choice);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function assertAnyContentDelta(chunks) {
|
|
20
|
+
return chunks.some(c => c?.choices?.[0]?.delta?.content);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
|
|
3
|
+
// Connects to an SSE endpoint and resolves when [DONE] is received.
|
|
4
|
+
// Calls onEvent for each parsed SSE message JSON.
|
|
5
|
+
export async function connectToSSEEndpoint(baseUrl, endpoint, payload, onEvent) {
|
|
6
|
+
return new Promise(async (resolve, reject) => {
|
|
7
|
+
let sawDone = false;
|
|
8
|
+
const timeout = setTimeout(() => {
|
|
9
|
+
reject(new Error('SSE timeout waiting for [DONE]'));
|
|
10
|
+
}, 8000); // 8 second timeout
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const instance = axios.create({
|
|
14
|
+
baseURL: baseUrl,
|
|
15
|
+
responseType: 'stream',
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const response = await instance.post(endpoint, payload);
|
|
19
|
+
const responseData = response.data;
|
|
20
|
+
|
|
21
|
+
const incomingMessage = Array.isArray(responseData) && responseData.length > 0
|
|
22
|
+
? responseData[0]
|
|
23
|
+
: responseData;
|
|
24
|
+
|
|
25
|
+
let eventCount = 0;
|
|
26
|
+
|
|
27
|
+
incomingMessage.on('data', data => {
|
|
28
|
+
const events = data.toString().split('\n');
|
|
29
|
+
|
|
30
|
+
events.forEach(event => {
|
|
31
|
+
if (event.trim() === '') return;
|
|
32
|
+
|
|
33
|
+
eventCount++;
|
|
34
|
+
|
|
35
|
+
if (event.trim() === 'data: [DONE]') {
|
|
36
|
+
sawDone = true;
|
|
37
|
+
clearTimeout(timeout);
|
|
38
|
+
resolve();
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const message = event.replace(/^data: /, '');
|
|
43
|
+
try {
|
|
44
|
+
const messageJson = JSON.parse(message);
|
|
45
|
+
onEvent && onEvent(messageJson);
|
|
46
|
+
} catch (_err) {
|
|
47
|
+
// ignore lines that are not JSON
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// If the underlying stream ends without a [DONE], treat as failure
|
|
53
|
+
incomingMessage.on('end', () => {
|
|
54
|
+
if (!sawDone) {
|
|
55
|
+
clearTimeout(timeout);
|
|
56
|
+
reject(new Error('SSE stream ended without [DONE]'));
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
incomingMessage.on('close', () => {
|
|
61
|
+
if (!sawDone) {
|
|
62
|
+
clearTimeout(timeout);
|
|
63
|
+
reject(new Error('SSE stream closed without [DONE]'));
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
} catch (error) {
|
|
67
|
+
clearTimeout(timeout);
|
|
68
|
+
reject(error);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function assertOAIStyleDeltaMessage(t, message) {
|
|
2
|
+
const progress = message?.data?.requestProgress;
|
|
3
|
+
t.truthy(progress);
|
|
4
|
+
if (progress.data) {
|
|
5
|
+
// Should be string of serialized JSON or plain text
|
|
6
|
+
const text = JSON.parse(progress.data);
|
|
7
|
+
t.true(typeof text === 'string' || typeof text === 'object');
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
|