@uncensoredcode/openbridge 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +117 -0
- package/bin/openbridge.js +10 -0
- package/package.json +85 -0
- package/packages/cli/dist/args.d.ts +30 -0
- package/packages/cli/dist/args.js +160 -0
- package/packages/cli/dist/cli.d.ts +2 -0
- package/packages/cli/dist/cli.js +9 -0
- package/packages/cli/dist/index.d.ts +26 -0
- package/packages/cli/dist/index.js +76 -0
- package/packages/runtime/dist/assistant-protocol.d.ts +34 -0
- package/packages/runtime/dist/assistant-protocol.js +121 -0
- package/packages/runtime/dist/execution/in-process.d.ts +14 -0
- package/packages/runtime/dist/execution/in-process.js +45 -0
- package/packages/runtime/dist/execution/types.d.ts +49 -0
- package/packages/runtime/dist/execution/types.js +20 -0
- package/packages/runtime/dist/index.d.ts +86 -0
- package/packages/runtime/dist/index.js +60 -0
- package/packages/runtime/dist/normalizers/index.d.ts +6 -0
- package/packages/runtime/dist/normalizers/index.js +12 -0
- package/packages/runtime/dist/normalizers/legacy-packet.d.ts +6 -0
- package/packages/runtime/dist/normalizers/legacy-packet.js +131 -0
- package/packages/runtime/dist/output-sanitizer.d.ts +23 -0
- package/packages/runtime/dist/output-sanitizer.js +78 -0
- package/packages/runtime/dist/packet-extractor.d.ts +17 -0
- package/packages/runtime/dist/packet-extractor.js +43 -0
- package/packages/runtime/dist/packet-normalizer.d.ts +21 -0
- package/packages/runtime/dist/packet-normalizer.js +47 -0
- package/packages/runtime/dist/prompt-compiler.d.ts +28 -0
- package/packages/runtime/dist/prompt-compiler.js +301 -0
- package/packages/runtime/dist/protocol.d.ts +44 -0
- package/packages/runtime/dist/protocol.js +165 -0
- package/packages/runtime/dist/provider-failure.d.ts +52 -0
- package/packages/runtime/dist/provider-failure.js +236 -0
- package/packages/runtime/dist/provider.d.ts +40 -0
- package/packages/runtime/dist/provider.js +1 -0
- package/packages/runtime/dist/runtime.d.ts +86 -0
- package/packages/runtime/dist/runtime.js +462 -0
- package/packages/runtime/dist/session-bound-provider.d.ts +52 -0
- package/packages/runtime/dist/session-bound-provider.js +366 -0
- package/packages/runtime/dist/tool-name-aliases.d.ts +5 -0
- package/packages/runtime/dist/tool-name-aliases.js +13 -0
- package/packages/runtime/dist/tools/bash.d.ts +9 -0
- package/packages/runtime/dist/tools/bash.js +157 -0
- package/packages/runtime/dist/tools/edit.d.ts +9 -0
- package/packages/runtime/dist/tools/edit.js +94 -0
- package/packages/runtime/dist/tools/index.d.ts +39 -0
- package/packages/runtime/dist/tools/index.js +27 -0
- package/packages/runtime/dist/tools/list-dir.d.ts +9 -0
- package/packages/runtime/dist/tools/list-dir.js +127 -0
- package/packages/runtime/dist/tools/read.d.ts +9 -0
- package/packages/runtime/dist/tools/read.js +56 -0
- package/packages/runtime/dist/tools/registry.d.ts +15 -0
- package/packages/runtime/dist/tools/registry.js +38 -0
- package/packages/runtime/dist/tools/runtime-path.d.ts +7 -0
- package/packages/runtime/dist/tools/runtime-path.js +22 -0
- package/packages/runtime/dist/tools/search-files.d.ts +9 -0
- package/packages/runtime/dist/tools/search-files.js +149 -0
- package/packages/runtime/dist/tools/text-file.d.ts +32 -0
- package/packages/runtime/dist/tools/text-file.js +101 -0
- package/packages/runtime/dist/tools/workspace-path.d.ts +17 -0
- package/packages/runtime/dist/tools/workspace-path.js +70 -0
- package/packages/runtime/dist/tools/write.d.ts +9 -0
- package/packages/runtime/dist/tools/write.js +59 -0
- package/packages/server/dist/bridge/bridge-model-catalog.d.ts +56 -0
- package/packages/server/dist/bridge/bridge-model-catalog.js +100 -0
- package/packages/server/dist/bridge/bridge-runtime-service.d.ts +61 -0
- package/packages/server/dist/bridge/bridge-runtime-service.js +1386 -0
- package/packages/server/dist/bridge/chat-completions/chat-completion-service.d.ts +127 -0
- package/packages/server/dist/bridge/chat-completions/chat-completion-service.js +1026 -0
- package/packages/server/dist/bridge/index.d.ts +335 -0
- package/packages/server/dist/bridge/index.js +45 -0
- package/packages/server/dist/bridge/live-provider-extraction-canary.d.ts +69 -0
- package/packages/server/dist/bridge/live-provider-extraction-canary.js +186 -0
- package/packages/server/dist/bridge/providers/generic-provider-transport.d.ts +53 -0
- package/packages/server/dist/bridge/providers/generic-provider-transport.js +973 -0
- package/packages/server/dist/bridge/providers/provider-session-resolver.d.ts +17 -0
- package/packages/server/dist/bridge/providers/provider-session-resolver.js +95 -0
- package/packages/server/dist/bridge/providers/provider-streams.d.ts +80 -0
- package/packages/server/dist/bridge/providers/provider-streams.js +844 -0
- package/packages/server/dist/bridge/providers/provider-transport-profile.d.ts +194 -0
- package/packages/server/dist/bridge/providers/provider-transport-profile.js +198 -0
- package/packages/server/dist/bridge/providers/web-provider-transport.d.ts +30 -0
- package/packages/server/dist/bridge/providers/web-provider-transport.js +151 -0
- package/packages/server/dist/bridge/state/file-bridge-state-store.d.ts +36 -0
- package/packages/server/dist/bridge/state/file-bridge-state-store.js +164 -0
- package/packages/server/dist/bridge/stores/local-session-package-store.d.ts +23 -0
- package/packages/server/dist/bridge/stores/local-session-package-store.js +548 -0
- package/packages/server/dist/bridge/stores/provider-store.d.ts +94 -0
- package/packages/server/dist/bridge/stores/provider-store.js +143 -0
- package/packages/server/dist/bridge/stores/session-backed-provider-store.d.ts +7 -0
- package/packages/server/dist/bridge/stores/session-backed-provider-store.js +26 -0
- package/packages/server/dist/bridge/stores/session-package-store.d.ts +286 -0
- package/packages/server/dist/bridge/stores/session-package-store.js +1527 -0
- package/packages/server/dist/bridge/stores/session-store.d.ts +120 -0
- package/packages/server/dist/bridge/stores/session-store.js +139 -0
- package/packages/server/dist/cli/index.d.ts +9 -0
- package/packages/server/dist/cli/index.js +6 -0
- package/packages/server/dist/cli/main.d.ts +2 -0
- package/packages/server/dist/cli/main.js +9 -0
- package/packages/server/dist/cli/run-bridge-server-cli.d.ts +54 -0
- package/packages/server/dist/cli/run-bridge-server-cli.js +371 -0
- package/packages/server/dist/client/bridge-api-client.d.ts +61 -0
- package/packages/server/dist/client/bridge-api-client.js +267 -0
- package/packages/server/dist/client/index.d.ts +11 -0
- package/packages/server/dist/client/index.js +11 -0
- package/packages/server/dist/config/bridge-server-config.d.ts +52 -0
- package/packages/server/dist/config/bridge-server-config.js +118 -0
- package/packages/server/dist/config/index.d.ts +20 -0
- package/packages/server/dist/config/index.js +8 -0
- package/packages/server/dist/http/bridge-api-route-context.d.ts +14 -0
- package/packages/server/dist/http/bridge-api-route-context.js +1 -0
- package/packages/server/dist/http/create-bridge-api-server.d.ts +72 -0
- package/packages/server/dist/http/create-bridge-api-server.js +225 -0
- package/packages/server/dist/http/index.d.ts +5 -0
- package/packages/server/dist/http/index.js +5 -0
- package/packages/server/dist/http/parse-request.d.ts +6 -0
- package/packages/server/dist/http/parse-request.js +27 -0
- package/packages/server/dist/http/register-bridge-api-routes.d.ts +7 -0
- package/packages/server/dist/http/register-bridge-api-routes.js +17 -0
- package/packages/server/dist/http/routes/admin-routes.d.ts +7 -0
- package/packages/server/dist/http/routes/admin-routes.js +135 -0
- package/packages/server/dist/http/routes/chat-completions-route.d.ts +7 -0
- package/packages/server/dist/http/routes/chat-completions-route.js +49 -0
- package/packages/server/dist/http/routes/health-routes.d.ts +6 -0
- package/packages/server/dist/http/routes/health-routes.js +7 -0
- package/packages/server/dist/http/routes/message-routes.d.ts +7 -0
- package/packages/server/dist/http/routes/message-routes.js +7 -0
- package/packages/server/dist/index.d.ts +85 -0
- package/packages/server/dist/index.js +28 -0
- package/packages/server/dist/security/bridge-auth.d.ts +9 -0
- package/packages/server/dist/security/bridge-auth.js +41 -0
- package/packages/server/dist/security/cors-policy.d.ts +5 -0
- package/packages/server/dist/security/cors-policy.js +34 -0
- package/packages/server/dist/security/index.d.ts +16 -0
- package/packages/server/dist/security/index.js +12 -0
- package/packages/server/dist/security/redact-sensitive-values.d.ts +19 -0
- package/packages/server/dist/security/redact-sensitive-values.js +67 -0
- package/packages/server/dist/shared/api-schema.d.ts +133 -0
- package/packages/server/dist/shared/api-schema.js +1 -0
- package/packages/server/dist/shared/bridge-api-error.d.ts +17 -0
- package/packages/server/dist/shared/bridge-api-error.js +19 -0
- package/packages/server/dist/shared/index.d.ts +7 -0
- package/packages/server/dist/shared/index.js +7 -0
- package/packages/server/dist/shared/output.d.ts +5 -0
- package/packages/server/dist/shared/output.js +14 -0
|
@@ -0,0 +1,844 @@
|
|
|
1
|
+
import { bridgeRuntime } from "@uncensoredcode/openbridge/runtime";
|
|
2
|
+
import { outputModule } from "../../shared/output.js";
|
|
3
|
+
const { extractPacketMessage, ProviderFailure } = bridgeRuntime;
|
|
4
|
+
const { sanitizeBridgeApiOutput } = outputModule;
|
|
5
|
+
async function collectSseCompletion(stream, pushEvent, finalizeContent) {
|
|
6
|
+
const completion = await collectProviderCompletion(stream, pushEvent);
|
|
7
|
+
return {
|
|
8
|
+
...completion,
|
|
9
|
+
content: finalizeContent ? finalizeContent(completion.content) : completion.content
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
async function* streamSseFragments(stream, pushEvent, onComplete) {
|
|
13
|
+
yield* streamProviderFragments(stream, pushEvent, onComplete);
|
|
14
|
+
}
|
|
15
|
+
async function* streamVisibleProviderCompletion(stream, pushEvent, onComplete) {
|
|
16
|
+
const rawFragments = [];
|
|
17
|
+
let emittedContent = "";
|
|
18
|
+
for await (const fragment of streamProviderFragments(stream, pushEvent, onComplete)) {
|
|
19
|
+
rawFragments.push(fragment.content);
|
|
20
|
+
const visibleContent = extractIncrementalPacketMessage(rawFragments.join(""));
|
|
21
|
+
if (visibleContent.startsWith(emittedContent) &&
|
|
22
|
+
visibleContent.length > emittedContent.length) {
|
|
23
|
+
const delta = visibleContent.slice(emittedContent.length);
|
|
24
|
+
emittedContent = visibleContent;
|
|
25
|
+
yield delta;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const finalContent = sanitizeBridgeApiOutput(rawFragments.join("")).content;
|
|
29
|
+
if (finalContent.startsWith(emittedContent) && finalContent.length > emittedContent.length) {
|
|
30
|
+
yield finalContent.slice(emittedContent.length);
|
|
31
|
+
}
|
|
32
|
+
else if (!emittedContent && finalContent) {
|
|
33
|
+
yield finalContent;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function createSseJsonEventParser(input) {
|
|
37
|
+
const contentPaths = input.contentPaths.map((path) => splitPath(path));
|
|
38
|
+
const responseIdPaths = (input.responseIdPaths ?? []).map((path) => splitPath(path));
|
|
39
|
+
const conversationIdPaths = (input.conversationIdPaths ?? []).map((path) => splitPath(path));
|
|
40
|
+
const eventFilters = (input.eventFilters ?? []).map((filter) => ({
|
|
41
|
+
path: splitPath(filter.path),
|
|
42
|
+
equals: filter.equals
|
|
43
|
+
}));
|
|
44
|
+
const patchState = createPatchContentState();
|
|
45
|
+
return (parts, rawLine, currentResponseId, currentConversationId) => {
|
|
46
|
+
const line = rawLine.trim();
|
|
47
|
+
if (!line || line.startsWith("event:")) {
|
|
48
|
+
return {
|
|
49
|
+
responseId: currentResponseId,
|
|
50
|
+
conversationId: currentConversationId,
|
|
51
|
+
eventCountDelta: 0,
|
|
52
|
+
fragmentCountDelta: 0
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const normalized = line.startsWith("data:") ? line.slice("data:".length).trim() : line;
|
|
56
|
+
if (!normalized || normalized === "[DONE]") {
|
|
57
|
+
return {
|
|
58
|
+
responseId: currentResponseId,
|
|
59
|
+
conversationId: currentConversationId,
|
|
60
|
+
eventCountDelta: 0,
|
|
61
|
+
fragmentCountDelta: 0
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const payload = JSON.parse(normalized);
|
|
66
|
+
const responseId = extractConfiguredString(payload, responseIdPaths) || currentResponseId;
|
|
67
|
+
const conversationId = extractConfiguredString(payload, conversationIdPaths) || currentConversationId;
|
|
68
|
+
if (!matchesEventFilters(payload, eventFilters)) {
|
|
69
|
+
return {
|
|
70
|
+
responseId,
|
|
71
|
+
conversationId,
|
|
72
|
+
eventCountDelta: 0,
|
|
73
|
+
fragmentCountDelta: 0
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const fragments = extractConfiguredFragments(payload, contentPaths);
|
|
77
|
+
const patchedFragments = fragments.length === 0 ? extractPatchedContentFragments(payload, patchState) : [];
|
|
78
|
+
if (patchedFragments.length > 0) {
|
|
79
|
+
fragments.push(...patchedFragments);
|
|
80
|
+
}
|
|
81
|
+
parts.push(...fragments);
|
|
82
|
+
return {
|
|
83
|
+
responseId,
|
|
84
|
+
conversationId,
|
|
85
|
+
eventCountDelta: 1,
|
|
86
|
+
fragmentCountDelta: fragments.length
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return {
|
|
91
|
+
responseId: currentResponseId,
|
|
92
|
+
conversationId: currentConversationId,
|
|
93
|
+
eventCountDelta: 0,
|
|
94
|
+
fragmentCountDelta: 0
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
async function collectConnectJsonCompletion(stream, input, finalizeContent) {
|
|
100
|
+
const completion = await collectConnectProviderCompletion(stream, input);
|
|
101
|
+
return {
|
|
102
|
+
...completion,
|
|
103
|
+
content: finalizeContent ? finalizeContent(completion.content) : completion.content
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
async function* streamConnectJsonFragments(stream, input, onComplete) {
|
|
107
|
+
yield* streamConnectProviderFragments(stream, input, onComplete);
|
|
108
|
+
}
|
|
109
|
+
function extractIncrementalPacketMessage(content) {
|
|
110
|
+
const finalStart = content.search(/<final>/i);
|
|
111
|
+
if (finalStart >= 0) {
|
|
112
|
+
const finalContent = content.slice(finalStart + "<final>".length);
|
|
113
|
+
const finalEnd = finalContent.search(/<\/final>/i);
|
|
114
|
+
return finalEnd >= 0 ? finalContent.slice(0, finalEnd) : finalContent;
|
|
115
|
+
}
|
|
116
|
+
const packetStart = content.search(/<(?:zc_packet|packet)\b/i);
|
|
117
|
+
if (packetStart < 0) {
|
|
118
|
+
return "";
|
|
119
|
+
}
|
|
120
|
+
const packetContent = content.slice(packetStart);
|
|
121
|
+
const messageStartMatch = packetContent.match(/<message>/i);
|
|
122
|
+
if (!messageStartMatch) {
|
|
123
|
+
return "";
|
|
124
|
+
}
|
|
125
|
+
const messageStartIndex = (messageStartMatch.index ?? 0) + messageStartMatch[0].length;
|
|
126
|
+
const messageBody = packetContent.slice(messageStartIndex);
|
|
127
|
+
if (/^<!\[CDATA\[/i.test(messageBody)) {
|
|
128
|
+
const cdataBody = messageBody.slice("<![CDATA[".length);
|
|
129
|
+
const cdataEnd = cdataBody.indexOf("]]>");
|
|
130
|
+
return cdataEnd >= 0 ? (extractPacketMessage(packetContent) ?? "") : cdataBody;
|
|
131
|
+
}
|
|
132
|
+
const messageEnd = messageBody.search(/<\/message>/i);
|
|
133
|
+
if (messageEnd >= 0) {
|
|
134
|
+
const messageContent = messageBody.slice(0, messageEnd);
|
|
135
|
+
return /</.test(messageContent) ? "" : messageContent;
|
|
136
|
+
}
|
|
137
|
+
return /</.test(messageBody) ? "" : messageBody;
|
|
138
|
+
}
|
|
139
|
+
function normalizeLeadingAssistantBlock(content) {
|
|
140
|
+
const trimmed = content.trim();
|
|
141
|
+
return (extractLeadingSimpleAssistantBlock(trimmed, "final") ??
|
|
142
|
+
extractLeadingSimpleAssistantBlock(trimmed, "tool") ??
|
|
143
|
+
extractLatestSimpleAssistantBlock(trimmed) ??
|
|
144
|
+
trimmed);
|
|
145
|
+
}
|
|
146
|
+
async function collectProviderCompletion(stream, pushEvent) {
|
|
147
|
+
if (!stream) {
|
|
148
|
+
return {
|
|
149
|
+
content: "",
|
|
150
|
+
responseId: "",
|
|
151
|
+
conversationId: "",
|
|
152
|
+
eventCount: 0,
|
|
153
|
+
fragmentCount: 0
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
const reader = stream.getReader();
|
|
157
|
+
const decoder = new TextDecoder();
|
|
158
|
+
let buffer = "";
|
|
159
|
+
const parts = [];
|
|
160
|
+
let responseId = "";
|
|
161
|
+
let conversationId = "";
|
|
162
|
+
let eventCount = 0;
|
|
163
|
+
let fragmentCount = 0;
|
|
164
|
+
while (true) {
|
|
165
|
+
const { done, value } = await reader.read();
|
|
166
|
+
if (done) {
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
buffer += decoder.decode(value, { stream: true });
|
|
170
|
+
const lines = buffer.split("\n");
|
|
171
|
+
buffer = lines.pop() ?? "";
|
|
172
|
+
for (const rawLine of lines) {
|
|
173
|
+
const result = pushEvent(parts, rawLine, responseId, conversationId);
|
|
174
|
+
responseId = result.responseId;
|
|
175
|
+
conversationId = result.conversationId;
|
|
176
|
+
eventCount += result.eventCountDelta;
|
|
177
|
+
fragmentCount += result.fragmentCountDelta;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const finalResult = pushEvent(parts, buffer, responseId, conversationId);
|
|
181
|
+
return {
|
|
182
|
+
content: parts.join("").trim(),
|
|
183
|
+
responseId: finalResult.responseId,
|
|
184
|
+
conversationId: finalResult.conversationId,
|
|
185
|
+
eventCount: eventCount + finalResult.eventCountDelta,
|
|
186
|
+
fragmentCount: fragmentCount + finalResult.fragmentCountDelta
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
async function* streamProviderFragments(stream, pushEvent, onComplete) {
|
|
190
|
+
const rawFragments = [];
|
|
191
|
+
let responseId = "";
|
|
192
|
+
let conversationId = "";
|
|
193
|
+
let eventCount = 0;
|
|
194
|
+
let fragmentCount = 0;
|
|
195
|
+
for await (const fragment of iterateProviderFragments(stream, pushEvent)) {
|
|
196
|
+
rawFragments.push(fragment.content);
|
|
197
|
+
responseId = fragment.responseId;
|
|
198
|
+
conversationId = fragment.conversationId;
|
|
199
|
+
eventCount += fragment.eventCountDelta;
|
|
200
|
+
fragmentCount += fragment.fragmentCountDelta;
|
|
201
|
+
yield fragment;
|
|
202
|
+
}
|
|
203
|
+
onComplete?.({
|
|
204
|
+
content: rawFragments.join("").trim(),
|
|
205
|
+
responseId,
|
|
206
|
+
conversationId,
|
|
207
|
+
eventCount,
|
|
208
|
+
fragmentCount
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
async function* iterateProviderFragments(stream, pushEvent) {
|
|
212
|
+
if (!stream) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const reader = stream.getReader();
|
|
216
|
+
const decoder = new TextDecoder();
|
|
217
|
+
let buffer = "";
|
|
218
|
+
let responseId = "";
|
|
219
|
+
let conversationId = "";
|
|
220
|
+
while (true) {
|
|
221
|
+
const { done, value } = await reader.read();
|
|
222
|
+
if (done) {
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
buffer += decoder.decode(value, { stream: true });
|
|
226
|
+
const lines = buffer.split("\n");
|
|
227
|
+
buffer = lines.pop() ?? "";
|
|
228
|
+
for (const rawLine of lines) {
|
|
229
|
+
const parts = [];
|
|
230
|
+
const result = pushEvent(parts, rawLine, responseId, conversationId);
|
|
231
|
+
responseId = result.responseId;
|
|
232
|
+
conversationId = result.conversationId;
|
|
233
|
+
for (const fragment of parts) {
|
|
234
|
+
if (fragment) {
|
|
235
|
+
yield {
|
|
236
|
+
content: fragment,
|
|
237
|
+
responseId,
|
|
238
|
+
conversationId,
|
|
239
|
+
eventCountDelta: result.eventCountDelta,
|
|
240
|
+
fragmentCountDelta: result.fragmentCountDelta
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const parts = [];
|
|
247
|
+
const result = pushEvent(parts, buffer, responseId, conversationId);
|
|
248
|
+
responseId = result.responseId;
|
|
249
|
+
conversationId = result.conversationId;
|
|
250
|
+
for (const fragment of parts) {
|
|
251
|
+
if (fragment) {
|
|
252
|
+
yield {
|
|
253
|
+
content: fragment,
|
|
254
|
+
responseId,
|
|
255
|
+
conversationId,
|
|
256
|
+
eventCountDelta: result.eventCountDelta,
|
|
257
|
+
fragmentCountDelta: result.fragmentCountDelta
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
async function collectConnectProviderCompletion(stream, input) {
|
|
263
|
+
const fragments = [];
|
|
264
|
+
let responseId = "";
|
|
265
|
+
let conversationId = "";
|
|
266
|
+
let eventCount = 0;
|
|
267
|
+
let fragmentCount = 0;
|
|
268
|
+
for await (const fragment of iterateConnectProviderFragments(stream, input)) {
|
|
269
|
+
fragments.push(fragment.content);
|
|
270
|
+
responseId = fragment.responseId;
|
|
271
|
+
conversationId = fragment.conversationId;
|
|
272
|
+
eventCount += fragment.eventCountDelta;
|
|
273
|
+
fragmentCount += fragment.fragmentCountDelta;
|
|
274
|
+
}
|
|
275
|
+
return {
|
|
276
|
+
content: fragments.join("").trim(),
|
|
277
|
+
responseId,
|
|
278
|
+
conversationId,
|
|
279
|
+
eventCount,
|
|
280
|
+
fragmentCount
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
async function* streamConnectProviderFragments(stream, input, onComplete) {
|
|
284
|
+
const rawFragments = [];
|
|
285
|
+
let responseId = "";
|
|
286
|
+
let conversationId = "";
|
|
287
|
+
let eventCount = 0;
|
|
288
|
+
let fragmentCount = 0;
|
|
289
|
+
for await (const fragment of iterateConnectProviderFragments(stream, input)) {
|
|
290
|
+
rawFragments.push(fragment.content);
|
|
291
|
+
responseId = fragment.responseId;
|
|
292
|
+
conversationId = fragment.conversationId;
|
|
293
|
+
eventCount += fragment.eventCountDelta;
|
|
294
|
+
fragmentCount += fragment.fragmentCountDelta;
|
|
295
|
+
yield fragment;
|
|
296
|
+
}
|
|
297
|
+
onComplete?.({
|
|
298
|
+
content: rawFragments.join("").trim(),
|
|
299
|
+
responseId,
|
|
300
|
+
conversationId,
|
|
301
|
+
eventCount,
|
|
302
|
+
fragmentCount
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
async function* iterateConnectProviderFragments(stream, input) {
|
|
306
|
+
if (!stream) {
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
const reader = stream.getReader();
|
|
310
|
+
let buffer = new Uint8Array(0);
|
|
311
|
+
const contentPaths = input.contentPaths.map((path) => splitPath(path));
|
|
312
|
+
const responseIdPaths = (input.responseIdPaths ?? []).map((path) => splitPath(path));
|
|
313
|
+
const conversationIdPaths = (input.conversationIdPaths ?? []).map((path) => splitPath(path));
|
|
314
|
+
const eventFilters = (input.eventFilters ?? []).map((filter) => ({
|
|
315
|
+
path: splitPath(filter.path),
|
|
316
|
+
equals: filter.equals
|
|
317
|
+
}));
|
|
318
|
+
let responseId = "";
|
|
319
|
+
let conversationId = "";
|
|
320
|
+
while (true) {
|
|
321
|
+
const { done, value } = await reader.read();
|
|
322
|
+
if (done) {
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
buffer = concatUint8Arrays(buffer, value);
|
|
326
|
+
while (buffer.length >= 5) {
|
|
327
|
+
const messageLength = readConnectEnvelopeLength(buffer);
|
|
328
|
+
const totalLength = 5 + messageLength;
|
|
329
|
+
if (buffer.length < totalLength) {
|
|
330
|
+
break;
|
|
331
|
+
}
|
|
332
|
+
const envelope = buffer.slice(0, totalLength);
|
|
333
|
+
buffer = buffer.slice(totalLength);
|
|
334
|
+
const fragment = parseConnectEnvelope(envelope, contentPaths, responseIdPaths, conversationIdPaths, eventFilters, responseId, conversationId);
|
|
335
|
+
if (!fragment) {
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
responseId = fragment.responseId;
|
|
339
|
+
conversationId = fragment.conversationId;
|
|
340
|
+
yield fragment;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
if (buffer.length > 0) {
|
|
344
|
+
throw new ProviderFailure({
|
|
345
|
+
kind: "permanent",
|
|
346
|
+
code: "request_invalid",
|
|
347
|
+
message: "Provider returned a truncated Connect stream.",
|
|
348
|
+
displayMessage: "Provider response format is invalid.",
|
|
349
|
+
retryable: false,
|
|
350
|
+
sessionResetEligible: false
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
function extractLeadingSimpleAssistantBlock(content, tag) {
|
|
355
|
+
const openTag = `<${tag}>`;
|
|
356
|
+
const closeTag = `</${tag}>`;
|
|
357
|
+
if (!content.startsWith(openTag)) {
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
const closeIndex = content.indexOf(closeTag);
|
|
361
|
+
if (closeIndex < 0) {
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
const inner = content.slice(openTag.length, closeIndex);
|
|
365
|
+
if (containsNestedAssistantBlockMarker(inner)) {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
const block = content.slice(0, closeIndex + closeTag.length);
|
|
369
|
+
const trailing = content.slice(closeIndex + closeTag.length).trim();
|
|
370
|
+
if (!trailing) {
|
|
371
|
+
return block;
|
|
372
|
+
}
|
|
373
|
+
return trailing.includes("<") ? null : block;
|
|
374
|
+
}
|
|
375
|
+
function containsNestedAssistantBlockMarker(content) {
|
|
376
|
+
return /<(?:final|tool)>/i.test(content) || /(?:^|[^<])(?:final|tool)>/i.test(content);
|
|
377
|
+
}
|
|
378
|
+
function extractLatestSimpleAssistantBlock(content) {
|
|
379
|
+
const finalBlock = extractLastSimpleAssistantBlock(content, "final");
|
|
380
|
+
const toolBlock = extractLastSimpleAssistantBlock(content, "tool");
|
|
381
|
+
if (!finalBlock) {
|
|
382
|
+
return toolBlock?.block ?? null;
|
|
383
|
+
}
|
|
384
|
+
if (!toolBlock) {
|
|
385
|
+
return finalBlock.block;
|
|
386
|
+
}
|
|
387
|
+
return finalBlock.index >= toolBlock.index ? finalBlock.block : toolBlock.block;
|
|
388
|
+
}
|
|
389
|
+
function extractLastSimpleAssistantBlock(content, tag) {
|
|
390
|
+
const closeTag = `</${tag}>`;
|
|
391
|
+
const closeIndex = content.lastIndexOf(closeTag);
|
|
392
|
+
if (closeIndex < 0) {
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
const marker = new RegExp(`(?:<)?${tag}>`, "gi");
|
|
396
|
+
let latestMatch = null;
|
|
397
|
+
for (const match of content.matchAll(marker)) {
|
|
398
|
+
const index = match.index ?? -1;
|
|
399
|
+
if (index < 0 || index >= closeIndex) {
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
latestMatch = {
|
|
403
|
+
index,
|
|
404
|
+
markerLength: match[0].length
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
if (!latestMatch) {
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
return {
|
|
411
|
+
index: latestMatch.index,
|
|
412
|
+
block: `<${tag}>${content.slice(latestMatch.index + latestMatch.markerLength, closeIndex)}${closeTag}`
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
function splitPath(path) {
|
|
416
|
+
return path
|
|
417
|
+
.split(".")
|
|
418
|
+
.map((segment) => segment.trim())
|
|
419
|
+
.filter(Boolean);
|
|
420
|
+
}
|
|
421
|
+
function extractConfiguredString(payload, paths) {
|
|
422
|
+
for (const path of paths) {
|
|
423
|
+
for (const value of extractPathValues(payload, path)) {
|
|
424
|
+
const normalized = normalizeProviderResponseId(value);
|
|
425
|
+
if (normalized) {
|
|
426
|
+
return normalized;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return "";
|
|
431
|
+
}
|
|
432
|
+
function extractConfiguredFragments(payload, paths) {
|
|
433
|
+
const fragments = [];
|
|
434
|
+
const filteredPaths = filterNonAssistantMessageContentPaths(payload, paths);
|
|
435
|
+
for (const path of filteredPaths) {
|
|
436
|
+
for (const value of extractPathValues(payload, path)) {
|
|
437
|
+
if (typeof value === "string" && value) {
|
|
438
|
+
const normalized = normalizeProviderContentFragment(value);
|
|
439
|
+
if (normalized) {
|
|
440
|
+
fragments.push(normalized);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return fragments;
|
|
446
|
+
}
|
|
447
|
+
function createPatchContentState() {
|
|
448
|
+
return {
|
|
449
|
+
fragmentTypes: new Map(),
|
|
450
|
+
pendingContent: new Map(),
|
|
451
|
+
currentPath: "",
|
|
452
|
+
currentOp: "SET",
|
|
453
|
+
collectionLastIndex: new Map()
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
function extractPatchedContentFragments(payload, state) {
|
|
457
|
+
const fragments = [];
|
|
458
|
+
for (const operation of flattenPatchOperations(payload, state)) {
|
|
459
|
+
collectPatchContentFromStructuredValue(operation.value, state, fragments);
|
|
460
|
+
const fragmentPath = parseResponseFragmentPath(operation.path, state);
|
|
461
|
+
if (!fragmentPath) {
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
if (fragmentPath.kind === "collection") {
|
|
465
|
+
collectFragmentArray(fragmentPath.basePath, operation.value, state, fragments, operation.op);
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
if (fragmentPath.kind === "fragment") {
|
|
469
|
+
collectFragmentRecord(fragmentPath.basePath, operation.value, state, fragments);
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
if (fragmentPath.kind === "field" &&
|
|
473
|
+
fragmentPath.field === "type" &&
|
|
474
|
+
typeof operation.value === "string") {
|
|
475
|
+
setFragmentType(fragmentPath.basePath, operation.value, state, fragments);
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
if (fragmentPath.kind === "field" &&
|
|
479
|
+
fragmentPath.field === "content" &&
|
|
480
|
+
typeof operation.value === "string") {
|
|
481
|
+
pushFragmentContent(fragmentPath.basePath, operation.value, state, fragments);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return fragments;
|
|
485
|
+
}
|
|
486
|
+
function flattenPatchOperations(payload, state) {
|
|
487
|
+
const parserState = {
|
|
488
|
+
path: state.currentPath,
|
|
489
|
+
op: state.currentOp
|
|
490
|
+
};
|
|
491
|
+
const operations = flattenPatchOperation({
|
|
492
|
+
p: typeof payload.p === "string" ? payload.p : "",
|
|
493
|
+
o: typeof payload.o === "string" ? payload.o : "",
|
|
494
|
+
v: "v" in payload ? payload.v : payload
|
|
495
|
+
}, parserState);
|
|
496
|
+
state.currentPath = parserState.path;
|
|
497
|
+
state.currentOp = parserState.op;
|
|
498
|
+
return operations;
|
|
499
|
+
}
|
|
500
|
+
function flattenPatchOperation(input, parserState) {
|
|
501
|
+
const operation = input.o.trim().toUpperCase() || parserState.op || "SET";
|
|
502
|
+
const path = input.p.trim() || parserState.path || "";
|
|
503
|
+
parserState.op = operation;
|
|
504
|
+
parserState.path = path;
|
|
505
|
+
if (operation !== "BATCH") {
|
|
506
|
+
return [
|
|
507
|
+
{
|
|
508
|
+
path,
|
|
509
|
+
op: operation,
|
|
510
|
+
value: input.v
|
|
511
|
+
}
|
|
512
|
+
];
|
|
513
|
+
}
|
|
514
|
+
if (!Array.isArray(input.v)) {
|
|
515
|
+
return [];
|
|
516
|
+
}
|
|
517
|
+
const batchState = {
|
|
518
|
+
path: "",
|
|
519
|
+
op: "SET"
|
|
520
|
+
};
|
|
521
|
+
return input.v.flatMap((entry) => {
|
|
522
|
+
if (!entry || typeof entry !== "object") {
|
|
523
|
+
return [];
|
|
524
|
+
}
|
|
525
|
+
const record = entry;
|
|
526
|
+
return flattenPatchOperation({
|
|
527
|
+
p: typeof record.p === "string" ? record.p : "",
|
|
528
|
+
o: typeof record.o === "string" ? record.o : "",
|
|
529
|
+
v: "v" in record ? record.v : record
|
|
530
|
+
}, batchState).map((nestedOperation) => ({
|
|
531
|
+
...nestedOperation,
|
|
532
|
+
path: joinPatchPaths(path, nestedOperation.path)
|
|
533
|
+
}));
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
function joinPatchPaths(basePath, nextPath) {
|
|
537
|
+
if (!basePath) {
|
|
538
|
+
return nextPath;
|
|
539
|
+
}
|
|
540
|
+
if (!nextPath) {
|
|
541
|
+
return basePath;
|
|
542
|
+
}
|
|
543
|
+
const normalizedBase = basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
|
|
544
|
+
const normalizedNext = nextPath.startsWith("/") ? nextPath.slice(1) : nextPath;
|
|
545
|
+
return `${normalizedBase}/${normalizedNext}`;
|
|
546
|
+
}
|
|
547
|
+
function collectPatchContentFromStructuredValue(value, state, fragments) {
|
|
548
|
+
for (const [path, basePath] of [
|
|
549
|
+
["response.fragments", "/response/fragments"],
|
|
550
|
+
["v.response.fragments", "/response/fragments"],
|
|
551
|
+
["fragments", "/response/fragments"]
|
|
552
|
+
]) {
|
|
553
|
+
const fragmentArrays = extractPathValues(value, splitPath(path));
|
|
554
|
+
for (const fragmentArray of fragmentArrays) {
|
|
555
|
+
collectFragmentArray(basePath, fragmentArray, state, fragments, "SET");
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
function collectFragmentArray(basePath, value, state, fragments, operation) {
|
|
560
|
+
if (!Array.isArray(value)) {
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
const startIndex = operation === "APPEND" ? (state.collectionLastIndex.get(basePath) ?? -1) + 1 : 0;
|
|
564
|
+
value.forEach((entry, index) => {
|
|
565
|
+
collectFragmentRecord(`${basePath}/${startIndex + index}`, entry, state, fragments);
|
|
566
|
+
});
|
|
567
|
+
state.collectionLastIndex.set(basePath, startIndex + value.length - 1);
|
|
568
|
+
}
|
|
569
|
+
function collectFragmentRecord(basePath, value, state, fragments) {
|
|
570
|
+
if (!value || typeof value !== "object") {
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
const record = value;
|
|
574
|
+
const type = typeof record.type === "string" ? record.type : "";
|
|
575
|
+
if (type) {
|
|
576
|
+
setFragmentType(basePath, type, state, fragments);
|
|
577
|
+
}
|
|
578
|
+
if (typeof record.content === "string") {
|
|
579
|
+
pushFragmentContent(basePath, record.content, state, fragments);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
function setFragmentType(basePath, type, state, fragments) {
|
|
583
|
+
const normalizedType = type.trim().toUpperCase();
|
|
584
|
+
if (!normalizedType) {
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
state.fragmentTypes.set(basePath, normalizedType);
|
|
588
|
+
const pending = state.pendingContent.get(basePath) ?? [];
|
|
589
|
+
state.pendingContent.delete(basePath);
|
|
590
|
+
if (!isVisibleResponseFragmentType(normalizedType)) {
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
for (const content of pending) {
|
|
594
|
+
if (content) {
|
|
595
|
+
fragments.push(content);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
function pushFragmentContent(basePath, content, state, fragments) {
|
|
600
|
+
if (!content) {
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
const type = state.fragmentTypes.get(basePath);
|
|
604
|
+
if (!type) {
|
|
605
|
+
fragments.push(content);
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
if (isVisibleResponseFragmentType(type)) {
|
|
609
|
+
fragments.push(content);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
function isVisibleResponseFragmentType(type) {
|
|
613
|
+
return type === "RESPONSE" || type === "TEMPLATE_RESPONSE";
|
|
614
|
+
}
|
|
615
|
+
function parseResponseFragmentPath(path, state) {
|
|
616
|
+
const segments = path
|
|
617
|
+
.split("/")
|
|
618
|
+
.map((segment) => segment.trim())
|
|
619
|
+
.filter(Boolean);
|
|
620
|
+
if (segments.length === 0) {
|
|
621
|
+
return null;
|
|
622
|
+
}
|
|
623
|
+
const normalized = segments.map((segment) => segment.toLowerCase());
|
|
624
|
+
const rootOffset = normalized[0] === "response" ? 1 : 0;
|
|
625
|
+
if (normalized[rootOffset] !== "fragments") {
|
|
626
|
+
return null;
|
|
627
|
+
}
|
|
628
|
+
const basePathRoot = "/response/fragments";
|
|
629
|
+
if (normalized.length === rootOffset + 1) {
|
|
630
|
+
return {
|
|
631
|
+
kind: "collection",
|
|
632
|
+
basePath: basePathRoot
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
const fragmentIndexToken = normalized[rootOffset + 1] ?? "";
|
|
636
|
+
const fragmentIndex = fragmentIndexToken === "-1"
|
|
637
|
+
? (state.collectionLastIndex.get(basePathRoot) ?? -1)
|
|
638
|
+
: Number.parseInt(fragmentIndexToken, 10);
|
|
639
|
+
if (!Number.isInteger(fragmentIndex)) {
|
|
640
|
+
return null;
|
|
641
|
+
}
|
|
642
|
+
const basePath = `${basePathRoot}/${fragmentIndex}`;
|
|
643
|
+
if (normalized.length === rootOffset + 2) {
|
|
644
|
+
return {
|
|
645
|
+
kind: "fragment",
|
|
646
|
+
basePath
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
return {
|
|
650
|
+
kind: "field",
|
|
651
|
+
basePath,
|
|
652
|
+
field: normalized[rootOffset + 2] ?? ""
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
function filterNonAssistantMessageContentPaths(payload, paths) {
|
|
656
|
+
const message = readProviderMessageRecord(payload);
|
|
657
|
+
if (!message || typeof message !== "object") {
|
|
658
|
+
return paths;
|
|
659
|
+
}
|
|
660
|
+
const role = readProviderMessageRole(message);
|
|
661
|
+
if (!role || role === "assistant") {
|
|
662
|
+
return paths;
|
|
663
|
+
}
|
|
664
|
+
return paths.filter((path) => path[0] !== "message");
|
|
665
|
+
}
|
|
666
|
+
function readProviderMessageRecord(payload) {
|
|
667
|
+
const direct = payload.message;
|
|
668
|
+
if (direct && typeof direct === "object") {
|
|
669
|
+
return direct;
|
|
670
|
+
}
|
|
671
|
+
const nestedValue = payload.v;
|
|
672
|
+
if (!nestedValue || typeof nestedValue !== "object") {
|
|
673
|
+
return null;
|
|
674
|
+
}
|
|
675
|
+
const nestedMessage = nestedValue.message;
|
|
676
|
+
return nestedMessage && typeof nestedMessage === "object"
|
|
677
|
+
? nestedMessage
|
|
678
|
+
: null;
|
|
679
|
+
}
|
|
680
|
+
function readProviderMessageRole(message) {
|
|
681
|
+
const directRole = typeof message.role === "string" ? message.role.trim().toLowerCase() : "";
|
|
682
|
+
if (directRole) {
|
|
683
|
+
return directRole;
|
|
684
|
+
}
|
|
685
|
+
const author = message.author;
|
|
686
|
+
if (!author || typeof author !== "object") {
|
|
687
|
+
return "";
|
|
688
|
+
}
|
|
689
|
+
return typeof author.role === "string"
|
|
690
|
+
? String(author.role)
|
|
691
|
+
.trim()
|
|
692
|
+
.toLowerCase()
|
|
693
|
+
: "";
|
|
694
|
+
}
|
|
695
|
+
function normalizeProviderContentFragment(value) {
|
|
696
|
+
if (isHiddenInlineReferenceToken(value)) {
|
|
697
|
+
return "";
|
|
698
|
+
}
|
|
699
|
+
return value;
|
|
700
|
+
}
|
|
701
|
+
function isHiddenInlineReferenceToken(value) {
|
|
702
|
+
return /^\uE200[A-Za-z0-9_-]+\uE202[\s\S]*\uE201$/u.test(value.trim());
|
|
703
|
+
}
|
|
704
|
+
function extractPathValues(value, segments) {
|
|
705
|
+
if (segments.length === 0) {
|
|
706
|
+
return [value];
|
|
707
|
+
}
|
|
708
|
+
if (value === null || value === undefined) {
|
|
709
|
+
return [];
|
|
710
|
+
}
|
|
711
|
+
const [segment, ...rest] = segments;
|
|
712
|
+
if (segment === "*") {
|
|
713
|
+
if (Array.isArray(value)) {
|
|
714
|
+
return value.flatMap((entry) => extractPathValues(entry, rest));
|
|
715
|
+
}
|
|
716
|
+
if (typeof value === "object") {
|
|
717
|
+
return Object.values(value).flatMap((entry) => extractPathValues(entry, rest));
|
|
718
|
+
}
|
|
719
|
+
return [];
|
|
720
|
+
}
|
|
721
|
+
if (Array.isArray(value)) {
|
|
722
|
+
const index = Number.parseInt(segment, 10);
|
|
723
|
+
if (!Number.isInteger(index)) {
|
|
724
|
+
return [];
|
|
725
|
+
}
|
|
726
|
+
return extractPathValues(value[index], rest);
|
|
727
|
+
}
|
|
728
|
+
if (typeof value === "object") {
|
|
729
|
+
return extractPathValues(value[segment], rest);
|
|
730
|
+
}
|
|
731
|
+
return [];
|
|
732
|
+
}
|
|
733
|
+
function normalizeProviderResponseId(value) {
|
|
734
|
+
if (typeof value === "string") {
|
|
735
|
+
return value;
|
|
736
|
+
}
|
|
737
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
738
|
+
return String(value);
|
|
739
|
+
}
|
|
740
|
+
return "";
|
|
741
|
+
}
|
|
742
|
+
function parseConnectEnvelope(envelope, contentPaths, responseIdPaths, conversationIdPaths, eventFilters, currentResponseId, currentConversationId) {
|
|
743
|
+
const flags = envelope[0] ?? 0;
|
|
744
|
+
if ((flags & 0x01) !== 0) {
|
|
745
|
+
throw new ProviderFailure({
|
|
746
|
+
kind: "permanent",
|
|
747
|
+
code: "request_invalid",
|
|
748
|
+
message: "Provider returned a compressed Connect message, which is not supported yet.",
|
|
749
|
+
displayMessage: "Provider response format is unsupported.",
|
|
750
|
+
retryable: false,
|
|
751
|
+
sessionResetEligible: false
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
const payloadBytes = envelope.slice(5);
|
|
755
|
+
const payloadText = new TextDecoder().decode(payloadBytes).trim();
|
|
756
|
+
if (!payloadText) {
|
|
757
|
+
return null;
|
|
758
|
+
}
|
|
759
|
+
let payload;
|
|
760
|
+
try {
|
|
761
|
+
payload = JSON.parse(payloadText);
|
|
762
|
+
}
|
|
763
|
+
catch {
|
|
764
|
+
return null;
|
|
765
|
+
}
|
|
766
|
+
if ((flags & 0x02) !== 0) {
|
|
767
|
+
const error = readConnectEndStreamError(payload);
|
|
768
|
+
if (error) {
|
|
769
|
+
throw new ProviderFailure({
|
|
770
|
+
kind: error.retryable ? "transient" : "permanent",
|
|
771
|
+
code: error.retryable ? "transport_error" : "request_invalid",
|
|
772
|
+
message: `Provider Connect stream ended with error code "${error.code}"${error.message ? `: ${error.message}` : "."}`,
|
|
773
|
+
displayMessage: error.message || "Provider request failed.",
|
|
774
|
+
retryable: error.retryable,
|
|
775
|
+
sessionResetEligible: false,
|
|
776
|
+
details: {
|
|
777
|
+
upstreamCode: error.code
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
return null;
|
|
782
|
+
}
|
|
783
|
+
const nextResponseId = extractConfiguredString(payload, responseIdPaths) || currentResponseId;
|
|
784
|
+
const nextConversationId = extractConfiguredString(payload, conversationIdPaths) || currentConversationId;
|
|
785
|
+
if (!matchesEventFilters(payload, eventFilters)) {
|
|
786
|
+
return {
|
|
787
|
+
content: "",
|
|
788
|
+
responseId: nextResponseId,
|
|
789
|
+
conversationId: nextConversationId,
|
|
790
|
+
eventCountDelta: 0,
|
|
791
|
+
fragmentCountDelta: 0
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
const fragments = extractConfiguredFragments(payload, contentPaths);
|
|
795
|
+
return {
|
|
796
|
+
content: fragments.join(""),
|
|
797
|
+
responseId: nextResponseId,
|
|
798
|
+
conversationId: nextConversationId,
|
|
799
|
+
eventCountDelta: fragments.length > 0 ? 1 : 0,
|
|
800
|
+
fragmentCountDelta: fragments.length
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
function matchesEventFilters(payload, filters) {
|
|
804
|
+
if (filters.length === 0) {
|
|
805
|
+
return true;
|
|
806
|
+
}
|
|
807
|
+
return filters.every((filter) => extractPathValues(payload, filter.path).some((value) => value === filter.equals));
|
|
808
|
+
}
|
|
809
|
+
function readConnectEnvelopeLength(buffer) {
|
|
810
|
+
return (((buffer[1] ?? 0) << 24) | ((buffer[2] ?? 0) << 16) | ((buffer[3] ?? 0) << 8) | (buffer[4] ?? 0));
|
|
811
|
+
}
|
|
812
|
+
function concatUint8Arrays(left, right) {
|
|
813
|
+
const combined = new Uint8Array(left.length + right.length);
|
|
814
|
+
combined.set(left, 0);
|
|
815
|
+
combined.set(right, left.length);
|
|
816
|
+
return combined;
|
|
817
|
+
}
|
|
818
|
+
function readConnectEndStreamError(payload) {
|
|
819
|
+
const error = payload.error;
|
|
820
|
+
if (typeof error !== "object" || error === null) {
|
|
821
|
+
return null;
|
|
822
|
+
}
|
|
823
|
+
const record = error;
|
|
824
|
+
const code = typeof record.code === "string" ? record.code.trim() : "";
|
|
825
|
+
if (!code) {
|
|
826
|
+
return null;
|
|
827
|
+
}
|
|
828
|
+
const message = typeof record.message === "string" ? record.message.trim() : "";
|
|
829
|
+
return {
|
|
830
|
+
code,
|
|
831
|
+
message,
|
|
832
|
+
retryable: /^(aborted|deadline_exceeded|resource_exhausted|unavailable)$/i.test(code)
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
export const providerStreamsModule = {
|
|
836
|
+
collectSseCompletion,
|
|
837
|
+
streamSseFragments,
|
|
838
|
+
streamVisibleProviderCompletion,
|
|
839
|
+
createSseJsonEventParser,
|
|
840
|
+
collectConnectJsonCompletion,
|
|
841
|
+
streamConnectJsonFragments,
|
|
842
|
+
extractIncrementalPacketMessage,
|
|
843
|
+
normalizeLeadingAssistantBlock
|
|
844
|
+
};
|