@dbx-tools/appkit-mastra 0.9.32 → 0.9.36
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 +15 -12
- package/index.ts +1 -2
- package/lib/index.d.ts +1 -2
- package/lib/index.js +2 -2
- package/lib/src/model.js +2 -2
- package/lib/src/observability.d.ts +7 -0
- package/lib/src/observability.js +17 -1
- package/lib/src/plugin.js +3 -2
- package/lib/src/serving-sanitize.d.ts +2 -133
- package/lib/src/serving-sanitize.js +3 -325
- package/lib/src/trace-io.d.ts +14 -12
- package/lib/src/trace-io.js +157 -44
- package/package.json +277 -19
- package/src/model.ts +1 -1
- package/src/observability.ts +16 -0
- package/src/plugin.ts +2 -1
- package/src/serving-sanitize.ts +14 -357
- package/src/trace-io.ts +172 -40
|
@@ -1,137 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* OpenAI-compatible `/chat/completions` route, in both directions.
|
|
4
|
-
*
|
|
5
|
-
* Outbound ({@link rewriteServingBody}), because the transcript Mastra
|
|
6
|
-
* persists is not always a transcript the provider will accept back:
|
|
7
|
-
* Databricks-hosted Claude rejects replayed extended-thinking blocks and reads
|
|
8
|
-
* a trailing assistant message as a prefill request, while GPT tool calls need
|
|
9
|
-
* model-specific reasoning-effort normalization on Chat Completions.
|
|
10
|
-
*
|
|
11
|
-
* Inbound ({@link rewriteServingResponseBody} and
|
|
12
|
-
* {@link rewriteServingResponseStream}), because Databricks-hosted Gemini and
|
|
13
|
-
* Claude can answer with native content-parts arrays where the OpenAI contract
|
|
14
|
-
* (and therefore the AI SDK's response schema) requires a plain string.
|
|
15
|
-
*
|
|
16
|
-
* Every repair here is a provider quirk rather than a schema violation, so all
|
|
17
|
-
* of them are applied on the wire (see the `globalThis.fetch` wrapper in
|
|
18
|
-
* `model.ts`) rather than by changing what the agent stores or what the UI
|
|
19
|
-
* shows.
|
|
2
|
+
* Databricks Model Serving wire repairs shared with the model gateway.
|
|
20
3
|
*
|
|
21
4
|
* @module
|
|
22
5
|
*/
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* A chat message as it arrives on the serving wire, plus the extended-thinking
|
|
26
|
-
* fields Databricks-hosted Claude adds. The OpenAI-standard part of the shape
|
|
27
|
-
* is {@link ChatMessage} from `@dbx-tools/shared-model`; only the provider
|
|
28
|
-
* extensions this module strips are declared here.
|
|
29
|
-
*/
|
|
30
|
-
export interface ServingChatMessage extends ChatMessage {
|
|
31
|
-
/** Narrowed to the roles this repair pass reasons about, plus Claude's `reasoning` turn. */
|
|
32
|
-
role: ChatRole | "reasoning";
|
|
33
|
-
reasoning?: unknown;
|
|
34
|
-
reasoning_content?: unknown;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Parse, sanitize, and re-serialize a `/serving-endpoints/...` POST
|
|
38
|
-
* body. Returns the original string verbatim when the body is not
|
|
39
|
-
* JSON or no rewrite was needed.
|
|
40
|
-
*/
|
|
41
|
-
export declare function rewriteServingBody(body: string): string;
|
|
42
|
-
/** Prepared fetch arguments plus the post-sanitize body used for diagnostics. */
|
|
43
|
-
export interface RewrittenServingRequest {
|
|
44
|
-
input: Parameters<typeof fetch>[0];
|
|
45
|
-
init: Parameters<typeof fetch>[1];
|
|
46
|
-
body: string;
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Read and rewrite a serving POST whether its JSON body lives on `init` or on
|
|
50
|
-
* a `Request`. A changed Request is rebuilt without stale byte headers.
|
|
51
|
-
*/
|
|
52
|
-
export declare function rewriteServingRequest(input: Parameters<typeof fetch>[0], init?: Parameters<typeof fetch>[1]): Promise<RewrittenServingRequest>;
|
|
53
|
-
/**
|
|
54
|
-
* Normalize Chat Completions reasoning for GPT models carrying function tools.
|
|
55
|
-
*
|
|
56
|
-
* GPT 5.6 requires reasoning disabled for tool calls on this route, so `none`
|
|
57
|
-
* wins over a caller/default effort. Responses-only models are routed away
|
|
58
|
-
* from Chat Completions before this sanitizer runs.
|
|
59
|
-
*/
|
|
60
|
-
export declare function applyToolReasoningCompatibility(body: Record<string, unknown>): boolean;
|
|
61
|
-
/**
|
|
62
|
-
* Drop extended-thinking / reasoning blocks from a replayed transcript.
|
|
63
|
-
*
|
|
64
|
-
* Hybrid Claude endpoints (e.g. Sonnet 4.5+) may emit `reasoning` content
|
|
65
|
-
* parts on the first turn. Mastra persists and replays them on the next
|
|
66
|
-
* agent step, but Databricks-hosted Claude rejects those blocks on
|
|
67
|
-
* multi-turn tool continuations. The UI already captured reasoning for
|
|
68
|
-
* display; stripping here keeps provider replay compatible without
|
|
69
|
-
* changing what users see in the chat bubble.
|
|
70
|
-
*/
|
|
71
|
-
export declare function stripReasoningFromServingMessages(messages: ServingChatMessage[]): boolean;
|
|
72
|
-
/**
|
|
73
|
-
* Repair a Mastra/AI SDK message replay that Databricks-hosted Claude
|
|
74
|
-
* rejects with `"This model does not support assistant message
|
|
75
|
-
* prefill. The conversation must end with a user message."`.
|
|
76
|
-
*
|
|
77
|
-
* The bug pattern: when an assistant turn streams text *and* a
|
|
78
|
-
* `tool_call`, the AI SDK persists them as two separate assistant
|
|
79
|
-
* entries (text-only and tool-call-only). On the next agent step the
|
|
80
|
-
* tool-call entry is replayed *before* the tool result and the
|
|
81
|
-
* text entry is replayed *after* it, so the conversation ends with a
|
|
82
|
-
* trailing assistant text message. Anthropic interprets that as a
|
|
83
|
-
* prefill request and rejects it on Databricks (the upstream Bedrock
|
|
84
|
-
* route disallows prefill).
|
|
85
|
-
*
|
|
86
|
-
* Fix: when the last message is an assistant text with no `tool_calls`
|
|
87
|
-
* and the chain immediately before it is `assistant(tool_calls=...)`
|
|
88
|
-
* followed only by `tool(...)` results, fold the trailing text back
|
|
89
|
-
* into the `content` of that opening assistant and drop the duplicate.
|
|
90
|
-
*/
|
|
91
|
-
export declare function repairAssistantPrefill(messages: ServingChatMessage[]): boolean;
|
|
92
|
-
/**
|
|
93
|
-
* Parse, sanitize, and re-serialize a `/serving-endpoints/...` non-streaming
|
|
94
|
-
* JSON RESPONSE body. Returns the original string verbatim when the body is
|
|
95
|
-
* not JSON or no rewrite was needed, mirroring
|
|
96
|
-
* {@link rewriteServingBody} on the request side.
|
|
97
|
-
*/
|
|
98
|
-
export declare function rewriteServingResponseBody(body: string): string;
|
|
99
|
-
/**
|
|
100
|
-
* Repair array-valued `choices[].delta.content` inside an OpenAI SSE stream
|
|
101
|
-
* without buffering the response. The decoder retains partial lines across
|
|
102
|
-
* arbitrary network chunk boundaries; each complete `data:` line is parsed and
|
|
103
|
-
* re-encoded only when its content needs normalization.
|
|
104
|
-
*/
|
|
105
|
-
export declare function rewriteServingResponseStream(body: ReadableStream<Uint8Array>): ReadableStream<Uint8Array>;
|
|
106
|
-
/**
|
|
107
|
-
* Collapse a structured `choices[].message.content` array to the plain string
|
|
108
|
-
* the OpenAI Chat Completions contract specifies.
|
|
109
|
-
*
|
|
110
|
-
* Databricks-hosted Gemini answers a non-streaming `/chat/completions` call
|
|
111
|
-
* with the Gemini-native parts shape:
|
|
112
|
-
*
|
|
113
|
-
* ```json
|
|
114
|
-
* "content": [{ "type": "text", "text": "...", "thoughtSignature": "..." }]
|
|
115
|
-
* ```
|
|
116
|
-
*
|
|
117
|
-
* `@ai-sdk/openai-compatible` validates the response against the OpenAI
|
|
118
|
-
* schema, where `content` is a nullable string, so it throws
|
|
119
|
-
* `Type validation failed: ... expected string, received array` and the whole
|
|
120
|
-
* call fails (`AI_APICallError: Invalid JSON response` on an HTTP 200). Mastra
|
|
121
|
-
* uses `doGenerate` for its side calls, so the visible symptom is
|
|
122
|
-
* `Error generating title` - every thread keeps its placeholder name.
|
|
123
|
-
*
|
|
124
|
-
* Flattening on the wire keeps the repair in one place: neither the agent's
|
|
125
|
-
* stored transcript nor the UI has to know the provider emitted parts. Any
|
|
126
|
-
* non-text part (a reasoning block, `thoughtSignature`-only entry, or inline
|
|
127
|
-
* image) contributes nothing, matching {@link openaiChat.chatContentToText},
|
|
128
|
-
* and an all-parts-empty message flattens to `""` rather than being dropped, so
|
|
129
|
-
* `finish_reason` and `usage` still round-trip.
|
|
130
|
-
*/
|
|
131
|
-
export declare function flattenChoiceMessageContent(payload: Record<string, unknown>): boolean;
|
|
132
|
-
/**
|
|
133
|
-
* Collapse structured streaming `choices[].delta.content` arrays to strings.
|
|
134
|
-
* Claude reasoning-only chunks become an empty content delta while text parts
|
|
135
|
-
* are concatenated in order.
|
|
136
|
-
*/
|
|
137
|
-
export declare function flattenChoiceDeltaContent(payload: Record<string, unknown>): boolean;
|
|
6
|
+
export { applyToolReasoningCompatibility, flattenChoiceDeltaContent, flattenChoiceMessageContent, repairAssistantPrefill, rewriteServingBody, rewriteServingRequest, rewriteServingResponseBody, rewriteServingResponseStream, stripReasoningFromServingMessages, type RewrittenServingRequest, type ServingChatMessage, } from "@dbx-tools/model/serving-wire";
|
|
@@ -1,329 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* OpenAI-compatible `/chat/completions` route, in both directions.
|
|
4
|
-
*
|
|
5
|
-
* Outbound ({@link rewriteServingBody}), because the transcript Mastra
|
|
6
|
-
* persists is not always a transcript the provider will accept back:
|
|
7
|
-
* Databricks-hosted Claude rejects replayed extended-thinking blocks and reads
|
|
8
|
-
* a trailing assistant message as a prefill request, while GPT tool calls need
|
|
9
|
-
* model-specific reasoning-effort normalization on Chat Completions.
|
|
10
|
-
*
|
|
11
|
-
* Inbound ({@link rewriteServingResponseBody} and
|
|
12
|
-
* {@link rewriteServingResponseStream}), because Databricks-hosted Gemini and
|
|
13
|
-
* Claude can answer with native content-parts arrays where the OpenAI contract
|
|
14
|
-
* (and therefore the AI SDK's response schema) requires a plain string.
|
|
15
|
-
*
|
|
16
|
-
* Every repair here is a provider quirk rather than a schema violation, so all
|
|
17
|
-
* of them are applied on the wire (see the `globalThis.fetch` wrapper in
|
|
18
|
-
* `model.ts`) rather than by changing what the agent stores or what the UI
|
|
19
|
-
* shows.
|
|
2
|
+
* Databricks Model Serving wire repairs shared with the model gateway.
|
|
20
3
|
*
|
|
21
4
|
* @module
|
|
22
5
|
*/
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
import { openaiChat } from "@dbx-tools/shared-model";
|
|
26
|
-
/** Signed Claude reasoning blocks that cannot be replayed after mutation. */
|
|
27
|
-
const REASONING_PART_TYPES = new Set([
|
|
28
|
-
"reasoning",
|
|
29
|
-
"thinking",
|
|
30
|
-
"redacted_thinking",
|
|
31
|
-
]);
|
|
32
|
-
/**
|
|
33
|
-
* Parse, sanitize, and re-serialize a `/serving-endpoints/...` POST
|
|
34
|
-
* body. Returns the original string verbatim when the body is not
|
|
35
|
-
* JSON or no rewrite was needed.
|
|
36
|
-
*/
|
|
37
|
-
export function rewriteServingBody(body) {
|
|
38
|
-
const parsed = json.parseRecord(body);
|
|
39
|
-
if (!parsed)
|
|
40
|
-
return body;
|
|
41
|
-
// Runs regardless of `messages`: Databricks refuses to parse a body carrying
|
|
42
|
-
// an unknown top-level field, so this failure is not specific to a transcript.
|
|
43
|
-
const toolReasoning = applyToolReasoningCompatibility(parsed);
|
|
44
|
-
let changed = openaiChat.stripUnsupportedChatFields(parsed).length > 0 || toolReasoning;
|
|
45
|
-
if (Array.isArray(parsed.messages)) {
|
|
46
|
-
const messages = parsed.messages;
|
|
47
|
-
// Evaluated eagerly, not short-circuited: one transcript can need both
|
|
48
|
-
// reasoning blocks stripped AND a trailing assistant prefill folded back.
|
|
49
|
-
const stripped = stripReasoningFromServingMessages(messages);
|
|
50
|
-
const repaired = repairAssistantPrefill(messages);
|
|
51
|
-
changed = changed || stripped || repaired;
|
|
52
|
-
}
|
|
53
|
-
return changed ? JSON.stringify(parsed) : body;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Read and rewrite a serving POST whether its JSON body lives on `init` or on
|
|
57
|
-
* a `Request`. A changed Request is rebuilt without stale byte headers.
|
|
58
|
-
*/
|
|
59
|
-
export async function rewriteServingRequest(input, init) {
|
|
60
|
-
const request = new Request(input, init);
|
|
61
|
-
const body = await request.clone().text();
|
|
62
|
-
const rewritten = rewriteServingBody(body);
|
|
63
|
-
if (rewritten === body)
|
|
64
|
-
return { input, init, body };
|
|
65
|
-
const headers = new Headers(request.headers);
|
|
66
|
-
headers.delete("content-length");
|
|
67
|
-
headers.delete("content-encoding");
|
|
68
|
-
return {
|
|
69
|
-
input: new Request(request, { body: rewritten, headers }),
|
|
70
|
-
init: undefined,
|
|
71
|
-
body: rewritten,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Normalize Chat Completions reasoning for GPT models carrying function tools.
|
|
76
|
-
*
|
|
77
|
-
* GPT 5.6 requires reasoning disabled for tool calls on this route, so `none`
|
|
78
|
-
* wins over a caller/default effort. Responses-only models are routed away
|
|
79
|
-
* from Chat Completions before this sanitizer runs.
|
|
80
|
-
*/
|
|
81
|
-
export function applyToolReasoningCompatibility(body) {
|
|
82
|
-
if (!Array.isArray(body.tools) || body.tools.length === 0) {
|
|
83
|
-
return false;
|
|
84
|
-
}
|
|
85
|
-
const model = stringUtils.trimToNull(body.model);
|
|
86
|
-
if (!model)
|
|
87
|
-
return false;
|
|
88
|
-
if (policy.chatToolReasoningEffort(model) !== "none")
|
|
89
|
-
return false;
|
|
90
|
-
if (body.reasoning_effort === "none")
|
|
91
|
-
return false;
|
|
92
|
-
body.reasoning_effort = "none";
|
|
93
|
-
return true;
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Drop extended-thinking / reasoning blocks from a replayed transcript.
|
|
97
|
-
*
|
|
98
|
-
* Hybrid Claude endpoints (e.g. Sonnet 4.5+) may emit `reasoning` content
|
|
99
|
-
* parts on the first turn. Mastra persists and replays them on the next
|
|
100
|
-
* agent step, but Databricks-hosted Claude rejects those blocks on
|
|
101
|
-
* multi-turn tool continuations. The UI already captured reasoning for
|
|
102
|
-
* display; stripping here keeps provider replay compatible without
|
|
103
|
-
* changing what users see in the chat bubble.
|
|
104
|
-
*/
|
|
105
|
-
export function stripReasoningFromServingMessages(messages) {
|
|
106
|
-
let changed = false;
|
|
107
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
108
|
-
const msg = messages[i];
|
|
109
|
-
if (!msg)
|
|
110
|
-
continue;
|
|
111
|
-
if (msg.role === "reasoning") {
|
|
112
|
-
messages.splice(i, 1);
|
|
113
|
-
changed = true;
|
|
114
|
-
continue;
|
|
115
|
-
}
|
|
116
|
-
if (msg.reasoning !== undefined) {
|
|
117
|
-
delete msg.reasoning;
|
|
118
|
-
changed = true;
|
|
119
|
-
}
|
|
120
|
-
if (msg.reasoning_content !== undefined) {
|
|
121
|
-
delete msg.reasoning_content;
|
|
122
|
-
changed = true;
|
|
123
|
-
}
|
|
124
|
-
const parts = openaiChat.chatContentParts(msg.content);
|
|
125
|
-
if (!parts)
|
|
126
|
-
continue;
|
|
127
|
-
const filtered = parts.filter((part) => {
|
|
128
|
-
const type = part?.type;
|
|
129
|
-
if (typeof type === "string" && REASONING_PART_TYPES.has(type)) {
|
|
130
|
-
changed = true;
|
|
131
|
-
return false;
|
|
132
|
-
}
|
|
133
|
-
return true;
|
|
134
|
-
});
|
|
135
|
-
if (filtered.length !== parts.length) {
|
|
136
|
-
msg.content = filtered;
|
|
137
|
-
}
|
|
138
|
-
const hasToolCalls = Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0;
|
|
139
|
-
if (msg.role === "assistant" && !hasToolCalls && isEmptyServingContent(msg.content)) {
|
|
140
|
-
messages.splice(i, 1);
|
|
141
|
-
changed = true;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
return changed;
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Repair a Mastra/AI SDK message replay that Databricks-hosted Claude
|
|
148
|
-
* rejects with `"This model does not support assistant message
|
|
149
|
-
* prefill. The conversation must end with a user message."`.
|
|
150
|
-
*
|
|
151
|
-
* The bug pattern: when an assistant turn streams text *and* a
|
|
152
|
-
* `tool_call`, the AI SDK persists them as two separate assistant
|
|
153
|
-
* entries (text-only and tool-call-only). On the next agent step the
|
|
154
|
-
* tool-call entry is replayed *before* the tool result and the
|
|
155
|
-
* text entry is replayed *after* it, so the conversation ends with a
|
|
156
|
-
* trailing assistant text message. Anthropic interprets that as a
|
|
157
|
-
* prefill request and rejects it on Databricks (the upstream Bedrock
|
|
158
|
-
* route disallows prefill).
|
|
159
|
-
*
|
|
160
|
-
* Fix: when the last message is an assistant text with no `tool_calls`
|
|
161
|
-
* and the chain immediately before it is `assistant(tool_calls=...)`
|
|
162
|
-
* followed only by `tool(...)` results, fold the trailing text back
|
|
163
|
-
* into the `content` of that opening assistant and drop the duplicate.
|
|
164
|
-
*/
|
|
165
|
-
export function repairAssistantPrefill(messages) {
|
|
166
|
-
if (messages.length < 2)
|
|
167
|
-
return false;
|
|
168
|
-
const last = messages[messages.length - 1];
|
|
169
|
-
if (!last || last.role !== "assistant" || (last.tool_calls && last.tool_calls.length > 0)) {
|
|
170
|
-
return false;
|
|
171
|
-
}
|
|
172
|
-
let i = messages.length - 2;
|
|
173
|
-
while (i >= 0 && messages[i]?.role === "tool")
|
|
174
|
-
i--;
|
|
175
|
-
if (i < 0)
|
|
176
|
-
return false;
|
|
177
|
-
const opener = messages[i];
|
|
178
|
-
if (!opener ||
|
|
179
|
-
opener.role !== "assistant" ||
|
|
180
|
-
!opener.tool_calls ||
|
|
181
|
-
opener.tool_calls.length === 0) {
|
|
182
|
-
return false;
|
|
183
|
-
}
|
|
184
|
-
const merged = [
|
|
185
|
-
stringUtils.trimToNull(textFromServingContent(opener.content)),
|
|
186
|
-
stringUtils.trimToNull(textFromServingContent(last.content)),
|
|
187
|
-
]
|
|
188
|
-
.filter((s) => s !== null)
|
|
189
|
-
.join("\n\n");
|
|
190
|
-
opener.content = merged;
|
|
191
|
-
messages.pop();
|
|
192
|
-
return true;
|
|
193
|
-
}
|
|
194
|
-
/**
|
|
195
|
-
* Flatten content to the prose a human would read: text parts only, separated
|
|
196
|
-
* by blank lines, since the result is spliced into another turn's message body.
|
|
197
|
-
*/
|
|
198
|
-
function textFromServingContent(content) {
|
|
199
|
-
return openaiChat.chatContentToText(content, { separator: "\n\n", types: ["text"] });
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* Whether a turn carries nothing worth replaying. Deliberately stricter than
|
|
203
|
-
* "has no text": a part of any other type (an image, a cache marker) counts as
|
|
204
|
-
* content, so a message holding one is kept even though it has no prose.
|
|
205
|
-
*/
|
|
206
|
-
function isEmptyServingContent(content) {
|
|
207
|
-
if (content === undefined)
|
|
208
|
-
return true;
|
|
209
|
-
if (typeof content === "string")
|
|
210
|
-
return content.trim().length === 0;
|
|
211
|
-
const parts = openaiChat.chatContentParts(content);
|
|
212
|
-
if (!parts)
|
|
213
|
-
return true;
|
|
214
|
-
return parts.every((part) => {
|
|
215
|
-
if (part?.type === "text") {
|
|
216
|
-
return typeof part.text !== "string" || part.text.trim().length === 0;
|
|
217
|
-
}
|
|
218
|
-
return false;
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* Parse, sanitize, and re-serialize a `/serving-endpoints/...` non-streaming
|
|
223
|
-
* JSON RESPONSE body. Returns the original string verbatim when the body is
|
|
224
|
-
* not JSON or no rewrite was needed, mirroring
|
|
225
|
-
* {@link rewriteServingBody} on the request side.
|
|
226
|
-
*/
|
|
227
|
-
export function rewriteServingResponseBody(body) {
|
|
228
|
-
const parsed = json.parseRecord(body);
|
|
229
|
-
if (!parsed)
|
|
230
|
-
return body;
|
|
231
|
-
return flattenChoiceMessageContent(parsed) ? JSON.stringify(parsed) : body;
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* Repair array-valued `choices[].delta.content` inside an OpenAI SSE stream
|
|
235
|
-
* without buffering the response. The decoder retains partial lines across
|
|
236
|
-
* arbitrary network chunk boundaries; each complete `data:` line is parsed and
|
|
237
|
-
* re-encoded only when its content needs normalization.
|
|
238
|
-
*/
|
|
239
|
-
export function rewriteServingResponseStream(body) {
|
|
240
|
-
const decoder = new TextDecoder();
|
|
241
|
-
const encoder = new TextEncoder();
|
|
242
|
-
let buffer = "";
|
|
243
|
-
return body.pipeThrough(new TransformStream({
|
|
244
|
-
transform(chunk, controller) {
|
|
245
|
-
buffer += decoder.decode(chunk, { stream: true });
|
|
246
|
-
let newline = buffer.indexOf("\n");
|
|
247
|
-
while (newline >= 0) {
|
|
248
|
-
const line = buffer.slice(0, newline + 1);
|
|
249
|
-
buffer = buffer.slice(newline + 1);
|
|
250
|
-
controller.enqueue(encoder.encode(rewriteServingResponseStreamLine(line)));
|
|
251
|
-
newline = buffer.indexOf("\n");
|
|
252
|
-
}
|
|
253
|
-
},
|
|
254
|
-
flush(controller) {
|
|
255
|
-
buffer += decoder.decode();
|
|
256
|
-
if (buffer)
|
|
257
|
-
controller.enqueue(encoder.encode(rewriteServingResponseStreamLine(buffer)));
|
|
258
|
-
},
|
|
259
|
-
}));
|
|
260
|
-
}
|
|
261
|
-
/** Normalize one SSE line while preserving its `data:` spacing and line ending. */
|
|
262
|
-
function rewriteServingResponseStreamLine(line) {
|
|
263
|
-
const lineEnding = line.endsWith("\r\n") ? "\r\n" : line.endsWith("\n") ? "\n" : "";
|
|
264
|
-
const content = lineEnding ? line.slice(0, -lineEnding.length) : line;
|
|
265
|
-
const match = /^(data:\s*)(.*)$/.exec(content);
|
|
266
|
-
if (!match || match[2] === "[DONE]")
|
|
267
|
-
return line;
|
|
268
|
-
const parsed = json.parseRecord(match[2]);
|
|
269
|
-
if (!parsed || !flattenChoiceDeltaContent(parsed))
|
|
270
|
-
return line;
|
|
271
|
-
return `${match[1]}${JSON.stringify(parsed)}${lineEnding}`;
|
|
272
|
-
}
|
|
273
|
-
/**
|
|
274
|
-
* Collapse a structured `choices[].message.content` array to the plain string
|
|
275
|
-
* the OpenAI Chat Completions contract specifies.
|
|
276
|
-
*
|
|
277
|
-
* Databricks-hosted Gemini answers a non-streaming `/chat/completions` call
|
|
278
|
-
* with the Gemini-native parts shape:
|
|
279
|
-
*
|
|
280
|
-
* ```json
|
|
281
|
-
* "content": [{ "type": "text", "text": "...", "thoughtSignature": "..." }]
|
|
282
|
-
* ```
|
|
283
|
-
*
|
|
284
|
-
* `@ai-sdk/openai-compatible` validates the response against the OpenAI
|
|
285
|
-
* schema, where `content` is a nullable string, so it throws
|
|
286
|
-
* `Type validation failed: ... expected string, received array` and the whole
|
|
287
|
-
* call fails (`AI_APICallError: Invalid JSON response` on an HTTP 200). Mastra
|
|
288
|
-
* uses `doGenerate` for its side calls, so the visible symptom is
|
|
289
|
-
* `Error generating title` - every thread keeps its placeholder name.
|
|
290
|
-
*
|
|
291
|
-
* Flattening on the wire keeps the repair in one place: neither the agent's
|
|
292
|
-
* stored transcript nor the UI has to know the provider emitted parts. Any
|
|
293
|
-
* non-text part (a reasoning block, `thoughtSignature`-only entry, or inline
|
|
294
|
-
* image) contributes nothing, matching {@link openaiChat.chatContentToText},
|
|
295
|
-
* and an all-parts-empty message flattens to `""` rather than being dropped, so
|
|
296
|
-
* `finish_reason` and `usage` still round-trip.
|
|
297
|
-
*/
|
|
298
|
-
export function flattenChoiceMessageContent(payload) {
|
|
299
|
-
return flattenChoiceContent(payload, "message");
|
|
300
|
-
}
|
|
301
|
-
/**
|
|
302
|
-
* Collapse structured streaming `choices[].delta.content` arrays to strings.
|
|
303
|
-
* Claude reasoning-only chunks become an empty content delta while text parts
|
|
304
|
-
* are concatenated in order.
|
|
305
|
-
*/
|
|
306
|
-
export function flattenChoiceDeltaContent(payload) {
|
|
307
|
-
return flattenChoiceContent(payload, "delta");
|
|
308
|
-
}
|
|
309
|
-
/** Shared choice walker for buffered `message` and streaming `delta` payloads. */
|
|
310
|
-
function flattenChoiceContent(payload, field) {
|
|
311
|
-
if (!Array.isArray(payload.choices))
|
|
312
|
-
return false;
|
|
313
|
-
let changed = false;
|
|
314
|
-
for (const choice of payload.choices) {
|
|
315
|
-
if (!choice || typeof choice !== "object")
|
|
316
|
-
continue;
|
|
317
|
-
const container = choice[field];
|
|
318
|
-
if (!container || typeof container !== "object")
|
|
319
|
-
continue;
|
|
320
|
-
const target = container;
|
|
321
|
-
const parts = openaiChat.chatContentParts(target.content);
|
|
322
|
-
if (!parts)
|
|
323
|
-
continue;
|
|
324
|
-
target.content = openaiChat.chatContentToText(parts, { types: ["text"] });
|
|
325
|
-
changed = true;
|
|
326
|
-
}
|
|
327
|
-
return changed;
|
|
328
|
-
}
|
|
329
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmluZy1zYW5pdGl6ZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zZXJ2aW5nLXNhbml0aXplLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FxQkc7QUFFSCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDMUMsT0FBTyxFQUFFLElBQUksRUFBRSxXQUFXLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUMzRCxPQUFPLEVBQW1DLFVBQVUsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBZXRGLDZFQUE2RTtBQUM3RSxNQUFNLG9CQUFvQixHQUF3QixJQUFJLEdBQUcsQ0FBQztJQUN4RCxXQUFXO0lBQ1gsVUFBVTtJQUNWLG1CQUFtQjtDQUNwQixDQUFDLENBQUM7QUFFSDs7OztHQUlHO0FBQ0gsTUFBTSxVQUFVLGtCQUFrQixDQUFDLElBQVk7SUFDN0MsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUN0QyxJQUFJLENBQUMsTUFBTTtRQUFFLE9BQU8sSUFBSSxDQUFDO0lBRXpCLDZFQUE2RTtJQUM3RSwrRUFBK0U7SUFDL0UsTUFBTSxhQUFhLEdBQUcsK0JBQStCLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDOUQsSUFBSSxPQUFPLEdBQUcsVUFBVSxDQUFDLDBCQUEwQixDQUFDLE1BQU0sQ0FBQyxDQUFDLE1BQU0sR0FBRyxDQUFDLElBQUksYUFBYSxDQUFDO0lBRXhGLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztRQUNuQyxNQUFNLFFBQVEsR0FBRyxNQUFNLENBQUMsUUFBZ0MsQ0FBQztRQUN6RCx1RUFBdUU7UUFDdkUsMEVBQTBFO1FBQzFFLE1BQU0sUUFBUSxHQUFHLGlDQUFpQyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQzdELE1BQU0sUUFBUSxHQUFHLHNC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RSxPQUFPLEdBQUcsSUFBSSxDQUFDO0lBQ2pCLENBQUM7SUFDRCxPQUFPLE9BQU8sQ0FBQztBQUNqQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBSZXBhaXJzIHRyYWZmaWMgYmV0d2VlbiBNYXN0cmEgLyB0aGUgQUkgU0RLIGFuZCBEYXRhYnJpY2tzIE1vZGVsIFNlcnZpbmcnc1xuICogT3BlbkFJLWNvbXBhdGlibGUgYC9jaGF0L2NvbXBsZXRpb25zYCByb3V0ZSwgaW4gYm90aCBkaXJlY3Rpb25zLlxuICpcbiAqIE91dGJvdW5kICh7QGxpbmsgcmV3cml0ZVNlcnZpbmdCb2R5fSksIGJlY2F1c2UgdGhlIHRyYW5zY3JpcHQgTWFzdHJhXG4gKiBwZXJzaXN0cyBpcyBub3QgYWx3YXlzIGEgdHJhbnNjcmlwdCB0aGUgcHJvdmlkZXIgd2lsbCBhY2NlcHQgYmFjazpcbiAqIERhdGFicmlja3MtaG9zdGVkIENsYXVkZSByZWplY3RzIHJlcGxheWVkIGV4dGVuZGVkLXRoaW5raW5nIGJsb2NrcyBhbmQgcmVhZHNcbiAqIGEgdHJhaWxpbmcgYXNzaXN0YW50IG1lc3NhZ2UgYXMgYSBwcmVmaWxsIHJlcXVlc3QsIHdoaWxlIEdQVCB0b29sIGNhbGxzIG5lZWRcbiAqIG1vZGVsLXNwZWNpZmljIHJlYXNvbmluZy1lZmZvcnQgbm9ybWFsaXphdGlvbiBvbiBDaGF0IENvbXBsZXRpb25zLlxuICpcbiAqIEluYm91bmQgKHtAbGluayByZXdyaXRlU2VydmluZ1Jlc3BvbnNlQm9keX0gYW5kXG4gKiB7QGxpbmsgcmV3cml0ZVNlcnZpbmdSZXNwb25zZVN0cmVhbX0pLCBiZWNhdXNlIERhdGFicmlja3MtaG9zdGVkIEdlbWluaSBhbmRcbiAqIENsYXVkZSBjYW4gYW5zd2VyIHdpdGggbmF0aXZlIGNvbnRlbnQtcGFydHMgYXJyYXlzIHdoZXJlIHRoZSBPcGVuQUkgY29udHJhY3RcbiAqIChhbmQgdGhlcmVmb3JlIHRoZSBBSSBTREsncyByZXNwb25zZSBzY2hlbWEpIHJlcXVpcmVzIGEgcGxhaW4gc3RyaW5nLlxuICpcbiAqIEV2ZXJ5IHJlcGFpciBoZXJlIGlzIGEgcHJvdmlkZXIgcXVpcmsgcmF0aGVyIHRoYW4gYSBzY2hlbWEgdmlvbGF0aW9uLCBzbyBhbGxcbiAqIG9mIHRoZW0gYXJlIGFwcGxpZWQgb24gdGhlIHdpcmUgKHNlZSB0aGUgYGdsb2JhbFRoaXMuZmV0Y2hgIHdyYXBwZXIgaW5cbiAqIGBtb2RlbC50c2ApIHJhdGhlciB0aGFuIGJ5IGNoYW5naW5nIHdoYXQgdGhlIGFnZW50IHN0b3JlcyBvciB3aGF0IHRoZSBVSVxuICogc2hvd3MuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7IHBvbGljeSB9IGZyb20gXCJAZGJ4LXRvb2xzL21vZGVsXCI7XG5pbXBvcnQgeyBqc29uLCBzdHJpbmdVdGlscyB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1jb3JlXCI7XG5pbXBvcnQgeyB0eXBlIENoYXRNZXNzYWdlLCB0eXBlIENoYXRSb2xlLCBvcGVuYWlDaGF0IH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLW1vZGVsXCI7XG5cbi8qKlxuICogQSBjaGF0IG1lc3NhZ2UgYXMgaXQgYXJyaXZlcyBvbiB0aGUgc2VydmluZyB3aXJlLCBwbHVzIHRoZSBleHRlbmRlZC10aGlua2luZ1xuICogZmllbGRzIERhdGFicmlja3MtaG9zdGVkIENsYXVkZSBhZGRzLiBUaGUgT3BlbkFJLXN0YW5kYXJkIHBhcnQgb2YgdGhlIHNoYXBlXG4gKiBpcyB7QGxpbmsgQ2hhdE1lc3NhZ2V9IGZyb20gYEBkYngtdG9vbHMvc2hhcmVkLW1vZGVsYDsgb25seSB0aGUgcHJvdmlkZXJcbiAqIGV4dGVuc2lvbnMgdGhpcyBtb2R1bGUgc3RyaXBzIGFyZSBkZWNsYXJlZCBoZXJlLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFNlcnZpbmdDaGF0TWVzc2FnZSBleHRlbmRzIENoYXRNZXNzYWdlIHtcbiAgLyoqIE5hcnJvd2VkIHRvIHRoZSByb2xlcyB0aGlzIHJlcGFpciBwYXNzIHJlYXNvbnMgYWJvdXQsIHBsdXMgQ2xhdWRlJ3MgYHJlYXNvbmluZ2AgdHVybi4gKi9cbiAgcm9sZTogQ2hhdFJvbGUgfCBcInJlYXNvbmluZ1wiO1xuICByZWFzb25pbmc/OiB1bmtub3duO1xuICByZWFzb25pbmdfY29udGVudD86IHVua25vd247XG59XG5cbi8qKiBTaWduZWQgQ2xhdWRlIHJlYXNvbmluZyBibG9ja3MgdGhhdCBjYW5ub3QgYmUgcmVwbGF5ZWQgYWZ0ZXIgbXV0YXRpb24uICovXG5jb25zdCBSRUFTT05JTkdfUEFSVF9UWVBFUzogUmVhZG9ubHlTZXQ8c3RyaW5nPiA9IG5ldyBTZXQoW1xuICBcInJlYXNvbmluZ1wiLFxuICBcInRoaW5raW5nXCIsXG4gIFwicmVkYWN0ZWRfdGhpbmtpbmdcIixcbl0pO1xuXG4vKipcbiAqIFBhcnNlLCBzYW5pdGl6ZSwgYW5kIHJlLXNlcmlhbGl6ZSBhIGAvc2VydmluZy1lbmRwb2ludHMvLi4uYCBQT1NUXG4gKiBib2R5LiBSZXR1cm5zIHRoZSBvcmlnaW5hbCBzdHJpbmcgdmVyYmF0aW0gd2hlbiB0aGUgYm9keSBpcyBub3RcbiAqIEpTT04gb3Igbm8gcmV3cml0ZSB3YXMgbmVlZGVkLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcmV3cml0ZVNlcnZpbmdCb2R5KGJvZHk6IHN0cmluZyk6IHN0cmluZyB7XG4gIGNvbnN0IHBhcnNlZCA9IGpzb24ucGFyc2VSZWNvcmQoYm9keSk7XG4gIGlmICghcGFyc2VkKSByZXR1cm4gYm9keTtcblxuICAvLyBSdW5zIHJlZ2FyZGxlc3Mgb2YgYG1lc3NhZ2VzYDogRGF0YWJyaWNrcyByZWZ1c2VzIHRvIHBhcnNlIGEgYm9keSBjYXJyeWluZ1xuICAvLyBhbiB1bmtub3duIHRvcC1sZXZlbCBmaWVsZCwgc28gdGhpcyBmYWlsdXJlIGlzIG5vdCBzcGVjaWZpYyB0byBhIHRyYW5zY3JpcHQuXG4gIGNvbnN0IHRvb2xSZWFzb25pbmcgPSBhcHBseVRvb2xSZWFzb25pbmdDb21wYXRpYmlsaXR5KHBhcnNlZCk7XG4gIGxldCBjaGFuZ2VkID0gb3BlbmFpQ2hhdC5zdHJpcFVuc3VwcG9ydGVkQ2hhdEZpZWxkcyhwYXJzZWQpLmxlbmd0aCA+IDAgfHwgdG9vbFJlYXNvbmluZztcblxuICBpZiAoQXJyYXkuaXNBcnJheShwYXJzZWQubWVzc2FnZXMpKSB7XG4gICAgY29uc3QgbWVzc2FnZXMgPSBwYXJzZWQubWVzc2FnZXMgYXMgU2VydmluZ0NoYXRNZXNzYWdlW107XG4gICAgLy8gRXZhbHVhdGVkIGVhZ2VybHksIG5vdCBzaG9ydC1jaXJjdWl0ZWQ6IG9uZSB0cmFuc2NyaXB0IGNhbiBuZWVkIGJvdGhcbiAgICAvLyByZWFzb25pbmcgYmxvY2tzIHN0cmlwcGVkIEFORCBhIHRyYWlsaW5nIGFzc2lzdGFudCBwcmVmaWxsIGZvbGRlZCBiYWNrLlxuICAgIGNvbnN0IHN0cmlwcGVkID0gc3RyaXBSZWFzb25pbmdGcm9tU2VydmluZ01lc3NhZ2VzKG1lc3NhZ2VzKTtcbiAgICBjb25zdCByZXBhaXJlZCA9IHJlcGFpckFzc2lzdGFudFByZWZpbGwobWVzc2FnZXMpO1xuICAgIGNoYW5nZWQgPSBjaGFuZ2VkIHx8IHN0cmlwcGVkIHx8IHJlcGFpcmVkO1xuICB9XG5cbiAgcmV0dXJuIGNoYW5nZWQgPyBKU09OLnN0cmluZ2lmeShwYXJzZWQpIDogYm9keTtcbn1cblxuLyoqIFByZXBhcmVkIGZldGNoIGFyZ3VtZW50cyBwbHVzIHRoZSBwb3N0LXNhbml0aXplIGJvZHkgdXNlZCBmb3IgZGlhZ25vc3RpY3MuICovXG5leHBvcnQgaW50ZXJmYWNlIFJld3JpdHRlblNlcnZpbmdSZXF1ZXN0IHtcbiAgaW5wdXQ6IFBhcmFtZXRlcnM8dHlwZW9mIGZldGNoPlswXTtcbiAgaW5pdDogUGFyYW1ldGVyczx0eXBlb2YgZmV0Y2g+WzFdO1xuICBib2R5OiBzdHJpbmc7XG59XG5cbi8qKlxuICogUmVhZCBhbmQgcmV3cml0ZSBhIHNlcnZpbmcgUE9TVCB3aGV0aGVyIGl0cyBKU09OIGJvZHkgbGl2ZXMgb24gYGluaXRgIG9yIG9uXG4gKiBhIGBSZXF1ZXN0YC4gQSBjaGFuZ2VkIFJlcXVlc3QgaXMgcmVidWlsdCB3aXRob3V0IHN0YWxlIGJ5dGUgaGVhZGVycy5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJld3JpdGVTZXJ2aW5nUmVxdWVzdChcbiAgaW5wdXQ6IFBhcmFtZXRlcnM8dHlwZW9mIGZldGNoPlswXSxcbiAgaW5pdD86IFBhcmFtZXRlcnM8dHlwZW9mIGZldGNoPlsxXSxcbik6IFByb21pc2U8UmV3cml0dGVuU2VydmluZ1JlcXVlc3Q+IHtcbiAgY29uc3QgcmVxdWVzdCA9IG5ldyBSZXF1ZXN0KGlucHV0LCBpbml0KTtcbiAgY29uc3QgYm9keSA9IGF3YWl0IHJlcXVlc3QuY2xvbmUoKS50ZXh0KCk7XG4gIGNvbnN0IHJld3JpdHRlbiA9IHJld3JpdGVTZXJ2aW5nQm9keShib2R5KTtcbiAgaWYgKHJld3JpdHRlbiA9PT0gYm9keSkgcmV0dXJuIHsgaW5wdXQsIGluaXQsIGJvZHkgfTtcblxuICBjb25zdCBoZWFkZXJzID0gbmV3IEhlYWRlcnMocmVxdWVzdC5oZWFkZXJzKTtcbiAgaGVhZGVycy5kZWxldGUoXCJjb250ZW50LWxlbmd0aFwiKTtcbiAgaGVhZGVycy5kZWxldGUoXCJjb250ZW50LWVuY29kaW5nXCIpO1xuICByZXR1cm4ge1xuICAgIGlucHV0OiBuZXcgUmVxdWVzdChyZXF1ZXN0LCB7IGJvZHk6IHJld3JpdHRlbiwgaGVhZGVycyB9KSxcbiAgICBpbml0OiB1bmRlZmluZWQsXG4gICAgYm9keTogcmV3cml0dGVuLFxuICB9O1xufVxuXG4vKipcbiAqIE5vcm1hbGl6ZSBDaGF0IENvbXBsZXRpb25zIHJlYXNvbmluZyBmb3IgR1BUIG1vZGVscyBjYXJyeWluZyBmdW5jdGlvbiB0b29scy5cbiAqXG4gKiBHUFQgNS42IHJlcXVpcmVzIHJlYXNvbmluZyBkaXNhYmxlZCBmb3IgdG9vbCBjYWxscyBvbiB0aGlzIHJvdXRlLCBzbyBgbm9uZWBcbiAqIHdpbnMgb3ZlciBhIGNhbGxlci9kZWZhdWx0IGVmZm9ydC4gUmVzcG9uc2VzLW9ubHkgbW9kZWxzIGFyZSByb3V0ZWQgYXdheVxuICogZnJvbSBDaGF0IENvbXBsZXRpb25zIGJlZm9yZSB0aGlzIHNhbml0aXplciBydW5zLlxuICovXG5leHBvcnQgZnVuY3Rpb24gYXBwbHlUb29sUmVhc29uaW5nQ29tcGF0aWJpbGl0eShib2R5OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPik6IGJvb2xlYW4ge1xuICBpZiAoIUFycmF5LmlzQXJyYXkoYm9keS50b29scykgfHwgYm9keS50b29scy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbiAgY29uc3QgbW9kZWwgPSBzdHJpbmdVdGlscy50cmltVG9OdWxsKGJvZHkubW9kZWwpO1xuICBpZiAoIW1vZGVsKSByZXR1cm4gZmFsc2U7XG4gIGlmIChwb2xpY3kuY2hhdFRvb2xSZWFzb25pbmdFZmZvcnQobW9kZWwpICE9PSBcIm5vbmVcIikgcmV0dXJuIGZhbHNlO1xuICBpZiAoYm9keS5yZWFzb25pbmdfZWZmb3J0ID09PSBcIm5vbmVcIikgcmV0dXJuIGZhbHNlO1xuICBib2R5LnJlYXNvbmluZ19lZmZvcnQgPSBcIm5vbmVcIjtcbiAgcmV0dXJuIHRydWU7XG59XG5cbi8qKlxuICogRHJvcCBleHRlbmRlZC10aGlua2luZyAvIHJlYXNvbmluZyBibG9ja3MgZnJvbSBhIHJlcGxheWVkIHRyYW5zY3JpcHQuXG4gKlxuICogSHlicmlkIENsYXVkZSBlbmRwb2ludHMgKGUuZy4gU29ubmV0IDQuNSspIG1heSBlbWl0IGByZWFzb25pbmdgIGNvbnRlbnRcbiAqIHBhcnRzIG9uIHRoZSBmaXJzdCB0dXJuLiBNYXN0cmEgcGVyc2lzdHMgYW5kIHJlcGxheXMgdGhlbSBvbiB0aGUgbmV4dFxuICogYWdlbnQgc3RlcCwgYnV0IERhdGFicmlja3MtaG9zdGVkIENsYXVkZSByZWplY3RzIHRob3NlIGJsb2NrcyBvblxuICogbXVsdGktdHVybiB0b29sIGNvbnRpbnVhdGlvbnMuIFRoZSBVSSBhbHJlYWR5IGNhcHR1cmVkIHJlYXNvbmluZyBmb3JcbiAqIGRpc3BsYXk7IHN0cmlwcGluZyBoZXJlIGtlZXBzIHByb3ZpZGVyIHJlcGxheSBjb21wYXRpYmxlIHdpdGhvdXRcbiAqIGNoYW5naW5nIHdoYXQgdXNlcnMgc2VlIGluIHRoZSBjaGF0IGJ1YmJsZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHN0cmlwUmVhc29uaW5nRnJvbVNlcnZpbmdNZXNzYWdlcyhtZXNzYWdlczogU2VydmluZ0NoYXRNZXNzYWdlW10pOiBib29sZWFuIHtcbiAgbGV0IGNoYW5nZWQgPSBmYWxzZTtcbiAgZm9yIChsZXQgaSA9IG1lc3NhZ2VzLmxlbmd0aCAtIDE7IGkgPj0gMDsgaS0tKSB7XG4gICAgY29uc3QgbXNnID0gbWVzc2FnZXNbaV07XG4gICAgaWYgKCFtc2cpIGNvbnRpbnVlO1xuICAgIGlmIChtc2cucm9sZSA9PT0gXCJyZWFzb25pbmdcIikge1xuICAgICAgbWVzc2FnZXMuc3BsaWNlKGksIDEpO1xuICAgICAgY2hhbmdlZCA9IHRydWU7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG4gICAgaWYgKG1zZy5yZWFzb25pbmcgIT09IHVuZGVmaW5lZCkge1xuICAgICAgZGVsZXRlIG1zZy5yZWFzb25pbmc7XG4gICAgICBjaGFuZ2VkID0gdHJ1ZTtcbiAgICB9XG4gICAgaWYgKG1zZy5yZWFzb25pbmdfY29udGVudCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBkZWxldGUgbXNnLnJlYXNvbmluZ19jb250ZW50O1xuICAgICAgY2hhbmdlZCA9IHRydWU7XG4gICAgfVxuICAgIGNvbnN0IHBhcnRzID0gb3BlbmFpQ2hhdC5jaGF0Q29udGVudFBhcnRzKG1zZy5jb250ZW50KTtcbiAgICBpZiAoIXBhcnRzKSBjb250aW51ZTtcbiAgICBjb25zdCBmaWx0ZXJlZCA9IHBhcnRzLmZpbHRlcigocGFydCkgPT4ge1xuICAgICAgY29uc3QgdHlwZSA9IHBhcnQ/LnR5cGU7XG4gICAgICBpZiAodHlwZW9mIHR5cGUgPT09IFwic3RyaW5nXCIgJiYgUkVBU09OSU5HX1BBUlRfVFlQRVMuaGFzKHR5cGUpKSB7XG4gICAgICAgIGNoYW5nZWQgPSB0cnVlO1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9KTtcbiAgICBpZiAoZmlsdGVyZWQubGVuZ3RoICE9PSBwYXJ0cy5sZW5ndGgpIHtcbiAgICAgIG1zZy5jb250ZW50ID0gZmlsdGVyZWQ7XG4gICAgfVxuICAgIGNvbnN0IGhhc1Rvb2xDYWxscyA9IEFycmF5LmlzQXJyYXkobXNnLnRvb2xfY2FsbHMpICYmIG1zZy50b29sX2NhbGxzLmxlbmd0aCA+IDA7XG4gICAgaWYgKG1zZy5yb2xlID09PSBcImFzc2lzdGFudFwiICYmICFoYXNUb29sQ2FsbHMgJiYgaXNFbXB0eVNlcnZpbmdDb250ZW50KG1zZy5jb250ZW50KSkge1xuICAgICAgbWVzc2FnZXMuc3BsaWNlKGksIDEpO1xuICAgICAgY2hhbmdlZCA9IHRydWU7XG4gICAgfVxuICB9XG4gIHJldHVybiBjaGFuZ2VkO1xufVxuXG4vKipcbiAqIFJlcGFpciBhIE1hc3RyYS9BSSBTREsgbWVzc2FnZSByZXBsYXkgdGhhdCBEYXRhYnJpY2tzLWhvc3RlZCBDbGF1ZGVcbiAqIHJlamVjdHMgd2l0aCBgXCJUaGlzIG1vZGVsIGRvZXMgbm90IHN1cHBvcnQgYXNzaXN0YW50IG1lc3NhZ2VcbiAqIHByZWZpbGwuIFRoZSBjb252ZXJzYXRpb24gbXVzdCBlbmQgd2l0aCBhIHVzZXIgbWVzc2FnZS5cImAuXG4gKlxuICogVGhlIGJ1ZyBwYXR0ZXJuOiB3aGVuIGFuIGFzc2lzdGFudCB0dXJuIHN0cmVhbXMgdGV4dCAqYW5kKiBhXG4gKiBgdG9vbF9jYWxsYCwgdGhlIEFJIFNESyBwZXJzaXN0cyB0aGVtIGFzIHR3byBzZXBhcmF0ZSBhc3Npc3RhbnRcbiAqIGVudHJpZXMgKHRleHQtb25seSBhbmQgdG9vbC1jYWxsLW9ubHkpLiBPbiB0aGUgbmV4dCBhZ2VudCBzdGVwIHRoZVxuICogdG9vbC1jYWxsIGVudHJ5IGlzIHJlcGxheWVkICpiZWZvcmUqIHRoZSB0b29sIHJlc3VsdCBhbmQgdGhlXG4gKiB0ZXh0IGVudHJ5IGlzIHJlcGxheWVkICphZnRlciogaXQsIHNvIHRoZSBjb252ZXJzYXRpb24gZW5kcyB3aXRoIGFcbiAqIHRyYWlsaW5nIGFzc2lzdGFudCB0ZXh0IG1lc3NhZ2UuIEFudGhyb3BpYyBpbnRlcnByZXRzIHRoYXQgYXMgYVxuICogcHJlZmlsbCByZXF1ZXN0IGFuZCByZWplY3RzIGl0IG9uIERhdGFicmlja3MgKHRoZSB1cHN0cmVhbSBCZWRyb2NrXG4gKiByb3V0ZSBkaXNhbGxvd3MgcHJlZmlsbCkuXG4gKlxuICogRml4OiB3aGVuIHRoZSBsYXN0IG1lc3NhZ2UgaXMgYW4gYXNzaXN0YW50IHRleHQgd2l0aCBubyBgdG9vbF9jYWxsc2BcbiAqIGFuZCB0aGUgY2hhaW4gaW1tZWRpYXRlbHkgYmVmb3JlIGl0IGlzIGBhc3Npc3RhbnQodG9vbF9jYWxscz0uLi4pYFxuICogZm9sbG93ZWQgb25seSBieSBgdG9vbCguLi4pYCByZXN1bHRzLCBmb2xkIHRoZSB0cmFpbGluZyB0ZXh0IGJhY2tcbiAqIGludG8gdGhlIGBjb250ZW50YCBvZiB0aGF0IG9wZW5pbmcgYXNzaXN0YW50IGFuZCBkcm9wIHRoZSBkdXBsaWNhdGUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXBhaXJBc3Npc3RhbnRQcmVmaWxsKG1lc3NhZ2VzOiBTZXJ2aW5nQ2hhdE1lc3NhZ2VbXSk6IGJvb2xlYW4ge1xuICBpZiAobWVzc2FnZXMubGVuZ3RoIDwgMikgcmV0dXJuIGZhbHNlO1xuICBjb25zdCBsYXN0ID0gbWVzc2FnZXNbbWVzc2FnZXMubGVuZ3RoIC0gMV07XG4gIGlmICghbGFzdCB8fCBsYXN0LnJvbGUgIT09IFwiYXNzaXN0YW50XCIgfHwgKGxhc3QudG9vbF9jYWxscyAmJiBsYXN0LnRvb2xfY2FsbHMubGVuZ3RoID4gMCkpIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICBsZXQgaSA9IG1lc3NhZ2VzLmxlbmd0aCAtIDI7XG4gIHdoaWxlIChpID49IDAgJiYgbWVzc2FnZXNbaV0/LnJvbGUgPT09IFwidG9vbFwiKSBpLS07XG4gIGlmIChpIDwgMCkgcmV0dXJuIGZhbHNlO1xuICBjb25zdCBvcGVuZXIgPSBtZXNzYWdlc1tpXTtcbiAgaWYgKFxuICAgICFvcGVuZXIgfHxcbiAgICBvcGVuZXIucm9sZSAhPT0gXCJhc3Npc3RhbnRcIiB8fFxuICAgICFvcGVuZXIudG9vbF9jYWxscyB8fFxuICAgIG9wZW5lci50b29sX2NhbGxzLmxlbmd0aCA9PT0gMFxuICApIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICBjb25zdCBtZXJnZWQgPSBbXG4gICAgc3RyaW5nVXRpbHMudHJpbVRvTnVsbCh0ZXh0RnJvbVNlcnZpbmdDb250ZW50KG9wZW5lci5jb250ZW50KSksXG4gICAgc3RyaW5nVXRpbHMudHJpbVRvTnVsbCh0ZXh0RnJvbVNlcnZpbmdDb250ZW50KGxhc3QuY29udGVudCkpLFxuICBdXG4gICAgLmZpbHRlcigocyk6IHMgaXMgc3RyaW5nID0+IHMgIT09IG51bGwpXG4gICAgLmpvaW4oXCJcXG5cXG5cIik7XG4gIG9wZW5lci5jb250ZW50ID0gbWVyZ2VkO1xuICBtZXNzYWdlcy5wb3AoKTtcbiAgcmV0dXJuIHRydWU7XG59XG5cbi8qKlxuICogRmxhdHRlbiBjb250ZW50IHRvIHRoZSBwcm9zZSBhIGh1bWFuIHdvdWxkIHJlYWQ6IHRleHQgcGFydHMgb25seSwgc2VwYXJhdGVkXG4gKiBieSBibGFuayBsaW5lcywgc2luY2UgdGhlIHJlc3VsdCBpcyBzcGxpY2VkIGludG8gYW5vdGhlciB0dXJuJ3MgbWVzc2FnZSBib2R5LlxuICovXG5mdW5jdGlvbiB0ZXh0RnJvbVNlcnZpbmdDb250ZW50KGNvbnRlbnQ6IFNlcnZpbmdDaGF0TWVzc2FnZVtcImNvbnRlbnRcIl0pOiBzdHJpbmcge1xuICByZXR1cm4gb3BlbmFpQ2hhdC5jaGF0Q29udGVudFRvVGV4dChjb250ZW50LCB7IHNlcGFyYXRvcjogXCJcXG5cXG5cIiwgdHlwZXM6IFtcInRleHRcIl0gfSk7XG59XG5cbi8qKlxuICogV2hldGhlciBhIHR1cm4gY2FycmllcyBub3RoaW5nIHdvcnRoIHJlcGxheWluZy4gRGVsaWJlcmF0ZWx5IHN0cmljdGVyIHRoYW5cbiAqIFwiaGFzIG5vIHRleHRcIjogYSBwYXJ0IG9mIGFueSBvdGhlciB0eXBlIChhbiBpbWFnZSwgYSBjYWNoZSBtYXJrZXIpIGNvdW50cyBhc1xuICogY29udGVudCwgc28gYSBtZXNzYWdlIGhvbGRpbmcgb25lIGlzIGtlcHQgZXZlbiB0aG91Z2ggaXQgaGFzIG5vIHByb3NlLlxuICovXG5mdW5jdGlvbiBpc0VtcHR5U2VydmluZ0NvbnRlbnQoY29udGVudDogU2VydmluZ0NoYXRNZXNzYWdlW1wiY29udGVudFwiXSk6IGJvb2xlYW4ge1xuICBpZiAoY29udGVudCA9PT0gdW5kZWZpbmVkKSByZXR1cm4gdHJ1ZTtcbiAgaWYgKHR5cGVvZiBjb250ZW50ID09PSBcInN0cmluZ1wiKSByZXR1cm4gY29udGVudC50cmltKCkubGVuZ3RoID09PSAwO1xuICBjb25zdCBwYXJ0cyA9IG9wZW5haUNoYXQuY2hhdENvbnRlbnRQYXJ0cyhjb250ZW50KTtcbiAgaWYgKCFwYXJ0cykgcmV0dXJuIHRydWU7XG4gIHJldHVybiBwYXJ0cy5ldmVyeSgocGFydCkgPT4ge1xuICAgIGlmIChwYXJ0Py50eXBlID09PSBcInRleHRcIikge1xuICAgICAgcmV0dXJuIHR5cGVvZiBwYXJ0LnRleHQgIT09IFwic3RyaW5nXCIgfHwgcGFydC50ZXh0LnRyaW0oKS5sZW5ndGggPT09IDA7XG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbiAgfSk7XG59XG5cbi8qKlxuICogUGFyc2UsIHNhbml0aXplLCBhbmQgcmUtc2VyaWFsaXplIGEgYC9zZXJ2aW5nLWVuZHBvaW50cy8uLi5gIG5vbi1zdHJlYW1pbmdcbiAqIEpTT04gUkVTUE9OU0UgYm9keS4gUmV0dXJucyB0aGUgb3JpZ2luYWwgc3RyaW5nIHZlcmJhdGltIHdoZW4gdGhlIGJvZHkgaXNcbiAqIG5vdCBKU09OIG9yIG5vIHJld3JpdGUgd2FzIG5lZWRlZCwgbWlycm9yaW5nXG4gKiB7QGxpbmsgcmV3cml0ZVNlcnZpbmdCb2R5fSBvbiB0aGUgcmVxdWVzdCBzaWRlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcmV3cml0ZVNlcnZpbmdSZXNwb25zZUJvZHkoYm9keTogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3QgcGFyc2VkID0ganNvbi5wYXJzZVJlY29yZChib2R5KTtcbiAgaWYgKCFwYXJzZWQpIHJldHVybiBib2R5O1xuICByZXR1cm4gZmxhdHRlbkNob2ljZU1lc3NhZ2VDb250ZW50KHBhcnNlZCkgPyBKU09OLnN0cmluZ2lmeShwYXJzZWQpIDogYm9keTtcbn1cblxuLyoqXG4gKiBSZXBhaXIgYXJyYXktdmFsdWVkIGBjaG9pY2VzW10uZGVsdGEuY29udGVudGAgaW5zaWRlIGFuIE9wZW5BSSBTU0Ugc3RyZWFtXG4gKiB3aXRob3V0IGJ1ZmZlcmluZyB0aGUgcmVzcG9uc2UuIFRoZSBkZWNvZGVyIHJldGFpbnMgcGFydGlhbCBsaW5lcyBhY3Jvc3NcbiAqIGFyYml0cmFyeSBuZXR3b3JrIGNodW5rIGJvdW5kYXJpZXM7IGVhY2ggY29tcGxldGUgYGRhdGE6YCBsaW5lIGlzIHBhcnNlZCBhbmRcbiAqIHJlLWVuY29kZWQgb25seSB3aGVuIGl0cyBjb250ZW50IG5lZWRzIG5vcm1hbGl6YXRpb24uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXdyaXRlU2VydmluZ1Jlc3BvbnNlU3RyZWFtKFxuICBib2R5OiBSZWFkYWJsZVN0cmVhbTxVaW50OEFycmF5Pixcbik6IFJlYWRhYmxlU3RyZWFtPFVpbnQ4QXJyYXk+IHtcbiAgY29uc3QgZGVjb2RlciA9IG5ldyBUZXh0RGVjb2RlcigpO1xuICBjb25zdCBlbmNvZGVyID0gbmV3IFRleHRFbmNvZGVyKCk7XG4gIGxldCBidWZmZXIgPSBcIlwiO1xuXG4gIHJldHVybiBib2R5LnBpcGVUaHJvdWdoKFxuICAgIG5ldyBUcmFuc2Zvcm1TdHJlYW08VWludDhBcnJheSwgVWludDhBcnJheT4oe1xuICAgICAgdHJhbnNmb3JtKGNodW5rLCBjb250cm9sbGVyKSB7XG4gICAgICAgIGJ1ZmZlciArPSBkZWNvZGVyLmRlY29kZShjaHVuaywgeyBzdHJlYW06IHRydWUgfSk7XG4gICAgICAgIGxldCBuZXdsaW5lID0gYnVmZmVyLmluZGV4T2YoXCJcXG5cIik7XG4gICAgICAgIHdoaWxlIChuZXdsaW5lID49IDApIHtcbiAgICAgICAgICBjb25zdCBsaW5lID0gYnVmZmVyLnNsaWNlKDAsIG5ld2xpbmUgKyAxKTtcbiAgICAgICAgICBidWZmZXIgPSBidWZmZXIuc2xpY2UobmV3bGluZSArIDEpO1xuICAgICAgICAgIGNvbnRyb2xsZXIuZW5xdWV1ZShlbmNvZGVyLmVuY29kZShyZXdyaXRlU2VydmluZ1Jlc3BvbnNlU3RyZWFtTGluZShsaW5lKSkpO1xuICAgICAgICAgIG5ld2xpbmUgPSBidWZmZXIuaW5kZXhPZihcIlxcblwiKTtcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgIGZsdXNoKGNvbnRyb2xsZXIpIHtcbiAgICAgICAgYnVmZmVyICs9IGRlY29kZXIuZGVjb2RlKCk7XG4gICAgICAgIGlmIChidWZmZXIpIGNvbnRyb2xsZXIuZW5xdWV1ZShlbmNvZGVyLmVuY29kZShyZXdyaXRlU2VydmluZ1Jlc3BvbnNlU3RyZWFtTGluZShidWZmZXIpKSk7XG4gICAgICB9LFxuICAgIH0pLFxuICApO1xufVxuXG4vKiogTm9ybWFsaXplIG9uZSBTU0UgbGluZSB3aGlsZSBwcmVzZXJ2aW5nIGl0cyBgZGF0YTpgIHNwYWNpbmcgYW5kIGxpbmUgZW5kaW5nLiAqL1xuZnVuY3Rpb24gcmV3cml0ZVNlcnZpbmdSZXNwb25zZVN0cmVhbUxpbmUobGluZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3QgbGluZUVuZGluZyA9IGxpbmUuZW5kc1dpdGgoXCJcXHJcXG5cIikgPyBcIlxcclxcblwiIDogbGluZS5lbmRzV2l0aChcIlxcblwiKSA/IFwiXFxuXCIgOiBcIlwiO1xuICBjb25zdCBjb250ZW50ID0gbGluZUVuZGluZyA/IGxpbmUuc2xpY2UoMCwgLWxpbmVFbmRpbmcubGVuZ3RoKSA6IGxpbmU7XG4gIGNvbnN0IG1hdGNoID0gL14oZGF0YTpcXHMqKSguKikkLy5leGVjKGNvbnRlbnQpO1xuICBpZiAoIW1hdGNoIHx8IG1hdGNoWzJdID09PSBcIltET05FXVwiKSByZXR1cm4gbGluZTtcblxuICBjb25zdCBwYXJzZWQgPSBqc29uLnBhcnNlUmVjb3JkKG1hdGNoWzJdKTtcbiAgaWYgKCFwYXJzZWQgfHwgIWZsYXR0ZW5DaG9pY2VEZWx0YUNvbnRlbnQocGFyc2VkKSkgcmV0dXJuIGxpbmU7XG4gIHJldHVybiBgJHttYXRjaFsxXX0ke0pTT04uc3RyaW5naWZ5KHBhcnNlZCl9JHtsaW5lRW5kaW5nfWA7XG59XG5cbi8qKlxuICogQ29sbGFwc2UgYSBzdHJ1Y3R1cmVkIGBjaG9pY2VzW10ubWVzc2FnZS5jb250ZW50YCBhcnJheSB0byB0aGUgcGxhaW4gc3RyaW5nXG4gKiB0aGUgT3BlbkFJIENoYXQgQ29tcGxldGlvbnMgY29udHJhY3Qgc3BlY2lmaWVzLlxuICpcbiAqIERhdGFicmlja3MtaG9zdGVkIEdlbWluaSBhbnN3ZXJzIGEgbm9uLXN0cmVhbWluZyBgL2NoYXQvY29tcGxldGlvbnNgIGNhbGxcbiAqIHdpdGggdGhlIEdlbWluaS1uYXRpdmUgcGFydHMgc2hhcGU6XG4gKlxuICogYGBganNvblxuICogXCJjb250ZW50XCI6IFt7IFwidHlwZVwiOiBcInRleHRcIiwgXCJ0ZXh0XCI6IFwiLi4uXCIsIFwidGhvdWdodFNpZ25hdHVyZVwiOiBcIi4uLlwiIH1dXG4gKiBgYGBcbiAqXG4gKiBgQGFpLXNkay9vcGVuYWktY29tcGF0aWJsZWAgdmFsaWRhdGVzIHRoZSByZXNwb25zZSBhZ2FpbnN0IHRoZSBPcGVuQUlcbiAqIHNjaGVtYSwgd2hlcmUgYGNvbnRlbnRgIGlzIGEgbnVsbGFibGUgc3RyaW5nLCBzbyBpdCB0aHJvd3NcbiAqIGBUeXBlIHZhbGlkYXRpb24gZmFpbGVkOiAuLi4gZXhwZWN0ZWQgc3RyaW5nLCByZWNlaXZlZCBhcnJheWAgYW5kIHRoZSB3aG9sZVxuICogY2FsbCBmYWlscyAoYEFJX0FQSUNhbGxFcnJvcjogSW52YWxpZCBKU09OIHJlc3BvbnNlYCBvbiBhbiBIVFRQIDIwMCkuIE1hc3RyYVxuICogdXNlcyBgZG9HZW5lcmF0ZWAgZm9yIGl0cyBzaWRlIGNhbGxzLCBzbyB0aGUgdmlzaWJsZSBzeW1wdG9tIGlzXG4gKiBgRXJyb3IgZ2VuZXJhdGluZyB0aXRsZWAgLSBldmVyeSB0aHJlYWQga2VlcHMgaXRzIHBsYWNlaG9sZGVyIG5hbWUuXG4gKlxuICogRmxhdHRlbmluZyBvbiB0aGUgd2lyZSBrZWVwcyB0aGUgcmVwYWlyIGluIG9uZSBwbGFjZTogbmVpdGhlciB0aGUgYWdlbnQnc1xuICogc3RvcmVkIHRyYW5zY3JpcHQgbm9yIHRoZSBVSSBoYXMgdG8ga25vdyB0aGUgcHJvdmlkZXIgZW1pdHRlZCBwYXJ0cy4gQW55XG4gKiBub24tdGV4dCBwYXJ0IChhIHJlYXNvbmluZyBibG9jaywgYHRob3VnaHRTaWduYXR1cmVgLW9ubHkgZW50cnksIG9yIGlubGluZVxuICogaW1hZ2UpIGNvbnRyaWJ1dGVzIG5vdGhpbmcsIG1hdGNoaW5nIHtAbGluayBvcGVuYWlDaGF0LmNoYXRDb250ZW50VG9UZXh0fSxcbiAqIGFuZCBhbiBhbGwtcGFydHMtZW1wdHkgbWVzc2FnZSBmbGF0dGVucyB0byBgXCJcImAgcmF0aGVyIHRoYW4gYmVpbmcgZHJvcHBlZCwgc29cbiAqIGBmaW5pc2hfcmVhc29uYCBhbmQgYHVzYWdlYCBzdGlsbCByb3VuZC10cmlwLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZmxhdHRlbkNob2ljZU1lc3NhZ2VDb250ZW50KHBheWxvYWQ6IFJlY29yZDxzdHJpbmcsIHVua25vd24+KTogYm9vbGVhbiB7XG4gIHJldHVybiBmbGF0dGVuQ2hvaWNlQ29udGVudChwYXlsb2FkLCBcIm1lc3NhZ2VcIik7XG59XG5cbi8qKlxuICogQ29sbGFwc2Ugc3RydWN0dXJlZCBzdHJlYW1pbmcgYGNob2ljZXNbXS5kZWx0YS5jb250ZW50YCBhcnJheXMgdG8gc3RyaW5ncy5cbiAqIENsYXVkZSByZWFzb25pbmctb25seSBjaHVua3MgYmVjb21lIGFuIGVtcHR5IGNvbnRlbnQgZGVsdGEgd2hpbGUgdGV4dCBwYXJ0c1xuICogYXJlIGNvbmNhdGVuYXRlZCBpbiBvcmRlci5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZsYXR0ZW5DaG9pY2VEZWx0YUNvbnRlbnQocGF5bG9hZDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4pOiBib29sZWFuIHtcbiAgcmV0dXJuIGZsYXR0ZW5DaG9pY2VDb250ZW50KHBheWxvYWQsIFwiZGVsdGFcIik7XG59XG5cbi8qKiBTaGFyZWQgY2hvaWNlIHdhbGtlciBmb3IgYnVmZmVyZWQgYG1lc3NhZ2VgIGFuZCBzdHJlYW1pbmcgYGRlbHRhYCBwYXlsb2Fkcy4gKi9cbmZ1bmN0aW9uIGZsYXR0ZW5DaG9pY2VDb250ZW50KFxuICBwYXlsb2FkOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcbiAgZmllbGQ6IFwibWVzc2FnZVwiIHwgXCJkZWx0YVwiLFxuKTogYm9vbGVhbiB7XG4gIGlmICghQXJyYXkuaXNBcnJheShwYXlsb2FkLmNob2ljZXMpKSByZXR1cm4gZmFsc2U7XG4gIGxldCBjaGFuZ2VkID0gZmFsc2U7XG4gIGZvciAoY29uc3QgY2hvaWNlIG9mIHBheWxvYWQuY2hvaWNlcykge1xuICAgIGlmICghY2hvaWNlIHx8IHR5cGVvZiBjaG9pY2UgIT09IFwib2JqZWN0XCIpIGNvbnRpbnVlO1xuICAgIGNvbnN0IGNvbnRhaW5lciA9IChjaG9pY2UgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4pW2ZpZWxkXTtcbiAgICBpZiAoIWNvbnRhaW5lciB8fCB0eXBlb2YgY29udGFpbmVyICE9PSBcIm9iamVjdFwiKSBjb250aW51ZTtcbiAgICBjb25zdCB0YXJnZXQgPSBjb250YWluZXIgYXMgeyBjb250ZW50PzogdW5rbm93biB9O1xuICAgIGNvbnN0IHBhcnRzID0gb3BlbmFpQ2hhdC5jaGF0Q29udGVudFBhcnRzKHRhcmdldC5jb250ZW50KTtcbiAgICBpZiAoIXBhcnRzKSBjb250aW51ZTtcbiAgICB0YXJnZXQuY29udGVudCA9IG9wZW5haUNoYXQuY2hhdENvbnRlbnRUb1RleHQocGFydHMsIHsgdHlwZXM6IFtcInRleHRcIl0gfSk7XG4gICAgY2hhbmdlZCA9IHRydWU7XG4gIH1cbiAgcmV0dXJuIGNoYW5nZWQ7XG59XG4iXX0=
|
|
6
|
+
export { applyToolReasoningCompatibility, flattenChoiceDeltaContent, flattenChoiceMessageContent, repairAssistantPrefill, rewriteServingBody, rewriteServingRequest, rewriteServingResponseBody, rewriteServingResponseStream, stripReasoningFromServingMessages, } from "@dbx-tools/model/serving-wire";
|
|
7
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmluZy1zYW5pdGl6ZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zZXJ2aW5nLXNhbml0aXplLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7O0dBSUc7QUFFSCxPQUFPLEVBQ0wsK0JBQStCLEVBQy9CLHlCQUF5QixFQUN6QiwyQkFBMkIsRUFDM0Isc0JBQXNCLEVBQ3RCLGtCQUFrQixFQUNsQixxQkFBcUIsRUFDckIsMEJBQTBCLEVBQzFCLDRCQUE0QixFQUM1QixpQ0FBaUMsR0FHbEMsTUFBTSwrQkFBK0IsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogRGF0YWJyaWNrcyBNb2RlbCBTZXJ2aW5nIHdpcmUgcmVwYWlycyBzaGFyZWQgd2l0aCB0aGUgbW9kZWwgZ2F0ZXdheS5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cblxuZXhwb3J0IHtcbiAgYXBwbHlUb29sUmVhc29uaW5nQ29tcGF0aWJpbGl0eSxcbiAgZmxhdHRlbkNob2ljZURlbHRhQ29udGVudCxcbiAgZmxhdHRlbkNob2ljZU1lc3NhZ2VDb250ZW50LFxuICByZXBhaXJBc3Npc3RhbnRQcmVmaWxsLFxuICByZXdyaXRlU2VydmluZ0JvZHksXG4gIHJld3JpdGVTZXJ2aW5nUmVxdWVzdCxcbiAgcmV3cml0ZVNlcnZpbmdSZXNwb25zZUJvZHksXG4gIHJld3JpdGVTZXJ2aW5nUmVzcG9uc2VTdHJlYW0sXG4gIHN0cmlwUmVhc29uaW5nRnJvbVNlcnZpbmdNZXNzYWdlcyxcbiAgdHlwZSBSZXdyaXR0ZW5TZXJ2aW5nUmVxdWVzdCxcbiAgdHlwZSBTZXJ2aW5nQ2hhdE1lc3NhZ2UsXG59IGZyb20gXCJAZGJ4LXRvb2xzL21vZGVsL3NlcnZpbmctd2lyZVwiO1xuIl19
|
package/lib/src/trace-io.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copy each Mastra chat turn's request/response onto
|
|
3
|
-
*
|
|
2
|
+
* Copy each Mastra chat turn's request/response onto its OTel root span
|
|
3
|
+
* so Databricks-managed MLflow can display them.
|
|
4
4
|
*
|
|
5
5
|
* Why this exists: MLflow's UC `<target>_trace_unified` view picks the
|
|
6
6
|
* one span whose `parent_span_id` is empty and reads request/response
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
* the view never reads, so chat traces arrive with both columns null
|
|
11
11
|
* even when the spans themselves look healthy.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* AppKit's HTTP instrumentation records the server span in OTel RPC metadata,
|
|
14
|
+
* which remains available even when Express makes a middleware-layer child
|
|
15
|
+
* active. When instrumentation is unavailable, this module creates one
|
|
16
|
+
* request-lifetime server span and runs Mastra under it.
|
|
17
17
|
*
|
|
18
18
|
* @module
|
|
19
19
|
*/
|
|
@@ -32,12 +32,14 @@ export declare const MLFLOW_SPAN_OUTPUTS_ATTR = "mlflow.spanOutputs";
|
|
|
32
32
|
* reassembled here.
|
|
33
33
|
*/
|
|
34
34
|
export declare function assistantTextFromSse(body: string): string;
|
|
35
|
+
/** Read the final assistant text from a non-streaming Mastra response. */
|
|
36
|
+
export declare function assistantTextFromJson(body: unknown): string;
|
|
35
37
|
/**
|
|
36
|
-
* Express middleware that stamps chat turn I/O onto the
|
|
38
|
+
* Express middleware that stamps chat turn I/O onto the exported OTel root.
|
|
37
39
|
*
|
|
38
|
-
* Tee `res.write`
|
|
39
|
-
* instrumentation ends
|
|
40
|
-
* a span ends never
|
|
40
|
+
* Tee `res.write`, `res.json`, and `res.end` instead of listening for `finish`:
|
|
41
|
+
* HTTP instrumentation ends its server span on finish, and attributes set
|
|
42
|
+
* after a span ends never reach the exporter.
|
|
41
43
|
*
|
|
42
44
|
* `path` is mount-relative (what the Mastra sub-app sees), e.g.
|
|
43
45
|
* `/agents/support/stream`.
|
|
@@ -47,7 +49,7 @@ export declare function chatTurnTraceIoMiddleware(req: express.Request, res: exp
|
|
|
47
49
|
* Install {@link chatTurnTraceIoMiddleware} on a Mastra Express sub-app.
|
|
48
50
|
*
|
|
49
51
|
* Call before `MastraServer.init()` so the layer sits ahead of the agent
|
|
50
|
-
* routes. Safe to call unconditionally:
|
|
51
|
-
*
|
|
52
|
+
* routes. Safe to call unconditionally: when no recording tracer is registered,
|
|
53
|
+
* the middleware is a no-op.
|
|
52
54
|
*/
|
|
53
55
|
export declare function attachChatTurnTraceIo(app: express.Express): void;
|