@arizeai/phoenix-cli 1.5.3 → 1.6.1
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/build/pxi/App.d.ts +22 -0
- package/build/pxi/App.d.ts.map +1 -0
- package/build/pxi/App.js +328 -0
- package/build/pxi/App.js.map +1 -0
- package/build/pxi/client.d.ts +77 -0
- package/build/pxi/client.d.ts.map +1 -0
- package/build/pxi/client.js +170 -0
- package/build/pxi/client.js.map +1 -0
- package/build/pxi/commands.d.ts +37 -0
- package/build/pxi/commands.d.ts.map +1 -0
- package/build/pxi/commands.js +61 -0
- package/build/pxi/commands.js.map +1 -0
- package/build/pxi/index.d.ts +14 -0
- package/build/pxi/index.d.ts.map +1 -0
- package/build/pxi/index.js +36 -0
- package/build/pxi/index.js.map +1 -0
- package/build/pxi/inkMarkdown.d.ts +17 -0
- package/build/pxi/inkMarkdown.d.ts.map +1 -0
- package/build/pxi/inkMarkdown.js +22 -0
- package/build/pxi/inkMarkdown.js.map +1 -0
- package/build/pxi/markdown.d.ts +24 -0
- package/build/pxi/markdown.d.ts.map +1 -0
- package/build/pxi/markdown.js +115 -0
- package/build/pxi/markdown.js.map +1 -0
- package/build/pxi/options.d.ts +64 -0
- package/build/pxi/options.d.ts.map +1 -0
- package/build/pxi/options.js +148 -0
- package/build/pxi/options.js.map +1 -0
- package/build/pxi/preflight.d.ts +88 -0
- package/build/pxi/preflight.d.ts.map +1 -0
- package/build/pxi/preflight.js +255 -0
- package/build/pxi/preflight.js.map +1 -0
- package/build/pxi/tokenUsage.d.ts +35 -0
- package/build/pxi/tokenUsage.d.ts.map +1 -0
- package/build/pxi/tokenUsage.js +62 -0
- package/build/pxi/tokenUsage.js.map +1 -0
- package/build/pxi/toolProgress.d.ts +37 -0
- package/build/pxi/toolProgress.d.ts.map +1 -0
- package/build/pxi/toolProgress.js +71 -0
- package/build/pxi/toolProgress.js.map +1 -0
- package/build/pxi/types.d.ts +123 -0
- package/build/pxi/types.d.ts.map +1 -0
- package/build/pxi/types.js +2 -0
- package/build/pxi/types.js.map +1 -0
- package/package.json +14 -5
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { PhoenixConfig } from "../config.js";
|
|
2
|
+
import type { ModelSelection, PxiRuntimeOptions } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Pre-launch validation of the selected model.
|
|
5
|
+
*
|
|
6
|
+
* Before the chat UI opens, PXI asks the Phoenix server which providers and
|
|
7
|
+
* models are actually installed and credentialed, then checks the user's
|
|
8
|
+
* `--provider`/`--model` selection against that catalog. Catching a bad or
|
|
9
|
+
* unconfigured model here turns what would be a cryptic mid-stream failure into
|
|
10
|
+
* a clear, actionable startup error. The whole check can be skipped with
|
|
11
|
+
* `--skip-model-preflight`.
|
|
12
|
+
*/
|
|
13
|
+
/** GraphQL query fetching the server's provider catalog, credential state, and known models. */
|
|
14
|
+
export declare const PXI_MODEL_PREFLIGHT_QUERY = "\n query PxiModelPreflightQuery {\n modelProviders {\n key\n name\n dependenciesInstalled\n credentialsSet\n credentialRequirements {\n envVarName\n isRequired\n }\n }\n playgroundModels {\n providerKey\n name\n }\n generativeModelCustomProviders(first: 50) {\n edges {\n node {\n id\n name\n sdk\n modelNames\n }\n }\n }\n }\n";
|
|
15
|
+
type PxiModelProvider = {
|
|
16
|
+
key: string;
|
|
17
|
+
name: string;
|
|
18
|
+
dependenciesInstalled: boolean;
|
|
19
|
+
credentialsSet: boolean;
|
|
20
|
+
credentialRequirements: Array<{
|
|
21
|
+
envVarName: string;
|
|
22
|
+
isRequired: boolean;
|
|
23
|
+
}>;
|
|
24
|
+
};
|
|
25
|
+
type PxiPlaygroundModel = {
|
|
26
|
+
providerKey: string;
|
|
27
|
+
name: string;
|
|
28
|
+
};
|
|
29
|
+
type PxiCustomProvider = {
|
|
30
|
+
id: string;
|
|
31
|
+
name: string;
|
|
32
|
+
sdk: string;
|
|
33
|
+
modelNames: string[];
|
|
34
|
+
};
|
|
35
|
+
type PxiModelPreflightData = {
|
|
36
|
+
modelProviders: PxiModelProvider[];
|
|
37
|
+
playgroundModels: PxiPlaygroundModel[];
|
|
38
|
+
generativeModelCustomProviders: {
|
|
39
|
+
edges: Array<{
|
|
40
|
+
node: PxiCustomProvider;
|
|
41
|
+
}>;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Fetch the provider/model catalog from Phoenix via GraphQL. Requires a
|
|
46
|
+
* configured endpoint and turns non-2xx responses into errors that include the
|
|
47
|
+
* HTTP status and any response body. `fetchImpl` is injectable for testing.
|
|
48
|
+
*/
|
|
49
|
+
export declare function fetchPxiModelPreflight({ config, fetchImpl, }: {
|
|
50
|
+
config: PhoenixConfig;
|
|
51
|
+
fetchImpl?: typeof globalThis.fetch;
|
|
52
|
+
}): Promise<PxiModelPreflightData>;
|
|
53
|
+
/**
|
|
54
|
+
* Check a model selection against the fetched catalog, throwing
|
|
55
|
+
* {@link InvalidArgumentError} with a helpful message on the first problem.
|
|
56
|
+
*
|
|
57
|
+
* For custom providers: the provider id must exist, and the model must be one of
|
|
58
|
+
* its configured names (when it advertises any). For built-in providers: the
|
|
59
|
+
* provider must be available, have its dependencies installed and credentials
|
|
60
|
+
* set, and — if the server publishes a model catalog for it — the model must be
|
|
61
|
+
* in that catalog. A provider with no published catalog accepts any model name.
|
|
62
|
+
*/
|
|
63
|
+
export declare function validatePxiModelSelection({ data, modelSelection, }: {
|
|
64
|
+
data: PxiModelPreflightData;
|
|
65
|
+
modelSelection: ModelSelection;
|
|
66
|
+
}): void;
|
|
67
|
+
/**
|
|
68
|
+
* Run the full preflight for a session: fetch the catalog and validate the
|
|
69
|
+
* selected model, unless `--skip-model-preflight` was passed. This is the single
|
|
70
|
+
* call the entry point makes before rendering the UI.
|
|
71
|
+
*/
|
|
72
|
+
export declare function runPxiModelPreflight({ options, fetchImpl, }: {
|
|
73
|
+
options: PxiRuntimeOptions;
|
|
74
|
+
fetchImpl?: typeof globalThis.fetch;
|
|
75
|
+
}): Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Wrap an error thrown while talking to PXI into a single message that names the
|
|
78
|
+
* model and appends a tailored next step — pointing custom providers at their
|
|
79
|
+
* Phoenix settings and built-in providers at credential configuration or
|
|
80
|
+
* choosing a different model. Used for failures that surface after the preflight
|
|
81
|
+
* has already passed.
|
|
82
|
+
*/
|
|
83
|
+
export declare function formatPxiRuntimeError({ error, modelSelection, }: {
|
|
84
|
+
error: unknown;
|
|
85
|
+
modelSelection: ModelSelection;
|
|
86
|
+
}): Error;
|
|
87
|
+
export {};
|
|
88
|
+
//# sourceMappingURL=preflight.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preflight.d.ts","sourceRoot":"","sources":["../../src/pxi/preflight.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjE;;;;;;;;;GASG;AAEH,gGAAgG;AAChG,eAAO,MAAM,yBAAyB,kdA2BrC,CAAC;AAEF,KAAK,gBAAgB,GAAG;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB,EAAE,OAAO,CAAC;IAC/B,cAAc,EAAE,OAAO,CAAC;IACxB,sBAAsB,EAAE,KAAK,CAAC;QAC5B,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,OAAO,CAAC;KACrB,CAAC,CAAC;CACJ,CAAC;AAEF,KAAK,kBAAkB,GAAG;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,KAAK,iBAAiB,GAAG;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC;AAEF,KAAK,qBAAqB,GAAG;IAC3B,cAAc,EAAE,gBAAgB,EAAE,CAAC;IACnC,gBAAgB,EAAE,kBAAkB,EAAE,CAAC;IACvC,8BAA8B,EAAE;QAC9B,KAAK,EAAE,KAAK,CAAC;YACX,IAAI,EAAE,iBAAiB,CAAC;SACzB,CAAC,CAAC;KACJ,CAAC;CACH,CAAC;AAoKF;;;;GAIG;AACH,wBAAsB,sBAAsB,CAAC,EAC3C,MAAM,EACN,SAA4B,GAC7B,EAAE;IACD,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACrC,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAqCjC;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CAAC,EACxC,IAAI,EACJ,cAAc,GACf,EAAE;IACD,IAAI,EAAE,qBAAqB,CAAC;IAC5B,cAAc,EAAE,cAAc,CAAC;CAChC,GAAG,IAAI,CA4DP;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,EACzC,OAAO,EACP,SAA4B,GAC7B,EAAE;IACD,OAAO,EAAE,iBAAiB,CAAC;IAC3B,SAAS,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACrC,GAAG,OAAO,CAAC,IAAI,CAAC,CAYhB;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,KAAK,EACL,cAAc,GACf,EAAE;IACD,KAAK,EAAE,OAAO,CAAC;IACf,cAAc,EAAE,cAAc,CAAC;CAChC,GAAG,KAAK,CASR"}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { buildGraphqlRequest } from "../commands/api.js";
|
|
2
|
+
import { InvalidArgumentError } from "../exitCodes.js";
|
|
3
|
+
/**
|
|
4
|
+
* Pre-launch validation of the selected model.
|
|
5
|
+
*
|
|
6
|
+
* Before the chat UI opens, PXI asks the Phoenix server which providers and
|
|
7
|
+
* models are actually installed and credentialed, then checks the user's
|
|
8
|
+
* `--provider`/`--model` selection against that catalog. Catching a bad or
|
|
9
|
+
* unconfigured model here turns what would be a cryptic mid-stream failure into
|
|
10
|
+
* a clear, actionable startup error. The whole check can be skipped with
|
|
11
|
+
* `--skip-model-preflight`.
|
|
12
|
+
*/
|
|
13
|
+
/** GraphQL query fetching the server's provider catalog, credential state, and known models. */
|
|
14
|
+
export const PXI_MODEL_PREFLIGHT_QUERY = /* GraphQL */ `
|
|
15
|
+
query PxiModelPreflightQuery {
|
|
16
|
+
modelProviders {
|
|
17
|
+
key
|
|
18
|
+
name
|
|
19
|
+
dependenciesInstalled
|
|
20
|
+
credentialsSet
|
|
21
|
+
credentialRequirements {
|
|
22
|
+
envVarName
|
|
23
|
+
isRequired
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
playgroundModels {
|
|
27
|
+
providerKey
|
|
28
|
+
name
|
|
29
|
+
}
|
|
30
|
+
generativeModelCustomProviders(first: 50) {
|
|
31
|
+
edges {
|
|
32
|
+
node {
|
|
33
|
+
id
|
|
34
|
+
name
|
|
35
|
+
sdk
|
|
36
|
+
modelNames
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
`;
|
|
42
|
+
/**
|
|
43
|
+
* Render a list of values for an error message, capping it at `limit` and
|
|
44
|
+
* summarizing the overflow as "…, and N more" so messages stay readable when a
|
|
45
|
+
* provider exposes many models.
|
|
46
|
+
*/
|
|
47
|
+
function formatList({ values, limit = 8, }) {
|
|
48
|
+
if (values.length === 0) {
|
|
49
|
+
return "none";
|
|
50
|
+
}
|
|
51
|
+
const visibleValues = values.slice(0, limit);
|
|
52
|
+
const remainingCount = values.length - visibleValues.length;
|
|
53
|
+
if (remainingCount <= 0) {
|
|
54
|
+
return visibleValues.join(", ");
|
|
55
|
+
}
|
|
56
|
+
return `${visibleValues.join(", ")}, and ${remainingCount} more`;
|
|
57
|
+
}
|
|
58
|
+
function getModelLabel({ modelSelection, }) {
|
|
59
|
+
if (modelSelection.providerType === "custom") {
|
|
60
|
+
return `custom:${modelSelection.providerId}/${modelSelection.modelName}`;
|
|
61
|
+
}
|
|
62
|
+
return `${modelSelection.provider}/${modelSelection.modelName}`;
|
|
63
|
+
}
|
|
64
|
+
function getErrorMessage({ error }) {
|
|
65
|
+
return error instanceof Error ? error.message : String(error);
|
|
66
|
+
}
|
|
67
|
+
function getErrorCauseMessage({ error }) {
|
|
68
|
+
if (!(error instanceof Error) || !("cause" in error)) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
const cause = error.cause;
|
|
72
|
+
if (!cause) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
return getErrorMessage({ error: cause });
|
|
76
|
+
}
|
|
77
|
+
function formatEndpointPreflightFailure({ error, endpoint, requestUrl, }) {
|
|
78
|
+
const causeMessage = getErrorCauseMessage({ error });
|
|
79
|
+
const causeLine = causeMessage ? `\nCause: ${causeMessage}` : "";
|
|
80
|
+
return [
|
|
81
|
+
"Could not reach Phoenix during PXI startup preflight.",
|
|
82
|
+
"",
|
|
83
|
+
`Endpoint: ${endpoint}`,
|
|
84
|
+
`Request: ${requestUrl}`,
|
|
85
|
+
`Network error: ${getErrorMessage({ error })}${causeLine}`,
|
|
86
|
+
"",
|
|
87
|
+
"How to fix:",
|
|
88
|
+
" 1. Start Phoenix and confirm the server is listening.",
|
|
89
|
+
" 2. If Phoenix is running at a different URL, pass --endpoint <url> or set PHOENIX_HOST.",
|
|
90
|
+
" 3. For remote endpoints, check VPN, proxy, firewall, and DNS settings.",
|
|
91
|
+
" 4. To skip only model validation, pass --skip-model-preflight.",
|
|
92
|
+
].join("\n");
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Compose the "missing credentials" error for a provider, listing the required
|
|
96
|
+
* environment variables (falling back to all known ones if none are flagged
|
|
97
|
+
* required) and clarifying that these are server-side provider credentials, not
|
|
98
|
+
* the PXI CLI `--api-key`.
|
|
99
|
+
*/
|
|
100
|
+
function buildServerCredentialMessage({ provider, }) {
|
|
101
|
+
const requiredEnvVars = provider.credentialRequirements
|
|
102
|
+
.filter((requirement) => requirement.isRequired)
|
|
103
|
+
.map((requirement) => requirement.envVarName);
|
|
104
|
+
const envVars = requiredEnvVars.length > 0
|
|
105
|
+
? requiredEnvVars
|
|
106
|
+
: provider.credentialRequirements.map((requirement) => requirement.envVarName);
|
|
107
|
+
return [
|
|
108
|
+
`Missing credentials for ${provider.name} (${provider.key}).`,
|
|
109
|
+
`Required server credential variables/secrets: ${formatList({
|
|
110
|
+
values: envVars,
|
|
111
|
+
})}.`,
|
|
112
|
+
"Configure credentials in Phoenix Settings > AI Providers, or set the required environment variables on the Phoenix server.",
|
|
113
|
+
"These are Phoenix server-side provider credentials, not the PXI CLI --api-key.",
|
|
114
|
+
].join(" ");
|
|
115
|
+
}
|
|
116
|
+
function getCustomProviders({ data, }) {
|
|
117
|
+
return data.generativeModelCustomProviders.edges.map((edge) => edge.node);
|
|
118
|
+
}
|
|
119
|
+
async function readResponseText({ response, }) {
|
|
120
|
+
try {
|
|
121
|
+
return await response.text();
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return "";
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Unwrap a GraphQL response into its `data`, throwing a descriptive error if the
|
|
129
|
+
* server returned GraphQL errors or no data at all. Error messages point at
|
|
130
|
+
* `--skip-model-preflight` as the escape hatch.
|
|
131
|
+
*/
|
|
132
|
+
function assertPreflightData({ payload, }) {
|
|
133
|
+
const errors = payload.errors
|
|
134
|
+
?.map((error) => error.message)
|
|
135
|
+
.filter((message) => Boolean(message));
|
|
136
|
+
if (errors && errors.length > 0) {
|
|
137
|
+
throw new Error(`Could not validate PXI model selection because Phoenix returned GraphQL errors: ${errors.join("; ")}. Use --skip-model-preflight to bypass this startup check.`);
|
|
138
|
+
}
|
|
139
|
+
if (!payload.data) {
|
|
140
|
+
throw new Error("Could not validate PXI model selection because Phoenix returned no GraphQL data. Use --skip-model-preflight to bypass this startup check.");
|
|
141
|
+
}
|
|
142
|
+
return payload.data;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Fetch the provider/model catalog from Phoenix via GraphQL. Requires a
|
|
146
|
+
* configured endpoint and turns non-2xx responses into errors that include the
|
|
147
|
+
* HTTP status and any response body. `fetchImpl` is injectable for testing.
|
|
148
|
+
*/
|
|
149
|
+
export async function fetchPxiModelPreflight({ config, fetchImpl = globalThis.fetch, }) {
|
|
150
|
+
if (!config.endpoint) {
|
|
151
|
+
throw new InvalidArgumentError("Phoenix endpoint not configured. Set PHOENIX_HOST or pass --endpoint.");
|
|
152
|
+
}
|
|
153
|
+
const request = buildGraphqlRequest({
|
|
154
|
+
query: PXI_MODEL_PREFLIGHT_QUERY,
|
|
155
|
+
config,
|
|
156
|
+
});
|
|
157
|
+
let response;
|
|
158
|
+
try {
|
|
159
|
+
response = await fetchImpl(request.url, {
|
|
160
|
+
method: request.method,
|
|
161
|
+
headers: request.headers,
|
|
162
|
+
body: request.body,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
throw new TypeError(formatEndpointPreflightFailure({
|
|
167
|
+
error,
|
|
168
|
+
endpoint: config.endpoint,
|
|
169
|
+
requestUrl: request.url,
|
|
170
|
+
}), { cause: error });
|
|
171
|
+
}
|
|
172
|
+
if (!response.ok) {
|
|
173
|
+
const detail = await readResponseText({ response });
|
|
174
|
+
const detailText = detail ? `: ${detail}` : "";
|
|
175
|
+
throw new Error(`Could not validate PXI model selection: HTTP ${response.status} ${response.statusText} from ${request.url}${detailText}. Use --skip-model-preflight to bypass this startup check.`);
|
|
176
|
+
}
|
|
177
|
+
const payload = (await response.json());
|
|
178
|
+
return assertPreflightData({ payload });
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Check a model selection against the fetched catalog, throwing
|
|
182
|
+
* {@link InvalidArgumentError} with a helpful message on the first problem.
|
|
183
|
+
*
|
|
184
|
+
* For custom providers: the provider id must exist, and the model must be one of
|
|
185
|
+
* its configured names (when it advertises any). For built-in providers: the
|
|
186
|
+
* provider must be available, have its dependencies installed and credentials
|
|
187
|
+
* set, and — if the server publishes a model catalog for it — the model must be
|
|
188
|
+
* in that catalog. A provider with no published catalog accepts any model name.
|
|
189
|
+
*/
|
|
190
|
+
export function validatePxiModelSelection({ data, modelSelection, }) {
|
|
191
|
+
if (modelSelection.providerType === "custom") {
|
|
192
|
+
const customProviders = getCustomProviders({ data });
|
|
193
|
+
const provider = customProviders.find((candidate) => candidate.id === modelSelection.providerId);
|
|
194
|
+
if (!provider) {
|
|
195
|
+
throw new InvalidArgumentError(`Custom provider ${modelSelection.providerId} was not found on this Phoenix server. Configure it in Phoenix Settings > AI Providers or pass --skip-model-preflight. Available custom provider IDs: ${formatList({ values: customProviders.map((candidate) => candidate.id) })}.`);
|
|
196
|
+
}
|
|
197
|
+
if (provider.modelNames.length > 0 &&
|
|
198
|
+
!provider.modelNames.includes(modelSelection.modelName)) {
|
|
199
|
+
throw new InvalidArgumentError(`Invalid model for custom provider ${provider.name} (${provider.id}): ${modelSelection.modelName}. Configured model names: ${formatList({ values: provider.modelNames })}.`);
|
|
200
|
+
}
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const provider = data.modelProviders.find((candidate) => candidate.key === modelSelection.provider);
|
|
204
|
+
const availableProviderKeys = data.modelProviders.map((candidate) => candidate.key);
|
|
205
|
+
if (!provider) {
|
|
206
|
+
throw new InvalidArgumentError(`Provider ${modelSelection.provider} is not available on this Phoenix server. Available providers: ${formatList({ values: availableProviderKeys })}.`);
|
|
207
|
+
}
|
|
208
|
+
if (!provider.dependenciesInstalled) {
|
|
209
|
+
throw new InvalidArgumentError(`${provider.name} (${provider.key}) is unavailable because the Phoenix server does not have that provider installed. Install the provider dependencies on the Phoenix server or choose another provider.`);
|
|
210
|
+
}
|
|
211
|
+
if (provider.credentialRequirements.length > 0 && !provider.credentialsSet) {
|
|
212
|
+
throw new InvalidArgumentError(buildServerCredentialMessage({ provider }));
|
|
213
|
+
}
|
|
214
|
+
const providerModels = data.playgroundModels
|
|
215
|
+
.filter((model) => model.providerKey === modelSelection.provider)
|
|
216
|
+
.map((model) => model.name);
|
|
217
|
+
const hasProviderCatalog = providerModels.length > 0;
|
|
218
|
+
const isKnownModel = providerModels.includes(modelSelection.modelName);
|
|
219
|
+
if (hasProviderCatalog && !isKnownModel) {
|
|
220
|
+
throw new InvalidArgumentError(`Invalid model for ${provider.name} (${provider.key}): ${modelSelection.modelName}. Available models for this provider include: ${formatList({ values: providerModels })}.`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Run the full preflight for a session: fetch the catalog and validate the
|
|
225
|
+
* selected model, unless `--skip-model-preflight` was passed. This is the single
|
|
226
|
+
* call the entry point makes before rendering the UI.
|
|
227
|
+
*/
|
|
228
|
+
export async function runPxiModelPreflight({ options, fetchImpl = globalThis.fetch, }) {
|
|
229
|
+
if (options.skipModelPreflight) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const data = await fetchPxiModelPreflight({
|
|
233
|
+
config: options.config,
|
|
234
|
+
fetchImpl,
|
|
235
|
+
});
|
|
236
|
+
validatePxiModelSelection({
|
|
237
|
+
data,
|
|
238
|
+
modelSelection: options.modelSelection,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Wrap an error thrown while talking to PXI into a single message that names the
|
|
243
|
+
* model and appends a tailored next step — pointing custom providers at their
|
|
244
|
+
* Phoenix settings and built-in providers at credential configuration or
|
|
245
|
+
* choosing a different model. Used for failures that surface after the preflight
|
|
246
|
+
* has already passed.
|
|
247
|
+
*/
|
|
248
|
+
export function formatPxiRuntimeError({ error, modelSelection, }) {
|
|
249
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
250
|
+
const nextAction = modelSelection.providerType === "custom"
|
|
251
|
+
? "Check the custom provider configuration in Phoenix Settings > AI Providers."
|
|
252
|
+
: `Configure ${modelSelection.provider} credentials in Phoenix Settings > AI Providers, set the required environment variables on the Phoenix server, or choose a different model.`;
|
|
253
|
+
return new Error(`PXI request failed for ${getModelLabel({ modelSelection })}: ${message} ${nextAction}`);
|
|
254
|
+
}
|
|
255
|
+
//# sourceMappingURL=preflight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preflight.js","sourceRoot":"","sources":["../../src/pxi/preflight.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { AssistantMessageMetadata, PxiMessage } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Token-usage helpers for the PXI status line.
|
|
4
|
+
*
|
|
5
|
+
* The Phoenix server attaches {@link AssistantMessageMetadata} usage to each
|
|
6
|
+
* assistant turn. These helpers pull the most recent usage off the transcript
|
|
7
|
+
* and format it for the bottom-right status line, mirroring how the web UI's
|
|
8
|
+
* `ChatSessionUsage` surfaces the latest token count plus any cache activity so
|
|
9
|
+
* the user can gauge how much of the context window is in play.
|
|
10
|
+
*/
|
|
11
|
+
/** The usage metadata reported for a single assistant turn. */
|
|
12
|
+
export type PxiUsage = NonNullable<AssistantMessageMetadata["usage"]>;
|
|
13
|
+
/**
|
|
14
|
+
* Return the usage reported by the most recent assistant message that carries
|
|
15
|
+
* any, scanning newest-first. Returns `null` when no assistant turn reported
|
|
16
|
+
* usage (tracing/usage reporting can be disabled server-side).
|
|
17
|
+
*/
|
|
18
|
+
export declare function getLatestAssistantUsage(messages: PxiMessage[]): PxiUsage | null;
|
|
19
|
+
/** Format a token count with thousands separators (e.g. `12,345`). */
|
|
20
|
+
export declare function formatTokenCount(value: number): string;
|
|
21
|
+
/**
|
|
22
|
+
* Build the cache-activity portion of the status line from a turn's prompt
|
|
23
|
+
* details, matching the web UI: always show cache reads, and only show cache
|
|
24
|
+
* writes when non-zero (OpenAI never reports them). Returns `null` when there
|
|
25
|
+
* is no cache activity to surface.
|
|
26
|
+
*/
|
|
27
|
+
export declare function formatCacheSummary(promptDetails: PxiUsage["promptDetails"]): string | null;
|
|
28
|
+
/**
|
|
29
|
+
* Compose the full bottom-right status line for a turn's usage — the headline
|
|
30
|
+
* total token count, optionally preceded by a cache-activity summary. Returns
|
|
31
|
+
* `null` when there is no usage to show, so the caller can omit the line
|
|
32
|
+
* entirely.
|
|
33
|
+
*/
|
|
34
|
+
export declare function formatTokenUsageLine(usage: PxiUsage | null): string | null;
|
|
35
|
+
//# sourceMappingURL=tokenUsage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenUsage.d.ts","sourceRoot":"","sources":["../../src/pxi/tokenUsage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAEpE;;;;;;;;GAQG;AAEH,+DAA+D;AAC/D,MAAM,MAAM,QAAQ,GAAG,WAAW,CAAC,wBAAwB,CAAC,OAAO,CAAC,CAAC,CAAC;AAEtE;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,UAAU,EAAE,GACrB,QAAQ,GAAG,IAAI,CAYjB;AAED,sEAAsE;AACtE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,aAAa,EAAE,QAAQ,CAAC,eAAe,CAAC,GACvC,MAAM,GAAG,IAAI,CAcf;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAW1E"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Return the usage reported by the most recent assistant message that carries
|
|
3
|
+
* any, scanning newest-first. Returns `null` when no assistant turn reported
|
|
4
|
+
* usage (tracing/usage reporting can be disabled server-side).
|
|
5
|
+
*/
|
|
6
|
+
export function getLatestAssistantUsage(messages) {
|
|
7
|
+
for (let index = messages.length - 1; index >= 0; index--) {
|
|
8
|
+
const message = messages[index];
|
|
9
|
+
if (message?.role !== "assistant") {
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
const usage = message.metadata?.usage;
|
|
13
|
+
if (usage != null) {
|
|
14
|
+
return usage;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
/** Format a token count with thousands separators (e.g. `12,345`). */
|
|
20
|
+
export function formatTokenCount(value) {
|
|
21
|
+
return value.toLocaleString("en-US");
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Build the cache-activity portion of the status line from a turn's prompt
|
|
25
|
+
* details, matching the web UI: always show cache reads, and only show cache
|
|
26
|
+
* writes when non-zero (OpenAI never reports them). Returns `null` when there
|
|
27
|
+
* is no cache activity to surface.
|
|
28
|
+
*/
|
|
29
|
+
export function formatCacheSummary(promptDetails) {
|
|
30
|
+
if (!promptDetails) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const cacheRead = promptDetails.cacheRead ?? 0;
|
|
34
|
+
const cacheWrite = promptDetails.cacheWrite ?? 0;
|
|
35
|
+
if (cacheRead <= 0 && cacheWrite <= 0) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
const parts = [`cache read ${formatTokenCount(cacheRead)}`];
|
|
39
|
+
if (cacheWrite > 0) {
|
|
40
|
+
parts.push(`cache write ${formatTokenCount(cacheWrite)}`);
|
|
41
|
+
}
|
|
42
|
+
return parts.join(" / ");
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Compose the full bottom-right status line for a turn's usage — the headline
|
|
46
|
+
* total token count, optionally preceded by a cache-activity summary. Returns
|
|
47
|
+
* `null` when there is no usage to show, so the caller can omit the line
|
|
48
|
+
* entirely.
|
|
49
|
+
*/
|
|
50
|
+
export function formatTokenUsageLine(usage) {
|
|
51
|
+
if (!usage) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const segments = [];
|
|
55
|
+
const cacheSummary = formatCacheSummary(usage.promptDetails);
|
|
56
|
+
if (cacheSummary) {
|
|
57
|
+
segments.push(cacheSummary);
|
|
58
|
+
}
|
|
59
|
+
segments.push(`${formatTokenCount(usage.tokens.total)} tokens`);
|
|
60
|
+
return segments.join(" · ");
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=tokenUsage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenUsage.js","sourceRoot":"","sources":["../../src/pxi/tokenUsage.ts"],"names":[],"mappings":"AAeA;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CACrC,QAAsB;IAEtB,KAAK,IAAI,KAAK,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;QAC1D,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;QAChC,IAAI,OAAO,EAAE,IAAI,KAAK,WAAW,EAAE,CAAC;YAClC,SAAS;QACX,CAAC;QACD,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC;QACtC,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;YAClB,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,OAAO,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;AACvC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAChC,aAAwC;IAExC,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,SAAS,GAAG,aAAa,CAAC,SAAS,IAAI,CAAC,CAAC;IAC/C,MAAM,UAAU,GAAG,aAAa,CAAC,UAAU,IAAI,CAAC,CAAC;IACjD,IAAI,SAAS,IAAI,CAAC,IAAI,UAAU,IAAI,CAAC,EAAE,CAAC;QACtC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,KAAK,GAAG,CAAC,cAAc,gBAAgB,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IAC5D,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;QACnB,KAAK,CAAC,IAAI,CAAC,eAAe,gBAAgB,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC3B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAsB;IACzD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,YAAY,GAAG,kBAAkB,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAC7D,IAAI,YAAY,EAAE,CAAC;QACjB,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC9B,CAAC;IACD,QAAQ,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAChE,OAAO,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { UIDataTypes, UIMessagePart, UITools } from "ai";
|
|
2
|
+
import type { PxiMessage } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Distills the AI SDK's tool-call message parts into a small, display-ready
|
|
5
|
+
* {@link ToolProgress} shape the UI can render directly. The SDK models a tool
|
|
6
|
+
* call as a sequence of parts moving through lifecycle states; this module reads
|
|
7
|
+
* the current state and produces a human-readable status line plus an optional
|
|
8
|
+
* one-line summary of the tool's input or output.
|
|
9
|
+
*/
|
|
10
|
+
/** Lifecycle state of a single tool call, mirroring the AI SDK's part states. */
|
|
11
|
+
export type ToolProgressState = "input-streaming" | "input-available" | "approval-requested" | "approval-responded" | "output-available" | "output-error" | "output-denied";
|
|
12
|
+
/** A display-ready summary of one tool call's current progress. */
|
|
13
|
+
export type ToolProgress = {
|
|
14
|
+
toolCallId: string;
|
|
15
|
+
toolName: string;
|
|
16
|
+
state: ToolProgressState;
|
|
17
|
+
statusText: string;
|
|
18
|
+
errorText?: string;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Collect the progress of every tool call across a list of messages, in order,
|
|
22
|
+
* skipping non-tool parts.
|
|
23
|
+
*/
|
|
24
|
+
export declare function getToolProgressFromMessages(messages: readonly PxiMessage[]): ToolProgress[];
|
|
25
|
+
/**
|
|
26
|
+
* Convert a single message part into a {@link ToolProgress}, or `null` if the
|
|
27
|
+
* part isn't a tool call. This is the core mapping that derives the tool name,
|
|
28
|
+
* status label, detail summary, and any error text from the raw SDK part.
|
|
29
|
+
*/
|
|
30
|
+
export declare function getToolProgressFromPart({ part, }: {
|
|
31
|
+
part: UIMessagePart<UIDataTypes, UITools>;
|
|
32
|
+
}): ToolProgress | null;
|
|
33
|
+
/** Concatenate the text of all text parts in a message, ignoring tool parts. */
|
|
34
|
+
export declare function getMessageText({ message }: {
|
|
35
|
+
message: PxiMessage;
|
|
36
|
+
}): string;
|
|
37
|
+
//# sourceMappingURL=toolProgress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolProgress.d.ts","sourceRoot":"","sources":["../../src/pxi/toolProgress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAE9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C;;;;;;GAMG;AAEH,iFAAiF;AACjF,MAAM,MAAM,iBAAiB,GACzB,iBAAiB,GACjB,iBAAiB,GACjB,oBAAoB,GACpB,oBAAoB,GACpB,kBAAkB,GAClB,cAAc,GACd,eAAe,CAAC;AAEpB,mEAAmE;AACnE,MAAM,MAAM,YAAY,GAAG;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,iBAAiB,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAoDF;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,SAAS,UAAU,EAAE,GAC9B,YAAY,EAAE,CAOhB;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,IAAI,GACL,EAAE;IACD,IAAI,EAAE,aAAa,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;CAC3C,GAAG,YAAY,GAAG,IAAI,CAatB;AAED,gFAAgF;AAChF,wBAAgB,cAAc,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,UAAU,CAAA;CAAE,GAAG,MAAM,CAK3E"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type guard recognizing the message parts that represent a tool call — both
|
|
3
|
+
* statically-typed (`tool-*`) and dynamic tools — so non-tool parts (like text)
|
|
4
|
+
* are skipped.
|
|
5
|
+
*/
|
|
6
|
+
function isPxiToolPart(part) {
|
|
7
|
+
return ("toolCallId" in part &&
|
|
8
|
+
"state" in part &&
|
|
9
|
+
(part.type === "dynamic-tool" || part.type.startsWith("tool-")));
|
|
10
|
+
}
|
|
11
|
+
function getToolName(part) {
|
|
12
|
+
return part.type === "dynamic-tool"
|
|
13
|
+
? (part.toolName ?? "dynamic-tool")
|
|
14
|
+
: part.type.replace(/^tool-/, "");
|
|
15
|
+
}
|
|
16
|
+
/** Map a lifecycle state to the short human-readable label shown to the user. */
|
|
17
|
+
function getStatusText(state) {
|
|
18
|
+
switch (state) {
|
|
19
|
+
case "input-streaming":
|
|
20
|
+
return "Preparing";
|
|
21
|
+
case "input-available":
|
|
22
|
+
return "Running";
|
|
23
|
+
case "approval-requested":
|
|
24
|
+
return "Awaiting approval";
|
|
25
|
+
case "approval-responded":
|
|
26
|
+
return "Approval recorded";
|
|
27
|
+
case "output-available":
|
|
28
|
+
return "Complete";
|
|
29
|
+
case "output-error":
|
|
30
|
+
return "Failed";
|
|
31
|
+
case "output-denied":
|
|
32
|
+
return "Denied";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Collect the progress of every tool call across a list of messages, in order,
|
|
37
|
+
* skipping non-tool parts.
|
|
38
|
+
*/
|
|
39
|
+
export function getToolProgressFromMessages(messages) {
|
|
40
|
+
return messages.flatMap((message) => message.parts.flatMap((part) => {
|
|
41
|
+
const toolProgress = getToolProgressFromPart({ part });
|
|
42
|
+
return toolProgress ? [toolProgress] : [];
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Convert a single message part into a {@link ToolProgress}, or `null` if the
|
|
47
|
+
* part isn't a tool call. This is the core mapping that derives the tool name,
|
|
48
|
+
* status label, detail summary, and any error text from the raw SDK part.
|
|
49
|
+
*/
|
|
50
|
+
export function getToolProgressFromPart({ part, }) {
|
|
51
|
+
if (!isPxiToolPart(part)) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const toolName = getToolName(part);
|
|
55
|
+
const state = part.state;
|
|
56
|
+
return {
|
|
57
|
+
toolCallId: part.toolCallId,
|
|
58
|
+
toolName,
|
|
59
|
+
state,
|
|
60
|
+
statusText: getStatusText(state),
|
|
61
|
+
errorText: "errorText" in part ? part.errorText : undefined,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/** Concatenate the text of all text parts in a message, ignoring tool parts. */
|
|
65
|
+
export function getMessageText({ message }) {
|
|
66
|
+
return message.parts
|
|
67
|
+
.filter((part) => part.type === "text")
|
|
68
|
+
.map((part) => part.text)
|
|
69
|
+
.join("");
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=toolProgress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolProgress.js","sourceRoot":"","sources":["../../src/pxi/toolProgress.ts"],"names":[],"mappings":"AAwCA;;;;GAIG;AACH,SAAS,aAAa,CACpB,IAAyC;IAEzC,OAAO,CACL,YAAY,IAAI,IAAI;QACpB,OAAO,IAAI,IAAI;QACf,CAAC,IAAI,CAAC,IAAI,KAAK,cAAc,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAChE,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,IAAiB;IACpC,OAAO,IAAI,CAAC,IAAI,KAAK,cAAc;QACjC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,IAAI,cAAc,CAAC;QACnC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AACtC,CAAC;AAED,iFAAiF;AACjF,SAAS,aAAa,CAAC,KAAwB;IAC7C,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,iBAAiB;YACpB,OAAO,WAAW,CAAC;QACrB,KAAK,iBAAiB;YACpB,OAAO,SAAS,CAAC;QACnB,KAAK,oBAAoB;YACvB,OAAO,mBAAmB,CAAC;QAC7B,KAAK,oBAAoB;YACvB,OAAO,mBAAmB,CAAC;QAC7B,KAAK,kBAAkB;YACrB,OAAO,UAAU,CAAC;QACpB,KAAK,cAAc;YACjB,OAAO,QAAQ,CAAC;QAClB,KAAK,eAAe;YAClB,OAAO,QAAQ,CAAC;IACpB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,2BAA2B,CACzC,QAA+B;IAE/B,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAClC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QAC7B,MAAM,YAAY,GAAG,uBAAuB,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QACvD,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5C,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,EACtC,IAAI,GAGL;IACC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IACnC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IACzB,OAAO;QACL,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,QAAQ;QACR,KAAK;QACL,UAAU,EAAE,aAAa,CAAC,KAAK,CAAC;QAChC,SAAS,EAAE,WAAW,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;KAC5D,CAAC;AACJ,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,cAAc,CAAC,EAAE,OAAO,EAA2B;IACjE,OAAO,OAAO,CAAC,KAAK;SACjB,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC;SACtC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;SACxB,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC"}
|