@dbx-tools/shared-model 0.3.43 → 0.4.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/lib/index.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ export * as classify from "./src/classify.js";
2
+ export * as display from "./src/display.js";
3
+ export * as model from "./src/model.js";
4
+ export * as openaiChat from "./src/openai-chat.js";
5
+ export * as openaiResponses from "./src/openai-responses.js";
6
+ export { endpointCapabilities, versionTuple, classifyByFamily, classifyEndpoints } from "./src/classify.js";
7
+ export type { EndpointCapabilities, FamilyClass } from "./src/classify.js";
8
+ export { toModelDisplayName } from "./src/display.js";
9
+ export { ModelClass, ModelClassSchema, ModelProfileSchema, ServingEndpointSummarySchema, ModelQuerySchema, RankedModelSchema } from "./src/model.js";
10
+ export type { ModelProfile, ServingEndpointSummary, ModelQuery, RankedModel } from "./src/model.js";
11
+ export { UNSUPPORTED_CHAT_FIELDS, stripUnsupportedChatFields, chatContentToText } from "./src/openai-chat.js";
12
+ export type { ChatRole, ChatContentPart, ChatToolCallFunction, ChatToolCall, ChatMessage, ChatContentToTextOptions } from "./src/openai-chat.js";
13
+ export { responsesToChat, chatToResponsesRequest, chatToResponse, responseToChatCompletion, createResponsesStreamTranslator, readResponsesOutput, sanitizeResponsesTools, REASONING_TYPES, sanitizeOpenResponsesInput, sanitizeOpenResponsesRequest } from "./src/openai-responses.js";
14
+ export type { ResponsesStreamTranslator, ResponsesCitation, ResponsesOutput } from "./src/openai-responses.js";
package/lib/index.js ADDED
@@ -0,0 +1,14 @@
1
+ // GENERATED by projen watch - DO NOT EDIT.
2
+ // Regenerated from the exporting modules in ./src.
3
+ // Hand edits are overwritten on the next watch; this file is read-only.
4
+ export * as classify from "./src/classify.js";
5
+ export * as display from "./src/display.js";
6
+ export * as model from "./src/model.js";
7
+ export * as openaiChat from "./src/openai-chat.js";
8
+ export * as openaiResponses from "./src/openai-responses.js";
9
+ export { endpointCapabilities, versionTuple, classifyByFamily, classifyEndpoints } from "./src/classify.js";
10
+ export { toModelDisplayName } from "./src/display.js";
11
+ export { ModelClass, ModelClassSchema, ModelProfileSchema, ServingEndpointSummarySchema, ModelQuerySchema, RankedModelSchema } from "./src/model.js";
12
+ export { UNSUPPORTED_CHAT_FIELDS, stripUnsupportedChatFields, chatContentToText } from "./src/openai-chat.js";
13
+ export { responsesToChat, chatToResponsesRequest, chatToResponse, responseToChatCompletion, createResponsesStreamTranslator, readResponsesOutput, sanitizeResponsesTools, REASONING_TYPES, sanitizeOpenResponsesInput, sanitizeOpenResponsesRequest } from "./src/openai-responses.js";
14
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwyQ0FBMkM7QUFDM0MsbURBQW1EO0FBQ25ELHdFQUF3RTtBQUV4RSxPQUFPLEtBQUssUUFBUSxNQUFNLGdCQUFnQixDQUFDO0FBQzNDLE9BQU8sS0FBSyxPQUFPLE1BQU0sZUFBZSxDQUFDO0FBQ3pDLE9BQU8sS0FBSyxLQUFLLE1BQU0sYUFBYSxDQUFDO0FBQ3JDLE9BQU8sS0FBSyxVQUFVLE1BQU0sbUJBQW1CLENBQUM7QUFDaEQsT0FBTyxLQUFLLGVBQWUsTUFBTSx3QkFBd0IsQ0FBQztBQUMxRCxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsWUFBWSxFQUFFLGdCQUFnQixFQUFFLGlCQUFpQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFekcsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ25ELE9BQU8sRUFBRSxVQUFVLEVBQUUsZ0JBQWdCLEVBQUUsa0JBQWtCLEVBQUUsNEJBQTRCLEVBQUUsZ0JBQWdCLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFFbEosT0FBTyxFQUFFLHVCQUF1QixFQUFFLDBCQUEwQixFQUFFLGlCQUFpQixFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFFM0csT0FBTyxFQUFFLGVBQWUsRUFBRSxzQkFBc0IsRUFBRSxjQUFjLEVBQUUsd0JBQXdCLEVBQUUsK0JBQStCLEVBQUUsbUJBQW1CLEVBQUUsc0JBQXNCLEVBQUUsZUFBZSxFQUFFLDBCQUEwQixFQUFFLDRCQUE0QixFQUFFLE1BQU0sd0JBQXdCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBHRU5FUkFURUQgYnkgcHJvamVuIHdhdGNoIC0gRE8gTk9UIEVESVQuXG4vLyBSZWdlbmVyYXRlZCBmcm9tIHRoZSBleHBvcnRpbmcgbW9kdWxlcyBpbiAuL3NyYy5cbi8vIEhhbmQgZWRpdHMgYXJlIG92ZXJ3cml0dGVuIG9uIHRoZSBuZXh0IHdhdGNoOyB0aGlzIGZpbGUgaXMgcmVhZC1vbmx5LlxuXG5leHBvcnQgKiBhcyBjbGFzc2lmeSBmcm9tIFwiLi9zcmMvY2xhc3NpZnlcIjtcbmV4cG9ydCAqIGFzIGRpc3BsYXkgZnJvbSBcIi4vc3JjL2Rpc3BsYXlcIjtcbmV4cG9ydCAqIGFzIG1vZGVsIGZyb20gXCIuL3NyYy9tb2RlbFwiO1xuZXhwb3J0ICogYXMgb3BlbmFpQ2hhdCBmcm9tIFwiLi9zcmMvb3BlbmFpLWNoYXRcIjtcbmV4cG9ydCAqIGFzIG9wZW5haVJlc3BvbnNlcyBmcm9tIFwiLi9zcmMvb3BlbmFpLXJlc3BvbnNlc1wiO1xuZXhwb3J0IHsgZW5kcG9pbnRDYXBhYmlsaXRpZXMsIHZlcnNpb25UdXBsZSwgY2xhc3NpZnlCeUZhbWlseSwgY2xhc3NpZnlFbmRwb2ludHMgfSBmcm9tIFwiLi9zcmMvY2xhc3NpZnlcIjtcbmV4cG9ydCB0eXBlIHsgRW5kcG9pbnRDYXBhYmlsaXRpZXMsIEZhbWlseUNsYXNzIH0gZnJvbSBcIi4vc3JjL2NsYXNzaWZ5XCI7XG5leHBvcnQgeyB0b01vZGVsRGlzcGxheU5hbWUgfSBmcm9tIFwiLi9zcmMvZGlzcGxheVwiO1xuZXhwb3J0IHsgTW9kZWxDbGFzcywgTW9kZWxDbGFzc1NjaGVtYSwgTW9kZWxQcm9maWxlU2NoZW1hLCBTZXJ2aW5nRW5kcG9pbnRTdW1tYXJ5U2NoZW1hLCBNb2RlbFF1ZXJ5U2NoZW1hLCBSYW5rZWRNb2RlbFNjaGVtYSB9IGZyb20gXCIuL3NyYy9tb2RlbFwiO1xuZXhwb3J0IHR5cGUgeyBNb2RlbFByb2ZpbGUsIFNlcnZpbmdFbmRwb2ludFN1bW1hcnksIE1vZGVsUXVlcnksIFJhbmtlZE1vZGVsIH0gZnJvbSBcIi4vc3JjL21vZGVsXCI7XG5leHBvcnQgeyBVTlNVUFBPUlRFRF9DSEFUX0ZJRUxEUywgc3RyaXBVbnN1cHBvcnRlZENoYXRGaWVsZHMsIGNoYXRDb250ZW50VG9UZXh0IH0gZnJvbSBcIi4vc3JjL29wZW5haS1jaGF0XCI7XG5leHBvcnQgdHlwZSB7IENoYXRSb2xlLCBDaGF0Q29udGVudFBhcnQsIENoYXRUb29sQ2FsbEZ1bmN0aW9uLCBDaGF0VG9vbENhbGwsIENoYXRNZXNzYWdlLCBDaGF0Q29udGVudFRvVGV4dE9wdGlvbnMgfSBmcm9tIFwiLi9zcmMvb3BlbmFpLWNoYXRcIjtcbmV4cG9ydCB7IHJlc3BvbnNlc1RvQ2hhdCwgY2hhdFRvUmVzcG9uc2VzUmVxdWVzdCwgY2hhdFRvUmVzcG9uc2UsIHJlc3BvbnNlVG9DaGF0Q29tcGxldGlvbiwgY3JlYXRlUmVzcG9uc2VzU3RyZWFtVHJhbnNsYXRvciwgcmVhZFJlc3BvbnNlc091dHB1dCwgc2FuaXRpemVSZXNwb25zZXNUb29scywgUkVBU09OSU5HX1RZUEVTLCBzYW5pdGl6ZU9wZW5SZXNwb25zZXNJbnB1dCwgc2FuaXRpemVPcGVuUmVzcG9uc2VzUmVxdWVzdCB9IGZyb20gXCIuL3NyYy9vcGVuYWktcmVzcG9uc2VzXCI7XG5leHBvcnQgdHlwZSB7IFJlc3BvbnNlc1N0cmVhbVRyYW5zbGF0b3IsIFJlc3BvbnNlc0NpdGF0aW9uLCBSZXNwb25zZXNPdXRwdXQgfSBmcm9tIFwiLi9zcmMvb3BlbmFpLXJlc3BvbnNlc1wiO1xuIl19
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Model-class classification for Databricks Model Serving endpoints.
3
+ *
4
+ * Chat capability bands are derived from the live workspace catalogue
5
+ * rather than a hand-maintained table. Databricks publishes
6
+ * per-endpoint `quality` / `speed` / `cost` scores (the AI Playground
7
+ * bars) on the serving list; {@link classifyEndpoints} buckets scored
8
+ * chat models into the chat {@link ModelClass} bands by the *relative*
9
+ * distribution of those scores (quantiles, not fixed cut-offs) so a
10
+ * brand-new model that lands outside today's score range still slots in
11
+ * next to its peers. Embedding endpoints (`task === "llm/v1/embeddings"`)
12
+ * are bucketed into {@link ModelClass.Embedding} by task, independent of
13
+ * any score.
14
+ *
15
+ * Unscored-but-recognizable chat endpoints are still placed by a small
16
+ * family heuristic ({@link classifyByFamily}) so a workspace whose
17
+ * models predate Foundation Model API scoring keeps working. The
18
+ * offline fallback floor - the hard-coded model list reached for when
19
+ * the live catalogue can't be read at all - is a server concern and
20
+ * lives in `@dbx-tools/model`, not here: a browser client never talks
21
+ * to Databricks directly, so it has nothing to fall back to.
22
+ *
23
+ * Pure (no Node-only imports), so a client can classify a `/models`
24
+ * response without server dependencies.
25
+ *
26
+ * @module
27
+ */
28
+ import { ModelClass, type ServingEndpointSummary } from "./model.js";
29
+ /** What an endpoint can be asked to do, as derived by {@link endpointCapabilities}. */
30
+ export interface EndpointCapabilities {
31
+ /** OpenAI chat/completions + Responses: the surface a chat agent needs. */
32
+ chat: boolean;
33
+ /** Embedding (vector) endpoint. */
34
+ embedding: boolean;
35
+ /** Function / tool calling. Every chat endpoint on Databricks supports it. */
36
+ tools: boolean;
37
+ }
38
+ /**
39
+ * Derive an endpoint's capabilities from its Databricks task hint and its
40
+ * classified {@link ModelClass}, so consumers filter on capability instead of
41
+ * re-deriving it from raw `task` / `class` strings.
42
+ *
43
+ * Either signal alone is enough: the task hint is authoritative when present,
44
+ * and the class covers endpoints Databricks left untasked but the classifier
45
+ * recognized. Embedding wins over chat when both point at it, since the two are
46
+ * not interchangeable.
47
+ */
48
+ export declare function endpointCapabilities(endpoint: ServingEndpointSummary): EndpointCapabilities;
49
+ /** Family-heuristic classification of a single endpoint name. */
50
+ export interface FamilyClass {
51
+ /** Chat capability band the family maps to (never embedding). */
52
+ class: ModelClass;
53
+ /** Intra-family ordering hint (higher is newer / more capable). */
54
+ rank: number;
55
+ }
56
+ /**
57
+ * Numeric `[major, minor, patch]` version parsed from an endpoint
58
+ * name, used to order siblings within a family/tier. Starts at the
59
+ * first digit in the name, then reads successive separator-delimited,
60
+ * digit-prefixed chunks as the three components (missing ones default
61
+ * to `0`):
62
+ *
63
+ * - `databricks-claude-opus-4-8` -> `[4, 8, 0]`
64
+ * - `databricks-claude-opus-4-10` -> `[4, 10, 0]` (sorts above 4-8)
65
+ * - `databricks-meta-llama-3-3-70b`-> `[3, 3, 70]`
66
+ * - `databricks-bge-large-en` -> `[0, 0, 0]` (no digits)
67
+ *
68
+ * Component-wise comparison (not a decimal collapse) so `4.10` beats
69
+ * `4.8` - the bug a `major + minor/10` score would hit.
70
+ */
71
+ export declare function versionTuple(name: string): [number, number, number];
72
+ /**
73
+ * Best-effort chat capability band for an endpoint we have no live
74
+ * score for, keyed off provider family and the well-known variant words
75
+ * in the name (`opus`/`sonnet`/`haiku`, `pro`/`mini`/`nano`,
76
+ * `flash`/`flash-lite`, Llama parameter sizes, etc). Returns `null` for
77
+ * names we don't recognize so unknown custom endpoints are never
78
+ * auto-selected as a default. The accompanying `rank` orders siblings
79
+ * within a class. Only ever returns a chat band - embedding endpoints
80
+ * are classified by task, not name.
81
+ */
82
+ export declare function classifyByFamily(name: string): FamilyClass | null;
83
+ /**
84
+ * Bucket live endpoints into {@link ModelClass}es, ranked best-first
85
+ * within each chat band.
86
+ *
87
+ * Embedding endpoints (`task === "llm/v1/embeddings"`) go into
88
+ * {@link ModelClass.Embedding} by task, in listing order (they carry no
89
+ * capability score to rank on).
90
+ *
91
+ * Chat endpoints (`task === "llm/v1/chat"`) split into the three chat
92
+ * bands. Scored endpoints (those carrying a `profile.quality`) drive
93
+ * the banding: the observed quality distribution is split at its 1/3
94
+ * and 2/3 quantiles, so the top third is {@link ModelClass.ChatThinking},
95
+ * the bottom third {@link ModelClass.ChatFast}, and the middle
96
+ * {@link ModelClass.ChatBalanced}. Because the thresholds come from the
97
+ * data, the split adapts as Databricks adds or rescores models -
98
+ * nothing is pinned to a fixed score band.
99
+ *
100
+ * Unscored chat endpoints are placed by {@link classifyByFamily} and
101
+ * ranked after the scored ones in their band; unrecognized, unscored
102
+ * endpoints (e.g. custom external models) are omitted entirely so they
103
+ * are never picked as an automatic default.
104
+ *
105
+ * Within a chat band, scored models sort by `quality` desc, then `cost`
106
+ * asc, then `speed` desc, then parsed name version desc; family-only
107
+ * models sort by version rank then parsed version. The version
108
+ * tie-break ({@link versionTuple}) is what separates point releases
109
+ * that share a score profile (e.g. `opus-4-8` ahead of `opus-4-7`).
110
+ */
111
+ export declare function classifyEndpoints(endpoints: readonly ServingEndpointSummary[]): Record<ModelClass, ServingEndpointSummary[]>;
@@ -0,0 +1,261 @@
1
+ /**
2
+ * Model-class classification for Databricks Model Serving endpoints.
3
+ *
4
+ * Chat capability bands are derived from the live workspace catalogue
5
+ * rather than a hand-maintained table. Databricks publishes
6
+ * per-endpoint `quality` / `speed` / `cost` scores (the AI Playground
7
+ * bars) on the serving list; {@link classifyEndpoints} buckets scored
8
+ * chat models into the chat {@link ModelClass} bands by the *relative*
9
+ * distribution of those scores (quantiles, not fixed cut-offs) so a
10
+ * brand-new model that lands outside today's score range still slots in
11
+ * next to its peers. Embedding endpoints (`task === "llm/v1/embeddings"`)
12
+ * are bucketed into {@link ModelClass.Embedding} by task, independent of
13
+ * any score.
14
+ *
15
+ * Unscored-but-recognizable chat endpoints are still placed by a small
16
+ * family heuristic ({@link classifyByFamily}) so a workspace whose
17
+ * models predate Foundation Model API scoring keeps working. The
18
+ * offline fallback floor - the hard-coded model list reached for when
19
+ * the live catalogue can't be read at all - is a server concern and
20
+ * lives in `@dbx-tools/model`, not here: a browser client never talks
21
+ * to Databricks directly, so it has nothing to fall back to.
22
+ *
23
+ * Pure (no Node-only imports), so a client can classify a `/models`
24
+ * response without server dependencies.
25
+ *
26
+ * @module
27
+ */
28
+ import { ModelClass } from "./model.js";
29
+ /** Task hint Databricks stamps on chat completion endpoints. */
30
+ const CHAT_TASK = "llm/v1/chat";
31
+ /** Task hint Databricks stamps on embedding endpoints. */
32
+ const EMBEDDING_TASK = "llm/v1/embeddings";
33
+ /**
34
+ * Derive an endpoint's capabilities from its Databricks task hint and its
35
+ * classified {@link ModelClass}, so consumers filter on capability instead of
36
+ * re-deriving it from raw `task` / `class` strings.
37
+ *
38
+ * Either signal alone is enough: the task hint is authoritative when present,
39
+ * and the class covers endpoints Databricks left untasked but the classifier
40
+ * recognized. Embedding wins over chat when both point at it, since the two are
41
+ * not interchangeable.
42
+ */
43
+ export function endpointCapabilities(endpoint) {
44
+ const embedding = endpoint.task === EMBEDDING_TASK || endpoint.class === ModelClass.Embedding;
45
+ // Every non-embedding class is a chat band, so "classified but not embedding"
46
+ // is the class-side test for chat capability.
47
+ const chat = !embedding && (endpoint.task === CHAT_TASK || endpoint.class !== undefined);
48
+ return { chat, embedding, tools: chat };
49
+ }
50
+ /**
51
+ * Numeric `[major, minor, patch]` version parsed from an endpoint
52
+ * name, used to order siblings within a family/tier. Starts at the
53
+ * first digit in the name, then reads successive separator-delimited,
54
+ * digit-prefixed chunks as the three components (missing ones default
55
+ * to `0`):
56
+ *
57
+ * - `databricks-claude-opus-4-8` -> `[4, 8, 0]`
58
+ * - `databricks-claude-opus-4-10` -> `[4, 10, 0]` (sorts above 4-8)
59
+ * - `databricks-meta-llama-3-3-70b`-> `[3, 3, 70]`
60
+ * - `databricks-bge-large-en` -> `[0, 0, 0]` (no digits)
61
+ *
62
+ * Component-wise comparison (not a decimal collapse) so `4.10` beats
63
+ * `4.8` - the bug a `major + minor/10` score would hit.
64
+ */
65
+ export function versionTuple(name) {
66
+ const start = name.search(/\d/);
67
+ if (start < 0)
68
+ return [0, 0, 0];
69
+ const nums = name
70
+ .slice(start)
71
+ .split(/[^a-z0-9]+/i)
72
+ .map((chunk) => chunk.match(/^\d+/)?.[0])
73
+ .filter((digits) => digits !== undefined)
74
+ .map(Number);
75
+ return [nums[0] ?? 0, nums[1] ?? 0, nums[2] ?? 0];
76
+ }
77
+ /** Compare two version tuples so the higher version sorts first (descending). */
78
+ function compareVersionDesc(a, b) {
79
+ for (let i = 0; i < 3; i++) {
80
+ const diff = (b[i] ?? 0) - (a[i] ?? 0);
81
+ if (diff !== 0)
82
+ return diff;
83
+ }
84
+ return 0;
85
+ }
86
+ /**
87
+ * Monotonic version key for ordering siblings within a family/tier
88
+ * (e.g. `opus-4-8` over `opus-4-7`). Encodes the {@link versionTuple}
89
+ * as a single number so callers that need a scalar rank (the family
90
+ * heuristic, the static fallback ordering) keep working; the encoding
91
+ * preserves component order without the decimal-collapse bug.
92
+ */
93
+ function versionScore(name) {
94
+ const [major, minor, patch] = versionTuple(name);
95
+ return major * 1_000_000 + minor * 1_000 + patch;
96
+ }
97
+ /**
98
+ * Best-effort chat capability band for an endpoint we have no live
99
+ * score for, keyed off provider family and the well-known variant words
100
+ * in the name (`opus`/`sonnet`/`haiku`, `pro`/`mini`/`nano`,
101
+ * `flash`/`flash-lite`, Llama parameter sizes, etc). Returns `null` for
102
+ * names we don't recognize so unknown custom endpoints are never
103
+ * auto-selected as a default. The accompanying `rank` orders siblings
104
+ * within a class. Only ever returns a chat band - embedding endpoints
105
+ * are classified by task, not name.
106
+ */
107
+ export function classifyByFamily(name) {
108
+ const n = name.toLowerCase();
109
+ const at = (cls) => ({ class: cls, rank: versionScore(n) });
110
+ // Anthropic Claude
111
+ if (n.includes("opus"))
112
+ return at(ModelClass.ChatThinking);
113
+ if (n.includes("sonnet"))
114
+ return at(ModelClass.ChatBalanced);
115
+ if (n.includes("haiku"))
116
+ return at(ModelClass.ChatFast);
117
+ // OpenAI open-weights (check before the generic gpt branch)
118
+ if (n.includes("gpt-oss")) {
119
+ return at(n.includes("120b") ? ModelClass.ChatBalanced : ModelClass.ChatFast);
120
+ }
121
+ // OpenAI GPT family
122
+ if (n.includes("gpt")) {
123
+ if (n.includes("pro"))
124
+ return at(ModelClass.ChatThinking);
125
+ if (n.includes("mini") || n.includes("nano"))
126
+ return at(ModelClass.ChatFast);
127
+ return at(ModelClass.ChatBalanced);
128
+ }
129
+ // Google Gemini / Gemma
130
+ if (n.includes("gemini")) {
131
+ if (n.includes("flash-lite"))
132
+ return at(ModelClass.ChatFast);
133
+ if (n.includes("pro"))
134
+ return at(ModelClass.ChatThinking);
135
+ return at(ModelClass.ChatBalanced);
136
+ }
137
+ if (n.includes("gemma"))
138
+ return at(ModelClass.ChatFast);
139
+ // Meta Llama
140
+ if (n.includes("llama")) {
141
+ if (n.includes("maverick") || n.includes("405b"))
142
+ return at(ModelClass.ChatThinking);
143
+ if (n.includes("70b"))
144
+ return at(ModelClass.ChatBalanced);
145
+ if (n.includes("8b") || n.includes("1b"))
146
+ return at(ModelClass.ChatFast);
147
+ return at(ModelClass.ChatBalanced);
148
+ }
149
+ // Alibaba Qwen
150
+ if (n.includes("qwen"))
151
+ return at(ModelClass.ChatBalanced);
152
+ return null;
153
+ }
154
+ /** Linear-interpolated quantile of an ascending-sorted numeric array. */
155
+ function quantile(sortedAsc, p) {
156
+ if (sortedAsc.length === 0)
157
+ return Number.NaN;
158
+ const idx = (sortedAsc.length - 1) * p;
159
+ const lo = Math.floor(idx);
160
+ const hi = Math.ceil(idx);
161
+ if (lo === hi)
162
+ return sortedAsc[lo];
163
+ return sortedAsc[lo] + (sortedAsc[hi] - sortedAsc[lo]) * (idx - lo);
164
+ }
165
+ function rankOrder(a, b) {
166
+ if (a.scored !== b.scored)
167
+ return a.scored ? -1 : 1;
168
+ if (b.sort !== a.sort)
169
+ return b.sort - a.sort;
170
+ if (a.tieCost !== b.tieCost)
171
+ return a.tieCost - b.tieCost;
172
+ if (b.tieSpeed !== a.tieSpeed)
173
+ return b.tieSpeed - a.tieSpeed;
174
+ // Same scored-ness, quality, cost, and speed (e.g. several Claude
175
+ // Opus point releases): prefer the newer parsed version so
176
+ // `opus-4-8` beats `opus-4-7`.
177
+ return compareVersionDesc(a.version, b.version);
178
+ }
179
+ /**
180
+ * Bucket live endpoints into {@link ModelClass}es, ranked best-first
181
+ * within each chat band.
182
+ *
183
+ * Embedding endpoints (`task === "llm/v1/embeddings"`) go into
184
+ * {@link ModelClass.Embedding} by task, in listing order (they carry no
185
+ * capability score to rank on).
186
+ *
187
+ * Chat endpoints (`task === "llm/v1/chat"`) split into the three chat
188
+ * bands. Scored endpoints (those carrying a `profile.quality`) drive
189
+ * the banding: the observed quality distribution is split at its 1/3
190
+ * and 2/3 quantiles, so the top third is {@link ModelClass.ChatThinking},
191
+ * the bottom third {@link ModelClass.ChatFast}, and the middle
192
+ * {@link ModelClass.ChatBalanced}. Because the thresholds come from the
193
+ * data, the split adapts as Databricks adds or rescores models -
194
+ * nothing is pinned to a fixed score band.
195
+ *
196
+ * Unscored chat endpoints are placed by {@link classifyByFamily} and
197
+ * ranked after the scored ones in their band; unrecognized, unscored
198
+ * endpoints (e.g. custom external models) are omitted entirely so they
199
+ * are never picked as an automatic default.
200
+ *
201
+ * Within a chat band, scored models sort by `quality` desc, then `cost`
202
+ * asc, then `speed` desc, then parsed name version desc; family-only
203
+ * models sort by version rank then parsed version. The version
204
+ * tie-break ({@link versionTuple}) is what separates point releases
205
+ * that share a score profile (e.g. `opus-4-8` ahead of `opus-4-7`).
206
+ */
207
+ export function classifyEndpoints(endpoints) {
208
+ const chat = endpoints.filter((e) => e.task === CHAT_TASK);
209
+ const qualities = chat
210
+ .map((e) => e.profile?.quality)
211
+ .filter((q) => Number.isFinite(q))
212
+ .sort((a, b) => a - b);
213
+ const low = quantile(qualities, 1 / 3);
214
+ const high = quantile(qualities, 2 / 3);
215
+ const buckets = {
216
+ [ModelClass.ChatThinking]: [],
217
+ [ModelClass.ChatBalanced]: [],
218
+ [ModelClass.ChatFast]: [],
219
+ [ModelClass.Embedding]: [],
220
+ };
221
+ for (const ep of chat) {
222
+ const q = ep.profile?.quality;
223
+ if (Number.isFinite(q)) {
224
+ const quality = q;
225
+ const cls = quality >= high
226
+ ? ModelClass.ChatThinking
227
+ : quality <= low
228
+ ? ModelClass.ChatFast
229
+ : ModelClass.ChatBalanced;
230
+ buckets[cls].push({
231
+ ep,
232
+ sort: quality,
233
+ scored: true,
234
+ tieCost: ep.profile?.cost ?? Number.POSITIVE_INFINITY,
235
+ tieSpeed: ep.profile?.speed ?? 0,
236
+ version: versionTuple(ep.name),
237
+ });
238
+ continue;
239
+ }
240
+ const family = classifyByFamily(ep.name);
241
+ if (!family)
242
+ continue;
243
+ buckets[family.class].push({
244
+ ep,
245
+ sort: family.rank,
246
+ scored: false,
247
+ tieCost: Number.POSITIVE_INFINITY,
248
+ tieSpeed: 0,
249
+ version: versionTuple(ep.name),
250
+ });
251
+ }
252
+ // Embeddings are bucketed by task in listing order - no score to rank.
253
+ const embeddings = endpoints.filter((e) => e.task === EMBEDDING_TASK);
254
+ return {
255
+ [ModelClass.ChatThinking]: buckets[ModelClass.ChatThinking].sort(rankOrder).map((x) => x.ep),
256
+ [ModelClass.ChatBalanced]: buckets[ModelClass.ChatBalanced].sort(rankOrder).map((x) => x.ep),
257
+ [ModelClass.ChatFast]: buckets[ModelClass.ChatFast].sort(rankOrder).map((x) => x.ep),
258
+ [ModelClass.Embedding]: embeddings,
259
+ };
260
+ }
261
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3NpZnkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY2xhc3NpZnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBMEJHO0FBRUgsT0FBTyxFQUFFLFVBQVUsRUFBK0IsTUFBTSxTQUFTLENBQUM7QUFFbEUsZ0VBQWdFO0FBQ2hFLE1BQU0sU0FBUyxHQUFHLGFBQWEsQ0FBQztBQUVoQywwREFBMEQ7QUFDMUQsTUFBTSxjQUFjLEdBQUcsbUJBQW1CLENBQUM7QUFZM0M7Ozs7Ozs7OztHQVNHO0FBQ0gsTUFBTSxVQUFVLG9CQUFvQixDQUFDLFFBQWdDO0lBQ25FLE1BQU0sU0FBUyxHQUFHLFFBQVEsQ0FBQyxJQUFJLEtBQUssY0FBYyxJQUFJLFFBQVEsQ0FBQyxLQUFLLEtBQUssVUFBVSxDQUFDLFNBQVMsQ0FBQztJQUM5Riw4RUFBOEU7SUFDOUUsOENBQThDO0lBQzlDLE1BQU0sSUFBSSxHQUFHLENBQUMsU0FBUyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksS0FBSyxTQUFTLElBQUksUUFBUSxDQUFDLEtBQUssS0FBSyxTQUFTLENBQUMsQ0FBQztJQUN6RixPQUFPLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLENBQUM7QUFDMUMsQ0FBQztBQVVEOzs7Ozs7Ozs7Ozs7OztHQWNHO0FBQ0gsTUFBTSxVQUFVLFlBQVksQ0FBQyxJQUFZO0lBQ3ZDLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDaEMsSUFBSSxLQUFLLEdBQUcsQ0FBQztRQUFFLE9BQU8sQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ2hDLE1BQU0sSUFBSSxHQUFHLElBQUk7U0FDZCxLQUFLLENBQUMsS0FBSyxDQUFDO1NBQ1osS0FBSyxDQUFDLGFBQWEsQ0FBQztTQUNwQixHQUFHLENBQUMs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TSxDQUFDO1FBQUUsT0FBTyxFQUFFLENBQUMsVUFBVSxDQUFDLFlBQVksQ0FBQyxDQUFDO0lBRTNELE9BQU8sSUFBSSxDQUFDO0FBQ2QsQ0FBQztBQUVELHlFQUF5RTtBQUN6RSxTQUFTLFFBQVEsQ0FBQyxTQUE0QixFQUFFLENBQVM7SUFDdkQsSUFBSSxTQUFTLENBQUMsTUFBTSxLQUFLLENBQUM7UUFBRSxPQUFPLE1BQU0sQ0FBQyxHQUFHLENBQUM7SUFDOUMsTUFBTSxHQUFHLEdBQUcsQ0FBQyxTQUFTLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUN2QyxNQUFNLEVBQUUsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQzNCLE1BQU0sRUFBRSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDMUIsSUFBSSxFQUFFLEtBQUssRUFBRTtRQUFFLE9BQU8sU0FBUyxDQUFDLEVBQUUsQ0FBRSxDQUFDO0lBQ3JDLE9BQU8sU0FBUyxDQUFDLEVBQUUsQ0FBRSxHQUFHLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBRSxHQUFHLFNBQVMsQ0FBQyxFQUFFLENBQUUsQ0FBQyxHQUFHLENBQUMsR0FBRyxHQUFHLEVBQUUsQ0FBQyxDQUFDO0FBQ3pFLENBQUM7QUFlRCxTQUFTLFNBQVMsQ0FBQyxDQUFTLEVBQUUsQ0FBUztJQUNyQyxJQUFJLENBQUMsQ0FBQyxNQUFNLEtBQUssQ0FBQyxDQUFDLE1BQU07UUFBRSxPQUFPLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDcEQsSUFBSSxDQUFDLENBQUMsSUFBSSxLQUFLLENBQUMsQ0FBQyxJQUFJO1FBQUUsT0FBTyxDQUFDLENBQUMsSUFBSSxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUM7SUFDOUMsSUFBSSxDQUFDLENBQUMsT0FBTyxLQUFLLENBQUMsQ0FBQyxPQUFPO1FBQUUsT0FBTyxDQUFDLENBQUMsT0FB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QUFJO1lBQ2pCLE1BQU0sRUFBRSxLQUFLO1lBQ2IsT0FBTyxFQUFFLE1BQU0sQ0FBQyxpQkFBaUI7WUFDakMsUUFBUSxFQUFFLENBQUM7WUFDWCxPQUFPLEVBQUUsWUFBWSxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUM7U0FDL0IsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVELHVFQUF1RTtJQUN2RSxNQUFNLFVBQVUsR0FBRyxTQUFTLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxLQUFLLGNBQWMsQ0FBQyxDQUFDO0lBRXRFLE9BQU87UUFDTCxDQUFDLFVBQVUsQ0FBQyxZQUFZLENBQUMsRUFBRSxPQUFPLENBQUMsVUFBVSxDQUFDLFlBQVksQ0FBQyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDNUYsQ0FBQyxVQUFVLENBQUMsWUFBWSxDQUFDLEVBQUUsT0FBTyxDQUFDLFVBQVUsQ0FBQyxZQUFZLENBQUMsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQzVGLENBQUMsVUFBVSxDQUFDLFFBQVEsQ0FBQyxFQUFFLE9BQU8sQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUNwRixDQUFDLFVBQVUsQ0FBQyxTQUFTLENBQUMsRUFBRSxVQUFVO0tBQ25DLENBQUM7QUFDSixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBNb2RlbC1jbGFzcyBjbGFzc2lmaWNhdGlvbiBmb3IgRGF0YWJyaWNrcyBNb2RlbCBTZXJ2aW5nIGVuZHBvaW50cy5cbiAqXG4gKiBDaGF0IGNhcGFiaWxpdHkgYmFuZHMgYXJlIGRlcml2ZWQgZnJvbSB0aGUgbGl2ZSB3b3Jrc3BhY2UgY2F0YWxvZ3VlXG4gKiByYXRoZXIgdGhhbiBhIGhhbmQtbWFpbnRhaW5lZCB0YWJsZS4gRGF0YWJyaWNrcyBwdWJsaXNoZXNcbiAqIHBlci1lbmRwb2ludCBgcXVhbGl0eWAgLyBgc3BlZWRgIC8gYGNvc3RgIHNjb3JlcyAodGhlIEFJIFBsYXlncm91bmRcbiAqIGJhcnMpIG9uIHRoZSBzZXJ2aW5nIGxpc3Q7IHtAbGluayBjbGFzc2lmeUVuZHBvaW50c30gYnVja2V0cyBzY29yZWRcbiAqIGNoYXQgbW9kZWxzIGludG8gdGhlIGNoYXQge0BsaW5rIE1vZGVsQ2xhc3N9IGJhbmRzIGJ5IHRoZSAqcmVsYXRpdmUqXG4gKiBkaXN0cmlidXRpb24gb2YgdGhvc2Ugc2NvcmVzIChxdWFudGlsZXMsIG5vdCBmaXhlZCBjdXQtb2Zmcykgc28gYVxuICogYnJhbmQtbmV3IG1vZGVsIHRoYXQgbGFuZHMgb3V0c2lkZSB0b2RheSdzIHNjb3JlIHJhbmdlIHN0aWxsIHNsb3RzIGluXG4gKiBuZXh0IHRvIGl0cyBwZWVycy4gRW1iZWRkaW5nIGVuZHBvaW50cyAoYHRhc2sgPT09IFwibGxtL3YxL2VtYmVkZGluZ3NcImApXG4gKiBhcmUgYnVja2V0ZWQgaW50byB7QGxpbmsgTW9kZWxDbGFzcy5FbWJlZGRpbmd9IGJ5IHRhc2ssIGluZGVwZW5kZW50IG9mXG4gKiBhbnkgc2NvcmUuXG4gKlxuICogVW5zY29yZWQtYnV0LXJlY29nbml6YWJsZSBjaGF0IGVuZHBvaW50cyBhcmUgc3RpbGwgcGxhY2VkIGJ5IGEgc21hbGxcbiAqIGZhbWlseSBoZXVyaXN0aWMgKHtAbGluayBjbGFzc2lmeUJ5RmFtaWx5fSkgc28gYSB3b3Jrc3BhY2Ugd2hvc2VcbiAqIG1vZGVscyBwcmVkYXRlIEZvdW5kYXRpb24gTW9kZWwgQVBJIHNjb3Jpbmcga2VlcHMgd29ya2luZy4gVGhlXG4gKiBvZmZsaW5lIGZhbGxiYWNrIGZsb29yIC0gdGhlIGhhcmQtY29kZWQgbW9kZWwgbGlzdCByZWFjaGVkIGZvciB3aGVuXG4gKiB0aGUgbGl2ZSBjYXRhbG9ndWUgY2FuJ3QgYmUgcmVhZCBhdCBhbGwgLSBpcyBhIHNlcnZlciBjb25jZXJuIGFuZFxuICogbGl2ZXMgaW4gYEBkYngtdG9vbHMvbW9kZWxgLCBub3QgaGVyZTogYSBicm93c2VyIGNsaWVudCBuZXZlciB0YWxrc1xuICogdG8gRGF0YWJyaWNrcyBkaXJlY3RseSwgc28gaXQgaGFzIG5vdGhpbmcgdG8gZmFsbCBiYWNrIHRvLlxuICpcbiAqIFB1cmUgKG5vIE5vZGUtb25seSBpbXBvcnRzKSwgc28gYSBjbGllbnQgY2FuIGNsYXNzaWZ5IGEgYC9tb2RlbHNgXG4gKiByZXNwb25zZSB3aXRob3V0IHNlcnZlciBkZXBlbmRlbmNpZXMuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7IE1vZGVsQ2xhc3MsIHR5cGUgU2VydmluZ0VuZHBvaW50U3VtbWFyeSB9IGZyb20gXCIuL21vZGVsXCI7XG5cbi8qKiBUYXNrIGhpbnQgRGF0YWJyaWNrcyBzdGFtcHMgb24gY2hhdCBjb21wbGV0aW9uIGVuZHBvaW50cy4gKi9cbmNvbnN0IENIQVRfVEFTSyA9IFwibGxtL3YxL2NoYXRcIjtcblxuLyoqIFRhc2sgaGludCBEYXRhYnJpY2tzIHN0YW1wcyBvbiBlbWJlZGRpbmcgZW5kcG9pbnRzLiAqL1xuY29uc3QgRU1CRURESU5HX1RBU0sgPSBcImxsbS92MS9lbWJlZGRpbmdzXCI7XG5cbi8qKiBXaGF0IGFuIGVuZHBvaW50IGNhbiBiZSBhc2tlZCB0byBkbywgYXMgZGVyaXZlZCBieSB7QGxpbmsgZW5kcG9pbnRDYXBhYmlsaXRpZXN9LiAqL1xuZXhwb3J0IGludGVyZmFjZSBFbmRwb2ludENhcGFiaWxpdGllcyB7XG4gIC8qKiBPcGVuQUkgY2hhdC9jb21wbGV0aW9ucyArIFJlc3BvbnNlczogdGhlIHN1cmZhY2UgYSBjaGF0IGFnZW50IG5lZWRzLiAqL1xuICBjaGF0OiBib29sZWFuO1xuICAvKiogRW1iZWRkaW5nICh2ZWN0b3IpIGVuZHBvaW50LiAqL1xuICBlbWJlZGRpbmc6IGJvb2xlYW47XG4gIC8qKiBGdW5jdGlvbiAvIHRvb2wgY2FsbGluZy4gRXZlcnkgY2hhdCBlbmRwb2ludCBvbiBEYXRhYnJpY2tzIHN1cHBvcnRzIGl0LiAqL1xuICB0b29sczogYm9vbGVhbjtcbn1cblxuLyoqXG4gKiBEZXJpdmUgYW4gZW5kcG9pbnQncyBjYXBhYmlsaXRpZXMgZnJvbSBpdHMgRGF0YWJyaWNrcyB0YXNrIGhpbnQgYW5kIGl0c1xuICogY2xhc3NpZmllZCB7QGxpbmsgTW9kZWxDbGFzc30sIHNvIGNvbnN1bWVycyBmaWx0ZXIgb24gY2FwYWJpbGl0eSBpbnN0ZWFkIG9mXG4gKiByZS1kZXJpdmluZyBpdCBmcm9tIHJhdyBgdGFza2AgLyBgY2xhc3NgIHN0cmluZ3MuXG4gKlxuICogRWl0aGVyIHNpZ25hbCBhbG9uZSBpcyBlbm91Z2g6IHRoZSB0YXNrIGhpbnQgaXMgYXV0aG9yaXRhdGl2ZSB3aGVuIHByZXNlbnQsXG4gKiBhbmQgdGhlIGNsYXNzIGNvdmVycyBlbmRwb2ludHMgRGF0YWJyaWNrcyBsZWZ0IHVudGFza2VkIGJ1dCB0aGUgY2xhc3NpZmllclxuICogcmVjb2duaXplZC4gRW1iZWRkaW5nIHdpbnMgb3ZlciBjaGF0IHdoZW4gYm90aCBwb2ludCBhdCBpdCwgc2luY2UgdGhlIHR3byBhcmVcbiAqIG5vdCBpbnRlcmNoYW5nZWFibGUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBlbmRwb2ludENhcGFiaWxpdGllcyhlbmRwb2ludDogU2VydmluZ0VuZHBvaW50U3VtbWFyeSk6IEVuZHBvaW50Q2FwYWJpbGl0aWVzIHtcbiAgY29uc3QgZW1iZWRkaW5nID0gZW5kcG9pbnQudGFzayA9PT0gRU1CRURESU5HX1RBU0sgfHwgZW5kcG9pbnQuY2xhc3MgPT09IE1vZGVsQ2xhc3MuRW1iZWRkaW5nO1xuICAvLyBFdmVyeSBub24tZW1iZWRkaW5nIGNsYXNzIGlzIGEgY2hhdCBiYW5kLCBzbyBcImNsYXNzaWZpZWQgYnV0IG5vdCBlbWJlZGRpbmdcIlxuICAvLyBpcyB0aGUgY2xhc3Mtc2lkZSB0ZXN0IGZvciBjaGF0IGNhcGFiaWxpdHkuXG4gIGNvbnN0IGNoYXQgPSAhZW1iZWRkaW5nICYmIChlbmRwb2ludC50YXNrID09PSBDSEFUX1RBU0sgfHwgZW5kcG9pbnQuY2xhc3MgIT09IHVuZGVmaW5lZCk7XG4gIHJldHVybiB7IGNoYXQsIGVtYmVkZGluZywgdG9vbHM6IGNoYXQgfTtcbn1cblxuLyoqIEZhbWlseS1oZXVyaXN0aWMgY2xhc3NpZmljYXRpb24gb2YgYSBzaW5nbGUgZW5kcG9pbnQgbmFtZS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRmFtaWx5Q2xhc3Mge1xuICAvKiogQ2hhdCBjYXBhYmlsaXR5IGJhbmQgdGhlIGZhbWlseSBtYXBzIHRvIChuZXZlciBlbWJlZGRpbmcpLiAqL1xuICBjbGFzczogTW9kZWxDbGFzcztcbiAgLyoqIEludHJhLWZhbWlseSBvcmRlcmluZyBoaW50IChoaWdoZXIgaXMgbmV3ZXIgLyBtb3JlIGNhcGFibGUpLiAqL1xuICByYW5rOiBudW1iZXI7XG59XG5cbi8qKlxuICogTnVtZXJpYyBgW21ham9yLCBtaW5vciwgcGF0Y2hdYCB2ZXJzaW9uIHBhcnNlZCBmcm9tIGFuIGVuZHBvaW50XG4gKiBuYW1lLCB1c2VkIHRvIG9yZGVyIHNpYmxpbmdzIHdpdGhpbiBhIGZhbWlseS90aWVyLiBTdGFydHMgYXQgdGhlXG4gKiBmaXJzdCBkaWdpdCBpbiB0aGUgbmFtZSwgdGhlbiByZWFkcyBzdWNjZXNzaXZlIHNlcGFyYXRvci1kZWxpbWl0ZWQsXG4gKiBkaWdpdC1wcmVmaXhlZCBjaHVua3MgYXMgdGhlIHRocmVlIGNvbXBvbmVudHMgKG1pc3Npbmcgb25lcyBkZWZhdWx0XG4gKiB0byBgMGApOlxuICpcbiAqICAgLSBgZGF0YWJyaWNrcy1jbGF1ZGUtb3B1cy00LThgICAgLT4gYFs0LCA4LCAwXWBcbiAqICAgLSBgZGF0YWJyaWNrcy1jbGF1ZGUtb3B1cy00LTEwYCAgLT4gYFs0LCAxMCwgMF1gIChzb3J0cyBhYm92ZSA0LTgpXG4gKiAgIC0gYGRhdGFicmlja3MtbWV0YS1sbGFtYS0zLTMtNzBiYC0+IGBbMywgMywgNzBdYFxuICogICAtIGBkYXRhYnJpY2tzLWJnZS1sYXJnZS1lbmAgICAgICAtPiBgWzAsIDAsIDBdYCAobm8gZGlnaXRzKVxuICpcbiAqIENvbXBvbmVudC13aXNlIGNvbXBhcmlzb24gKG5vdCBhIGRlY2ltYWwgY29sbGFwc2UpIHNvIGA0LjEwYCBiZWF0c1xuICogYDQuOGAgLSB0aGUgYnVnIGEgYG1ham9yICsgbWlub3IvMTBgIHNjb3JlIHdvdWxkIGhpdC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHZlcnNpb25UdXBsZShuYW1lOiBzdHJpbmcpOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0ge1xuICBjb25zdCBzdGFydCA9IG5hbWUuc2VhcmNoKC9cXGQvKTtcbiAgaWYgKHN0YXJ0IDwgMCkgcmV0dXJuIFswLCAwLCAwXTtcbiAgY29uc3QgbnVtcyA9IG5hbWVcbiAgICAuc2xpY2Uoc3RhcnQpXG4gICAgLnNwbGl0KC9bXmEtejAtOV0rL2kpXG4gICAgLm1hcCgoY2h1bmspID0+IGNodW5rLm1hdGNoKC9eXFxkKy8pPy5bMF0pXG4gICAgLmZpbHRlcigoZGlnaXRzKTogZGlnaXRzIGlzIHN0cmluZyA9PiBkaWdpdHMgIT09IHVuZGVmaW5lZClcbiAgICAubWFwKE51bWJlcik7XG4gIHJldHVybiBbbnVtc1swXSA/PyAwLCBudW1zWzFdID8/IDAsIG51bXNbMl0gPz8gMF07XG59XG5cbi8qKiBDb21wYXJlIHR3byB2ZXJzaW9uIHR1cGxlcyBzbyB0aGUgaGlnaGVyIHZlcnNpb24gc29ydHMgZmlyc3QgKGRlc2NlbmRpbmcpLiAqL1xuZnVuY3Rpb24gY29tcGFyZVZlcnNpb25EZXNjKGE6IHJlYWRvbmx5IG51bWJlcltdLCBiOiByZWFkb25seSBudW1iZXJbXSk6IG51bWJlciB7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgMzsgaSsrKSB7XG4gICAgY29uc3QgZGlmZiA9IChiW2ldID8/IDApIC0gKGFbaV0gPz8gMCk7XG4gICAgaWYgKGRpZmYgIT09IDApIHJldHVybiBkaWZmO1xuICB9XG4gIHJldHVybiAwO1xufVxuXG4vKipcbiAqIE1vbm90b25pYyB2ZXJzaW9uIGtleSBmb3Igb3JkZXJpbmcgc2libGluZ3Mgd2l0aGluIGEgZmFtaWx5L3RpZXJcbiAqIChlLmcuIGBvcHVzLTQtOGAgb3ZlciBgb3B1cy00LTdgKS4gRW5jb2RlcyB0aGUge0BsaW5rIHZlcnNpb25UdXBsZX1cbiAqIGFzIGEgc2luZ2xlIG51bWJlciBzbyBjYWxsZXJzIHRoYXQgbmVlZCBhIHNjYWxhciByYW5rICh0aGUgZmFtaWx5XG4gKiBoZXVyaXN0aWMsIHRoZSBzdGF0aWMgZmFsbGJhY2sgb3JkZXJpbmcpIGtlZXAgd29ya2luZzsgdGhlIGVuY29kaW5nXG4gKiBwcmVzZXJ2ZXMgY29tcG9uZW50IG9yZGVyIHdpdGhvdXQgdGhlIGRlY2ltYWwtY29sbGFwc2UgYnVnLlxuICovXG5mdW5jdGlvbiB2ZXJzaW9uU2NvcmUobmFtZTogc3RyaW5nKTogbnVtYmVyIHtcbiAgY29uc3QgW21ham9yLCBtaW5vciwgcGF0Y2hdID0gdmVyc2lvblR1cGxlKG5hbWUpO1xuICByZXR1cm4gbWFqb3IgKiAxXzAwMF8wMDAgKyBtaW5vciAqIDFfMDAwICsgcGF0Y2g7XG59XG5cbi8qKlxuICogQmVzdC1lZmZvcnQgY2hhdCBjYXBhYmlsaXR5IGJhbmQgZm9yIGFuIGVuZHBvaW50IHdlIGhhdmUgbm8gbGl2ZVxuICogc2NvcmUgZm9yLCBrZXllZCBvZmYgcHJvdmlkZXIgZmFtaWx5IGFuZCB0aGUgd2VsbC1rbm93biB2YXJpYW50IHdvcmRzXG4gKiBpbiB0aGUgbmFtZSAoYG9wdXNgL2Bzb25uZXRgL2BoYWlrdWAsIGBwcm9gL2BtaW5pYC9gbmFub2AsXG4gKiBgZmxhc2hgL2BmbGFzaC1saXRlYCwgTGxhbWEgcGFyYW1ldGVyIHNpemVzLCBldGMpLiBSZXR1cm5zIGBudWxsYCBmb3JcbiAqIG5hbWVzIHdlIGRvbid0IHJlY29nbml6ZSBzbyB1bmtub3duIGN1c3RvbSBlbmRwb2ludHMgYXJlIG5ldmVyXG4gKiBhdXRvLXNlbGVjdGVkIGFzIGEgZGVmYXVsdC4gVGhlIGFjY29tcGFueWluZyBgcmFua2Agb3JkZXJzIHNpYmxpbmdzXG4gKiB3aXRoaW4gYSBjbGFzcy4gT25seSBldmVyIHJldHVybnMgYSBjaGF0IGJhbmQgLSBlbWJlZGRpbmcgZW5kcG9pbnRzXG4gKiBhcmUgY2xhc3NpZmllZCBieSB0YXNrLCBub3QgbmFtZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNsYXNzaWZ5QnlGYW1pbHkobmFtZTogc3RyaW5nKTogRmFtaWx5Q2xhc3MgfCBudWxsIHtcbiAgY29uc3QgbiA9IG5hbWUudG9Mb3dlckNhc2UoKTtcbiAgY29uc3QgYXQgPSAoY2xzOiBNb2RlbENsYXNzKTogRmFtaWx5Q2xhc3MgPT4gKHsgY2xhc3M6IGNscywgcmFuazogdmVyc2lvblNjb3JlKG4pIH0pO1xuXG4gIC8vIEFudGhyb3BpYyBDbGF1ZGVcbiAgaWYgKG4uaW5jbHVkZXMoXCJvcHVzXCIpKSByZXR1cm4gYXQoTW9kZWxDbGFzcy5DaGF0VGhpbmtpbmcpO1xuICBpZiAobi5pbmNsdWRlcyhcInNvbm5ldFwiKSkgcmV0dXJuIGF0KE1vZGVsQ2xhc3MuQ2hhdEJhbGFuY2VkKTtcbiAgaWYgKG4uaW5jbHVkZXMoXCJoYWlrdVwiKSkgcmV0dXJuIGF0KE1vZGVsQ2xhc3MuQ2hhdEZhc3QpO1xuXG4gIC8vIE9wZW5BSSBvcGVuLXdlaWdodHMgKGNoZWNrIGJlZm9yZSB0aGUgZ2VuZXJpYyBncHQgYnJhbmNoKVxuICBpZiAobi5pbmNsdWRlcyhcImdwdC1vc3NcIikpIHtcbiAgICByZXR1cm4gYXQobi5pbmNsdWRlcyhcIjEyMGJcIikgPyBNb2RlbENsYXNzLkNoYXRCYWxhbmNlZCA6IE1vZGVsQ2xhc3MuQ2hhdEZhc3QpO1xuICB9XG4gIC8vIE9wZW5BSSBHUFQgZmFtaWx5XG4gIGlmIChuLmluY2x1ZGVzKFwiZ3B0XCIpKSB7XG4gICAgaWYgKG4uaW5jbHVkZXMoXCJwcm9cIikpIHJldHVybiBhdChNb2RlbENsYXNzLkNoYXRUaGlua2luZyk7XG4gICAgaWYgKG4uaW5jbHVkZXMoXCJtaW5pXCIpIHx8IG4uaW5jbHVkZXMoXCJuYW5vXCIpKSByZXR1cm4gYXQoTW9kZWxDbGFzcy5DaGF0RmFzdCk7XG4gICAgcmV0dXJuIGF0KE1vZGVsQ2xhc3MuQ2hhdEJhbGFuY2VkKTtcbiAgfVxuXG4gIC8vIEdvb2dsZSBHZW1pbmkgLyBHZW1tYVxuICBpZiAobi5pbmNsdWRlcyhcImdlbWluaVwiKSkge1xuICAgIGlmIChuLmluY2x1ZGVzKFwiZmxhc2gtbGl0ZVwiKSkgcmV0dXJuIGF0KE1vZGVsQ2xhc3MuQ2hhdEZhc3QpO1xuICAgIGlmIChuLmluY2x1ZGVzKFwicHJvXCIpKSByZXR1cm4gYXQoTW9kZWxDbGFzcy5DaGF0VGhpbmtpbmcpO1xuICAgIHJldHVybiBhdChNb2RlbENsYXNzLkNoYXRCYWxhbmNlZCk7XG4gIH1cbiAgaWYgKG4uaW5jbHVkZXMoXCJnZW1tYVwiKSkgcmV0dXJuIGF0KE1vZGVsQ2xhc3MuQ2hhdEZhc3QpO1xuXG4gIC8vIE1ldGEgTGxhbWFcbiAgaWYgKG4uaW5jbHVkZXMoXCJsbGFtYVwiKSkge1xuICAgIGlmIChuLmluY2x1ZGVzKFwibWF2ZXJpY2tcIikgfHwgbi5pbmNsdWRlcyhcIjQwNWJcIikpIHJldHVybiBhdChNb2RlbENsYXNzLkNoYXRUaGlua2luZyk7XG4gICAgaWYgKG4uaW5jbHVkZXMoXCI3MGJcIikpIHJldHVybiBhdChNb2RlbENsYXNzLkNoYXRCYWxhbmNlZCk7XG4gICAgaWYgKG4uaW5jbHVkZXMoXCI4YlwiKSB8fCBuLmluY2x1ZGVzKFwiMWJcIikpIHJldHVybiBhdChNb2RlbENsYXNzLkNoYXRGYXN0KTtcbiAgICByZXR1cm4gYXQoTW9kZWxDbGFzcy5DaGF0QmFsYW5jZWQpO1xuICB9XG5cbiAgLy8gQWxpYmFiYSBRd2VuXG4gIGlmIChuLmluY2x1ZGVzKFwicXdlblwiKSkgcmV0dXJuIGF0KE1vZGVsQ2xhc3MuQ2hhdEJhbGFuY2VkKTtcblxuICByZXR1cm4gbnVsbDtcbn1cblxuLyoqIExpbmVhci1pbnRlcnBvbGF0ZWQgcXVhbnRpbGUgb2YgYW4gYXNjZW5kaW5nLXNvcnRlZCBudW1lcmljIGFycmF5LiAqL1xuZnVuY3Rpb24gcXVhbnRpbGUoc29ydGVkQXNjOiByZWFkb25seSBudW1iZXJbXSwgcDogbnVtYmVyKTogbnVtYmVyIHtcbiAgaWYgKHNvcnRlZEFzYy5sZW5ndGggPT09IDApIHJldHVybiBOdW1iZXIuTmFOO1xuICBjb25zdCBpZHggPSAoc29ydGVkQXNjLmxlbmd0aCAtIDEpICogcDtcbiAgY29uc3QgbG8gPSBNYXRoLmZsb29yKGlkeCk7XG4gIGNvbnN0IGhpID0gTWF0aC5jZWlsKGlkeCk7XG4gIGlmIChsbyA9PT0gaGkpIHJldHVybiBzb3J0ZWRBc2NbbG9dITtcbiAgcmV0dXJuIHNvcnRlZEFzY1tsb10hICsgKHNvcnRlZEFzY1toaV0hIC0gc29ydGVkQXNjW2xvXSEpICogKGlkeCAtIGxvKTtcbn1cblxuLyoqIEludGVybmFsIHNvcnRhYmxlIHdyYXBwZXIgY2FycnlpbmcgdGhlIG9yZGVyaW5nIGtleXMgZm9yIGEgYnVja2V0IGVudHJ5LiAqL1xuaW50ZXJmYWNlIFJhbmtlZCB7XG4gIGVwOiBTZXJ2aW5nRW5kcG9pbnRTdW1tYXJ5O1xuICAvKiogUXVhbGl0eSAoc2NvcmVkKSBvciBmYW1pbHkgdmVyc2lvbiAodW5zY29yZWQpIC0gaGlnaGVyIGZpcnN0LiAqL1xuICBzb3J0OiBudW1iZXI7XG4gIC8qKiBTY29yZWQgZW5kcG9pbnRzIHJhbmsgYWhlYWQgb2YgZmFtaWx5LW9ubHkgZ3Vlc3Nlcy4gKi9cbiAgc2NvcmVkOiBib29sZWFuO1xuICB0aWVDb3N0OiBudW1iZXI7XG4gIHRpZVNwZWVkOiBudW1iZXI7XG4gIC8qKiBQYXJzZWQgbmFtZSB2ZXJzaW9uLCB0aGUgZmluYWwgdGllLWJyZWFrZXIgKG5ld2VyIHNpYmxpbmcgZmlyc3QpLiAqL1xuICB2ZXJzaW9uOiByZWFkb25seSBudW1iZXJbXTtcbn1cblxuZnVuY3Rpb24gcmFua09yZGVyKGE6IFJhbmtlZCwgYjogUmFua2VkKTogbnVtYmVyIHtcbiAgaWYgKGEuc2NvcmVkICE9PSBiLnNjb3JlZCkgcmV0dXJuIGEuc2NvcmVkID8gLTEgOiAxO1xuICBpZiAoYi5zb3J0ICE9PSBhLnNvcnQpIHJldHVybiBiLnNvcnQgLSBhLnNvcnQ7XG4gIGlmIChhLnRpZUNvc3QgIT09IGIudGllQ29zdCkgcmV0dXJuIGEudGllQ29zdCAtIGIudGllQ29zdDtcbiAgaWYgKGIudGllU3BlZWQgIT09IGEudGllU3BlZWQpIHJldHVybiBiLnRpZVNwZWVkIC0gYS50aWVTcGVlZDtcbiAgLy8gU2FtZSBzY29yZWQtbmVzcywgcXVhbGl0eSwgY29zdCwgYW5kIHNwZWVkIChlLmcuIHNldmVyYWwgQ2xhdWRlXG4gIC8vIE9wdXMgcG9pbnQgcmVsZWFzZXMpOiBwcmVmZXIgdGhlIG5ld2VyIHBhcnNlZCB2ZXJzaW9uIHNvXG4gIC8vIGBvcHVzLTQtOGAgYmVhdHMgYG9wdXMtNC03YC5cbiAgcmV0dXJuIGNvbXBhcmVWZXJzaW9uRGVzYyhhLnZlcnNpb24sIGIudmVyc2lvbik7XG59XG5cbi8qKlxuICogQnVja2V0IGxpdmUgZW5kcG9pbnRzIGludG8ge0BsaW5rIE1vZGVsQ2xhc3N9ZXMsIHJhbmtlZCBiZXN0LWZpcnN0XG4gKiB3aXRoaW4gZWFjaCBjaGF0IGJhbmQuXG4gKlxuICogRW1iZWRkaW5nIGVuZHBvaW50cyAoYHRhc2sgPT09IFwibGxtL3YxL2VtYmVkZGluZ3NcImApIGdvIGludG9cbiAqIHtAbGluayBNb2RlbENsYXNzLkVtYmVkZGluZ30gYnkgdGFzaywgaW4gbGlzdGluZyBvcmRlciAodGhleSBjYXJyeSBub1xuICogY2FwYWJpbGl0eSBzY29yZSB0byByYW5rIG9uKS5cbiAqXG4gKiBDaGF0IGVuZHBvaW50cyAoYHRhc2sgPT09IFwibGxtL3YxL2NoYXRcImApIHNwbGl0IGludG8gdGhlIHRocmVlIGNoYXRcbiAqIGJhbmRzLiBTY29yZWQgZW5kcG9pbnRzICh0aG9zZSBjYXJyeWluZyBhIGBwcm9maWxlLnF1YWxpdHlgKSBkcml2ZVxuICogdGhlIGJhbmRpbmc6IHRoZSBvYnNlcnZlZCBxdWFsaXR5IGRpc3RyaWJ1dGlvbiBpcyBzcGxpdCBhdCBpdHMgMS8zXG4gKiBhbmQgMi8zIHF1YW50aWxlcywgc28gdGhlIHRvcCB0aGlyZCBpcyB7QGxpbmsgTW9kZWxDbGFzcy5DaGF0VGhpbmtpbmd9LFxuICogdGhlIGJvdHRvbSB0aGlyZCB7QGxpbmsgTW9kZWxDbGFzcy5DaGF0RmFzdH0sIGFuZCB0aGUgbWlkZGxlXG4gKiB7QGxpbmsgTW9kZWxDbGFzcy5DaGF0QmFsYW5jZWR9LiBCZWNhdXNlIHRoZSB0aHJlc2hvbGRzIGNvbWUgZnJvbSB0aGVcbiAqIGRhdGEsIHRoZSBzcGxpdCBhZGFwdHMgYXMgRGF0YWJyaWNrcyBhZGRzIG9yIHJlc2NvcmVzIG1vZGVscyAtXG4gKiBub3RoaW5nIGlzIHBpbm5lZCB0byBhIGZpeGVkIHNjb3JlIGJhbmQuXG4gKlxuICogVW5zY29yZWQgY2hhdCBlbmRwb2ludHMgYXJlIHBsYWNlZCBieSB7QGxpbmsgY2xhc3NpZnlCeUZhbWlseX0gYW5kXG4gKiByYW5rZWQgYWZ0ZXIgdGhlIHNjb3JlZCBvbmVzIGluIHRoZWlyIGJhbmQ7IHVucmVjb2duaXplZCwgdW5zY29yZWRcbiAqIGVuZHBvaW50cyAoZS5nLiBjdXN0b20gZXh0ZXJuYWwgbW9kZWxzKSBhcmUgb21pdHRlZCBlbnRpcmVseSBzbyB0aGV5XG4gKiBhcmUgbmV2ZXIgcGlja2VkIGFzIGFuIGF1dG9tYXRpYyBkZWZhdWx0LlxuICpcbiAqIFdpdGhpbiBhIGNoYXQgYmFuZCwgc2NvcmVkIG1vZGVscyBzb3J0IGJ5IGBxdWFsaXR5YCBkZXNjLCB0aGVuIGBjb3N0YFxuICogYXNjLCB0aGVuIGBzcGVlZGAgZGVzYywgdGhlbiBwYXJzZWQgbmFtZSB2ZXJzaW9uIGRlc2M7IGZhbWlseS1vbmx5XG4gKiBtb2RlbHMgc29ydCBieSB2ZXJzaW9uIHJhbmsgdGhlbiBwYXJzZWQgdmVyc2lvbi4gVGhlIHZlcnNpb25cbiAqIHRpZS1icmVhayAoe0BsaW5rIHZlcnNpb25UdXBsZX0pIGlzIHdoYXQgc2VwYXJhdGVzIHBvaW50IHJlbGVhc2VzXG4gKiB0aGF0IHNoYXJlIGEgc2NvcmUgcHJvZmlsZSAoZS5nLiBgb3B1cy00LThgIGFoZWFkIG9mIGBvcHVzLTQtN2ApLlxuICovXG5leHBvcnQgZnVuY3Rpb24gY2xhc3NpZnlFbmRwb2ludHMoXG4gIGVuZHBvaW50czogcmVhZG9ubHkgU2VydmluZ0VuZHBvaW50U3VtbWFyeVtdLFxuKTogUmVjb3JkPE1vZGVsQ2xhc3MsIFNlcnZpbmdFbmRwb2ludFN1bW1hcnlbXT4ge1xuICBjb25zdCBjaGF0ID0gZW5kcG9pbnRzLmZpbHRlcigoZSkgPT4gZS50YXNrID09PSBDSEFUX1RBU0spO1xuICBjb25zdCBxdWFsaXRpZXMgPSBjaGF0XG4gICAgLm1hcCgoZSkgPT4gZS5wcm9maWxlPy5xdWFsaXR5KVxuICAgIC5maWx0ZXIoKHEpOiBxIGlzIG51bWJlciA9PiBOdW1iZXIuaXNGaW5pdGUocSkpXG4gICAgLnNvcnQoKGEsIGIpID0+IGEgLSBiKTtcbiAgY29uc3QgbG93ID0gcXVhbnRpbGUocXVhbGl0aWVzLCAxIC8gMyk7XG4gIGNvbnN0IGhpZ2ggPSBxdWFudGlsZShxdWFsaXRpZXMsIDIgLyAzKTtcblxuICBjb25zdCBidWNrZXRzOiBSZWNvcmQ8TW9kZWxDbGFzcywgUmFua2VkW10+ID0ge1xuICAgIFtNb2RlbENsYXNzLkNoYXRUaGlua2luZ106IFtdLFxuICAgIFtNb2RlbENsYXNzLkNoYXRCYWxhbmNlZF06IFtdLFxuICAgIFtNb2RlbENsYXNzLkNoYXRGYXN0XTogW10sXG4gICAgW01vZGVsQ2xhc3MuRW1iZWRkaW5nXTogW10sXG4gIH07XG5cbiAgZm9yIChjb25zdCBlcCBvZiBjaGF0KSB7XG4gICAgY29uc3QgcSA9IGVwLnByb2ZpbGU/LnF1YWxpdHk7XG4gICAgaWYgKE51bWJlci5pc0Zpbml0ZShxKSkge1xuICAgICAgY29uc3QgcXVhbGl0eSA9IHEgYXMgbnVtYmVyO1xuICAgICAgY29uc3QgY2xzID1cbiAgICAgICAgcXVhbGl0eSA+PSBoaWdoXG4gICAgICAgICAgPyBNb2RlbENsYXNzLkNoYXRUaGlua2luZ1xuICAgICAgICAgIDogcXVhbGl0eSA8PSBsb3dcbiAgICAgICAgICAgID8gTW9kZWxDbGFzcy5DaGF0RmFzdFxuICAgICAgICAgICAgOiBNb2RlbENsYXNzLkNoYXRCYWxhbmNlZDtcbiAgICAgIGJ1Y2tldHNbY2xzXS5wdXNoKHtcbiAgICAgICAgZXAsXG4gICAgICAgIHNvcnQ6IHF1YWxpdHksXG4gICAgICAgIHNjb3JlZDogdHJ1ZSxcbiAgICAgICAgdGllQ29zdDogZXAucHJvZmlsZT8uY29zdCA/PyBOdW1iZXIuUE9TSVRJVkVfSU5GSU5JVFksXG4gICAgICAgIHRpZVNwZWVkOiBlcC5wcm9maWxlPy5zcGVlZCA/PyAwLFxuICAgICAgICB2ZXJzaW9uOiB2ZXJzaW9uVHVwbGUoZXAubmFtZSksXG4gICAgICB9KTtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cbiAgICBjb25zdCBmYW1pbHkgPSBjbGFzc2lmeUJ5RmFtaWx5KGVwLm5hbWUpO1xuICAgIGlmICghZmFtaWx5KSBjb250aW51ZTtcbiAgICBidWNrZXRzW2ZhbWlseS5jbGFzc10ucHVzaCh7XG4gICAgICBlcCxcbiAgICAgIHNvcnQ6IGZhbWlseS5yYW5rLFxuICAgICAgc2NvcmVkOiBmYWxzZSxcbiAgICAgIHRpZUNvc3Q6IE51bWJlci5QT1NJVElWRV9JTkZJTklUWSxcbiAgICAgIHRpZVNwZWVkOiAwLFxuICAgICAgdmVyc2lvbjogdmVyc2lvblR1cGxlKGVwLm5hbWUpLFxuICAgIH0pO1xuICB9XG5cbiAgLy8gRW1iZWRkaW5ncyBhcmUgYnVja2V0ZWQgYnkgdGFzayBpbiBsaXN0aW5nIG9yZGVyIC0gbm8gc2NvcmUgdG8gcmFuay5cbiAgY29uc3QgZW1iZWRkaW5ncyA9IGVuZHBvaW50cy5maWx0ZXIoKGUpID0+IGUudGFzayA9PT0gRU1CRURESU5HX1RBU0spO1xuXG4gIHJldHVybiB7XG4gICAgW01vZGVsQ2xhc3MuQ2hhdFRoaW5raW5nXTogYnVja2V0c1tNb2RlbENsYXNzLkNoYXRUaGlua2luZ10uc29ydChyYW5rT3JkZXIpLm1hcCgoeCkgPT4geC5lcCksXG4gICAgW01vZGVsQ2xhc3MuQ2hhdEJhbGFuY2VkXTogYnVja2V0c1tNb2RlbENsYXNzLkNoYXRCYWxhbmNlZF0uc29ydChyYW5rT3JkZXIpLm1hcCgoeCkgPT4geC5lcCksXG4gICAgW01vZGVsQ2xhc3MuQ2hhdEZhc3RdOiBidWNrZXRzW01vZGVsQ2xhc3MuQ2hhdEZhc3RdLnNvcnQocmFua09yZGVyKS5tYXAoKHgpID0+IHguZXApLFxuICAgIFtNb2RlbENsYXNzLkVtYmVkZGluZ106IGVtYmVkZGluZ3MsXG4gIH07XG59XG4iXX0=
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Derive a human-readable display name for a serving endpoint.
3
+ *
4
+ * When `provided` is a non-blank string (a Databricks display-name tag or
5
+ * external-model name), it wins verbatim (trimmed). Otherwise the endpoint
6
+ * `name` is humanized: leading vendor prefixes (`databricks`, the
7
+ * `system.ai` namespace, `dbx`) are dropped, then each `-`/`_`/`.`-separated
8
+ * segment is title-cased (acronyms like GPT/GTE/BGE uppercased, "AI" kept),
9
+ * runs of purely-numeric segments are joined into a dotted version
10
+ * (`...-4-6` -> "4.6"), and a size unit glued to its number (`120b` ->
11
+ * "120B"). Falls back to the trimmed raw `name` if stripping leaves nothing.
12
+ *
13
+ * @example
14
+ * toModelDisplayName("databricks-claude-sonnet-4-6") // "Claude Sonnet 4.6"
15
+ * toModelDisplayName("databricks-gpt-oss-120b") // "GPT OSS 120B"
16
+ * toModelDisplayName("system.ai.bge_large_en") // "BGE Large En"
17
+ * toModelDisplayName("x", "Claude 4.6 (Preview)") // provided wins
18
+ */
19
+ export declare function toModelDisplayName(name: string, provided?: string | null): string;
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Human-readable model display names. Pure (no Node-only imports) so a
3
+ * browser client can label a `/models` response without server deps.
4
+ *
5
+ * The endpoint `name` is the invoke id (e.g. `databricks-claude-sonnet-4-6`);
6
+ * this derives a friendly label from it. Prefer a Databricks-provided name
7
+ * (a display-name tag or an external-model name) when the server has one -
8
+ * see `@dbx-tools/model`'s serving lister, which passes it as `provided`.
9
+ *
10
+ * @module
11
+ */
12
+ import { string } from "@dbx-tools/shared-core";
13
+ /**
14
+ * Vendor / namespace prefixes stripped from a tokenized endpoint name
15
+ * before title-casing. Lowercased tokens; matched only as leading tokens
16
+ * so a legitimate mid-name token is never dropped. `ai` is stripped ONLY
17
+ * as the second half of the `system.ai.*` builtin namespace (handled
18
+ * below), never on its own - so a model literally named `...-ai-...`
19
+ * keeps its "AI" word via the tokenizer's built-in casing override.
20
+ */
21
+ const STRIP_LEADING_TOKENS = new Set(["databricks", "system", "dbx"]);
22
+ /** `ai` is stripped only as the `system.ai.*` namespace half (after `system`). */
23
+ const NAMESPACE_SECOND_TOKENS = { system: "ai" };
24
+ /**
25
+ * Segment-internal tokens rendered fully uppercased (acronyms), not
26
+ * title-cased. `ai` is intentionally omitted: the shared tokenizer already
27
+ * force-cases `ai` -> `AI` via its {@link string.TokenizeOptions} `capitalize`
28
+ * override, which `renderSegment` always passes.
29
+ */
30
+ const ACRONYMS = new Set(["gpt", "gte", "bge", "dbrx", "oss", "llm", "moe"]);
31
+ /** Parameter-count unit letters glued to a preceding number (`120b` -> `120B`). */
32
+ const SIZE_UNITS = new Set(["b", "m", "k"]);
33
+ const SEGMENT_SPLIT = /[-_.\s/]+/;
34
+ /**
35
+ * Derive a human-readable display name for a serving endpoint.
36
+ *
37
+ * When `provided` is a non-blank string (a Databricks display-name tag or
38
+ * external-model name), it wins verbatim (trimmed). Otherwise the endpoint
39
+ * `name` is humanized: leading vendor prefixes (`databricks`, the
40
+ * `system.ai` namespace, `dbx`) are dropped, then each `-`/`_`/`.`-separated
41
+ * segment is title-cased (acronyms like GPT/GTE/BGE uppercased, "AI" kept),
42
+ * runs of purely-numeric segments are joined into a dotted version
43
+ * (`...-4-6` -> "4.6"), and a size unit glued to its number (`120b` ->
44
+ * "120B"). Falls back to the trimmed raw `name` if stripping leaves nothing.
45
+ *
46
+ * @example
47
+ * toModelDisplayName("databricks-claude-sonnet-4-6") // "Claude Sonnet 4.6"
48
+ * toModelDisplayName("databricks-gpt-oss-120b") // "GPT OSS 120B"
49
+ * toModelDisplayName("system.ai.bge_large_en") // "BGE Large En"
50
+ * toModelDisplayName("x", "Claude 4.6 (Preview)") // provided wins
51
+ */
52
+ export function toModelDisplayName(name, provided) {
53
+ const providedTrimmed = string.trimToNull(provided);
54
+ if (providedTrimmed)
55
+ return providedTrimmed;
56
+ const segments = name.split(SEGMENT_SPLIT).filter(Boolean);
57
+ // Drop known vendor/namespace prefixes only while they lead the list.
58
+ let start = 0;
59
+ while (start < segments.length) {
60
+ const lower = segments[start].toLowerCase();
61
+ if (!STRIP_LEADING_TOKENS.has(lower))
62
+ break;
63
+ // Consume the namespace's second token too (e.g. the `ai` of `system.ai`).
64
+ if (NAMESPACE_SECOND_TOKENS[lower] === segments[start + 1]?.toLowerCase()) {
65
+ start += 1;
66
+ }
67
+ start += 1;
68
+ }
69
+ const kept = segments.slice(start);
70
+ const source = kept.length > 0 ? kept : segments;
71
+ const pieces = [];
72
+ let versionRun = [];
73
+ const flushVersion = () => {
74
+ if (versionRun.length > 0) {
75
+ pieces.push(versionRun.join("."));
76
+ versionRun = [];
77
+ }
78
+ };
79
+ for (const segment of source) {
80
+ if (/^\d+$/.test(segment)) {
81
+ versionRun.push(segment);
82
+ continue;
83
+ }
84
+ flushVersion();
85
+ pieces.push(renderSegment(segment));
86
+ }
87
+ flushVersion();
88
+ return pieces.join(" ") || name.trim();
89
+ }
90
+ /** Title-case one name segment, uppercasing acronyms and gluing size units. */
91
+ function renderSegment(segment) {
92
+ const tokens = [...string.tokenizeWithOptions({ capitalize: true }, segment)];
93
+ const out = [];
94
+ for (let i = 0; i < tokens.length; i += 1) {
95
+ const token = tokens[i];
96
+ const lower = token.toLowerCase();
97
+ // Glue a size unit onto the preceding number: `120` + `b` -> `120B`.
98
+ if (SIZE_UNITS.has(lower) && out.length > 0 && /^\d+$/.test(out[out.length - 1])) {
99
+ out[out.length - 1] = `${out[out.length - 1]}${lower.toUpperCase()}`;
100
+ continue;
101
+ }
102
+ out.push(ACRONYMS.has(lower) ? lower.toUpperCase() : token);
103
+ }
104
+ return out.join(" ");
105
+ }
106
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGlzcGxheS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaXNwbGF5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7O0dBVUc7QUFDSCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFFaEQ7Ozs7Ozs7R0FPRztBQUNILE1BQU0sb0JBQW9CLEdBQUcsSUFBSSxHQUFHLENBQUMsQ0FBQyxZQUFZLEVBQUUsUUFBUSxFQUFFLEtBQUssQ0FBQyxDQUFDLENBQUM7QUFFdEUsa0ZBQWtGO0FBQ2xGLE1BQU0sdUJBQXVCLEdBQTJCLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxDQUFDO0FBRXpFOzs7OztHQUtHO0FBQ0gsTUFBTSxRQUFRLEdBQUcsSUFBSSxHQUFHLENBQUMsQ0FBQyxLQUFLLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxLQUFLLENBQUMsQ0FBQyxDQUFDO0FBRTdFLG1GQUFtRjtBQUNuRixNQUFNLFVBQVUsR0FBRyxJQUFJLEdBQUcsQ0FBQyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQztBQUU1QyxNQUFNLGFBQWEsR0FBRyxXQUFXLENBQUM7QUFFbEM7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBaUJHO0FBQ0gsTUFBTSxVQUFVLGtCQUFrQixDQUFDLElBQVksRUFBRSxRQUF3QjtJQUN2RSxNQUFNLGVBQWUsR0FBRyxNQUFNLENBQUMsVUFBVSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ3BELElBQUksZUFBZTtRQUFFLE9BQU8sZUFBZSxDQUFDO0lBRTVDLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsYUFBYSxDQUFDLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQzNELHNFQUFzRTtJQUN0RSxJQUFJLEtBQUssR0FBRyxDQUFDLENBQUM7SUFDZCxPQUFPLEtBQUssR0FB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QyxDQUFDLEtBQUssQ0FBQyxXQUFXLEVBQUUsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDOUQsQ0FBQztJQUNELE9BQU8sR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztBQUN2QixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBIdW1hbi1yZWFkYWJsZSBtb2RlbCBkaXNwbGF5IG5hbWVzLiBQdXJlIChubyBOb2RlLW9ubHkgaW1wb3J0cykgc28gYVxuICogYnJvd3NlciBjbGllbnQgY2FuIGxhYmVsIGEgYC9tb2RlbHNgIHJlc3BvbnNlIHdpdGhvdXQgc2VydmVyIGRlcHMuXG4gKlxuICogVGhlIGVuZHBvaW50IGBuYW1lYCBpcyB0aGUgaW52b2tlIGlkIChlLmcuIGBkYXRhYnJpY2tzLWNsYXVkZS1zb25uZXQtNC02YCk7XG4gKiB0aGlzIGRlcml2ZXMgYSBmcmllbmRseSBsYWJlbCBmcm9tIGl0LiBQcmVmZXIgYSBEYXRhYnJpY2tzLXByb3ZpZGVkIG5hbWVcbiAqIChhIGRpc3BsYXktbmFtZSB0YWcgb3IgYW4gZXh0ZXJuYWwtbW9kZWwgbmFtZSkgd2hlbiB0aGUgc2VydmVyIGhhcyBvbmUgLVxuICogc2VlIGBAZGJ4LXRvb2xzL21vZGVsYCdzIHNlcnZpbmcgbGlzdGVyLCB3aGljaCBwYXNzZXMgaXQgYXMgYHByb3ZpZGVkYC5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cbmltcG9ydCB7IHN0cmluZyB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1jb3JlXCI7XG5cbi8qKlxuICogVmVuZG9yIC8gbmFtZXNwYWNlIHByZWZpeGVzIHN0cmlwcGVkIGZyb20gYSB0b2tlbml6ZWQgZW5kcG9pbnQgbmFtZVxuICogYmVmb3JlIHRpdGxlLWNhc2luZy4gTG93ZXJjYXNlZCB0b2tlbnM7IG1hdGNoZWQgb25seSBhcyBsZWFkaW5nIHRva2Vuc1xuICogc28gYSBsZWdpdGltYXRlIG1pZC1uYW1lIHRva2VuIGlzIG5ldmVyIGRyb3BwZWQuIGBhaWAgaXMgc3RyaXBwZWQgT05MWVxuICogYXMgdGhlIHNlY29uZCBoYWxmIG9mIHRoZSBgc3lzdGVtLmFpLipgIGJ1aWx0aW4gbmFtZXNwYWNlIChoYW5kbGVkXG4gKiBiZWxvdyksIG5ldmVyIG9uIGl0cyBvd24gLSBzbyBhIG1vZGVsIGxpdGVyYWxseSBuYW1lZCBgLi4uLWFpLS4uLmBcbiAqIGtlZXBzIGl0cyBcIkFJXCIgd29yZCB2aWEgdGhlIHRva2VuaXplcidzIGJ1aWx0LWluIGNhc2luZyBvdmVycmlkZS5cbiAqL1xuY29uc3QgU1RSSVBfTEVBRElOR19UT0tFTlMgPSBuZXcgU2V0KFtcImRhdGFicmlja3NcIiwgXCJzeXN0ZW1cIiwgXCJkYnhcIl0pO1xuXG4vKiogYGFpYCBpcyBzdHJpcHBlZCBvbmx5IGFzIHRoZSBgc3lzdGVtLmFpLipgIG5hbWVzcGFjZSBoYWxmIChhZnRlciBgc3lzdGVtYCkuICovXG5jb25zdCBOQU1FU1BBQ0VfU0VDT05EX1RPS0VOUzogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHsgc3lzdGVtOiBcImFpXCIgfTtcblxuLyoqXG4gKiBTZWdtZW50LWludGVybmFsIHRva2VucyByZW5kZXJlZCBmdWxseSB1cHBlcmNhc2VkIChhY3JvbnltcyksIG5vdFxuICogdGl0bGUtY2FzZWQuIGBhaWAgaXMgaW50ZW50aW9uYWxseSBvbWl0dGVkOiB0aGUgc2hhcmVkIHRva2VuaXplciBhbHJlYWR5XG4gKiBmb3JjZS1jYXNlcyBgYWlgIC0+IGBBSWAgdmlhIGl0cyB7QGxpbmsgc3RyaW5nLlRva2VuaXplT3B0aW9uc30gYGNhcGl0YWxpemVgXG4gKiBvdmVycmlkZSwgd2hpY2ggYHJlbmRlclNlZ21lbnRgIGFsd2F5cyBwYXNzZXMuXG4gKi9cbmNvbnN0IEFDUk9OWU1TID0gbmV3IFNldChbXCJncHRcIiwgXCJndGVcIiwgXCJiZ2VcIiwgXCJkYnJ4XCIsIFwib3NzXCIsIFwibGxtXCIsIFwibW9lXCJdKTtcblxuLyoqIFBhcmFtZXRlci1jb3VudCB1bml0IGxldHRlcnMgZ2x1ZWQgdG8gYSBwcmVjZWRpbmcgbnVtYmVyIChgMTIwYmAgLT4gYDEyMEJgKS4gKi9cbmNvbnN0IFNJWkVfVU5JVFMgPSBuZXcgU2V0KFtcImJcIiwgXCJtXCIsIFwia1wiXSk7XG5cbmNvbnN0IFNFR01FTlRfU1BMSVQgPSAvWy1fLlxccy9dKy87XG5cbi8qKlxuICogRGVyaXZlIGEgaHVtYW4tcmVhZGFibGUgZGlzcGxheSBuYW1lIGZvciBhIHNlcnZpbmcgZW5kcG9pbnQuXG4gKlxuICogV2hlbiBgcHJvdmlkZWRgIGlzIGEgbm9uLWJsYW5rIHN0cmluZyAoYSBEYXRhYnJpY2tzIGRpc3BsYXktbmFtZSB0YWcgb3JcbiAqIGV4dGVybmFsLW1vZGVsIG5hbWUpLCBpdCB3aW5zIHZlcmJhdGltICh0cmltbWVkKS4gT3RoZXJ3aXNlIHRoZSBlbmRwb2ludFxuICogYG5hbWVgIGlzIGh1bWFuaXplZDogbGVhZGluZyB2ZW5kb3IgcHJlZml4ZXMgKGBkYXRhYnJpY2tzYCwgdGhlXG4gKiBgc3lzdGVtLmFpYCBuYW1lc3BhY2UsIGBkYnhgKSBhcmUgZHJvcHBlZCwgdGhlbiBlYWNoIGAtYC9gX2AvYC5gLXNlcGFyYXRlZFxuICogc2VnbWVudCBpcyB0aXRsZS1jYXNlZCAoYWNyb255bXMgbGlrZSBHUFQvR1RFL0JHRSB1cHBlcmNhc2VkLCBcIkFJXCIga2VwdCksXG4gKiBydW5zIG9mIHB1cmVseS1udW1lcmljIHNlZ21lbnRzIGFyZSBqb2luZWQgaW50byBhIGRvdHRlZCB2ZXJzaW9uXG4gKiAoYC4uLi00LTZgIC0+IFwiNC42XCIpLCBhbmQgYSBzaXplIHVuaXQgZ2x1ZWQgdG8gaXRzIG51bWJlciAoYDEyMGJgIC0+XG4gKiBcIjEyMEJcIikuIEZhbGxzIGJhY2sgdG8gdGhlIHRyaW1tZWQgcmF3IGBuYW1lYCBpZiBzdHJpcHBpbmcgbGVhdmVzIG5vdGhpbmcuXG4gKlxuICogQGV4YW1wbGVcbiAqIHRvTW9kZWxEaXNwbGF5TmFtZShcImRhdGFicmlja3MtY2xhdWRlLXNvbm5ldC00LTZcIikgLy8gXCJDbGF1ZGUgU29ubmV0IDQuNlwiXG4gKiB0b01vZGVsRGlzcGxheU5hbWUoXCJkYXRhYnJpY2tzLWdwdC1vc3MtMTIwYlwiKSAgICAgIC8vIFwiR1BUIE9TUyAxMjBCXCJcbiAqIHRvTW9kZWxEaXNwbGF5TmFtZShcInN5c3RlbS5haS5iZ2VfbGFyZ2VfZW5cIikgICAgICAgLy8gXCJCR0UgTGFyZ2UgRW5cIlxuICogdG9Nb2RlbERpc3BsYXlOYW1lKFwieFwiLCBcIkNsYXVkZSA0LjYgKFByZXZpZXcpXCIpICAgIC8vIHByb3ZpZGVkIHdpbnNcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHRvTW9kZWxEaXNwbGF5TmFtZShuYW1lOiBzdHJpbmcsIHByb3ZpZGVkPzogc3RyaW5nIHwgbnVsbCk6IHN0cmluZyB7XG4gIGNvbnN0IHByb3ZpZGVkVHJpbW1lZCA9IHN0cmluZy50cmltVG9OdWxsKHByb3ZpZGVkKTtcbiAgaWYgKHByb3ZpZGVkVHJpbW1lZCkgcmV0dXJuIHByb3ZpZGVkVHJpbW1lZDtcblxuICBjb25zdCBzZWdtZW50cyA9IG5hbWUuc3BsaXQoU0VHTUVOVF9TUExJVCkuZmlsdGVyKEJvb2xlYW4pO1xuICAvLyBEcm9wIGtub3duIHZlbmRvci9uYW1lc3BhY2UgcHJlZml4ZXMgb25seSB3aGlsZSB0aGV5IGxlYWQgdGhlIGxpc3QuXG4gIGxldCBzdGFydCA9IDA7XG4gIHdoaWxlIChzdGFydCA8IHNlZ21lbnRzLmxlbmd0aCkge1xuICAgIGNvbnN0IGxvd2VyID0gc2VnbWVudHNbc3RhcnRdIS50b0xvd2VyQ2FzZSgpO1xuICAgIGlmICghU1RSSVBfTEVBRElOR19UT0tFTlMuaGFzKGxvd2VyKSkgYnJlYWs7XG4gICAgLy8gQ29uc3VtZSB0aGUgbmFtZXNwYWNlJ3Mgc2Vjb25kIHRva2VuIHRvbyAoZS5nLiB0aGUgYGFpYCBvZiBgc3lzdGVtLmFpYCkuXG4gICAgaWYgKE5BTUVTUEFDRV9TRUNPTkRfVE9LRU5TW2xvd2VyXSA9PT0gc2VnbWVudHNbc3RhcnQgKyAxXT8udG9Mb3dlckNhc2UoKSkge1xuICAgICAgc3RhcnQgKz0gMTtcbiAgICB9XG4gICAgc3RhcnQgKz0gMTtcbiAgfVxuICBjb25zdCBrZXB0ID0gc2VnbWVudHMuc2xpY2Uoc3RhcnQpO1xuICBjb25zdCBzb3VyY2UgPSBrZXB0Lmxlbmd0aCA+IDAgPyBrZXB0IDogc2VnbWVudHM7XG5cbiAgY29uc3QgcGllY2VzOiBzdHJpbmdbXSA9IFtdO1xuICBsZXQgdmVyc2lvblJ1bjogc3RyaW5nW10gPSBbXTtcbiAgY29uc3QgZmx1c2hWZXJzaW9uID0gKCkgPT4ge1xuICAgIGlmICh2ZXJzaW9uUnVuLmxlbmd0aCA+IDApIHtcbiAgICAgIHBpZWNlcy5wdXNoKHZlcnNpb25SdW4uam9pbihcIi5cIikpO1xuICAgICAgdmVyc2lvblJ1biA9IFtdO1xuICAgIH1cbiAgfTtcbiAgZm9yIChjb25zdCBzZWdtZW50IG9mIHNvdXJjZSkge1xuICAgIGlmICgvXlxcZCskLy50ZXN0KHNlZ21lbnQpKSB7XG4gICAgICB2ZXJzaW9uUnVuLnB1c2goc2VnbWVudCk7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG4gICAgZmx1c2hWZXJzaW9uKCk7XG4gICAgcGllY2VzLnB1c2gocmVuZGVyU2VnbWVudChzZWdtZW50KSk7XG4gIH1cbiAgZmx1c2hWZXJzaW9uKCk7XG5cbiAgcmV0dXJuIHBpZWNlcy5qb2luKFwiIFwiKSB8fCBuYW1lLnRyaW0oKTtcbn1cblxuLyoqIFRpdGxlLWNhc2Ugb25lIG5hbWUgc2VnbWVudCwgdXBwZXJjYXNpbmcgYWNyb255bXMgYW5kIGdsdWluZyBzaXplIHVuaXRzLiAqL1xuZnVuY3Rpb24gcmVuZGVyU2VnbWVudChzZWdtZW50OiBzdHJpbmcpOiBzdHJpbmcge1xuICBjb25zdCB0b2tlbnMgPSBbLi4uc3RyaW5nLnRva2VuaXplV2l0aE9wdGlvbnMoeyBjYXBpdGFsaXplOiB0cnVlIH0sIHNlZ21lbnQpXTtcbiAgY29uc3Qgb3V0OiBzdHJpbmdbXSA9IFtdO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IHRva2Vucy5sZW5ndGg7IGkgKz0gMSkge1xuICAgIGNvbnN0IHRva2VuID0gdG9rZW5zW2ldITtcbiAgICBjb25zdCBsb3dlciA9IHRva2VuLnRvTG93ZXJDYXNlKCk7XG4gICAgLy8gR2x1ZSBhIHNpemUgdW5pdCBvbnRvIHRoZSBwcmVjZWRpbmcgbnVtYmVyOiBgMTIwYCArIGBiYCAtPiBgMTIwQmAuXG4gICAgaWYgKFNJWkVfVU5JVFMuaGFzKGxvd2VyKSAmJiBvdXQubGVuZ3RoID4gMCAmJiAvXlxcZCskLy50ZXN0KG91dFtvdXQubGVuZ3RoIC0gMV0hKSkge1xuICAgICAgb3V0W291dC5sZW5ndGggLSAxXSA9IGAke291dFtvdXQubGVuZ3RoIC0gMV19JHtsb3dlci50b1VwcGVyQ2FzZSgpfWA7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG4gICAgb3V0LnB1c2goQUNST05ZTVMuaGFzKGxvd2VyKSA/IGxvd2VyLnRvVXBwZXJDYXNlKCkgOiB0b2tlbik7XG4gIH1cbiAgcmV0dXJuIG91dC5qb2luKFwiIFwiKTtcbn1cbiJdfQ==