@dbx-tools/shared-model 0.3.44 → 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 +14 -0
- package/lib/index.js +14 -0
- package/lib/src/classify.d.ts +111 -0
- package/lib/src/classify.js +261 -0
- package/lib/src/display.d.ts +19 -0
- package/lib/src/display.js +106 -0
- package/lib/src/model.d.ts +107 -0
- package/lib/src/model.js +123 -0
- package/lib/src/openai-chat.d.ts +108 -0
- package/lib/src/openai-chat.js +87 -0
- package/lib/src/openai-responses.d.ts +175 -0
- package/lib/src/openai-responses.js +686 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/package.json +10 -6
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==
|