@sudobility/shapeshyft_engine 1.0.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.
Files changed (83) hide show
  1. package/CLAUDE.md +27 -0
  2. package/README.md +11 -0
  3. package/dist/config/providers.d.ts +111 -0
  4. package/dist/config/providers.d.ts.map +1 -0
  5. package/dist/config/providers.js +1535 -0
  6. package/dist/config/providers.js.map +1 -0
  7. package/dist/index.d.ts +27 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +27 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/lib/api-helper.d.ts +35 -0
  12. package/dist/lib/api-helper.d.ts.map +1 -0
  13. package/dist/lib/api-helper.js +123 -0
  14. package/dist/lib/api-helper.js.map +1 -0
  15. package/dist/lib/capability-validator.d.ts +36 -0
  16. package/dist/lib/capability-validator.d.ts.map +1 -0
  17. package/dist/lib/capability-validator.js +140 -0
  18. package/dist/lib/capability-validator.js.map +1 -0
  19. package/dist/lib/media-constants.d.ts +55 -0
  20. package/dist/lib/media-constants.d.ts.map +1 -0
  21. package/dist/lib/media-constants.js +166 -0
  22. package/dist/lib/media-constants.js.map +1 -0
  23. package/dist/lib/media-conversion.d.ts +36 -0
  24. package/dist/lib/media-conversion.d.ts.map +1 -0
  25. package/dist/lib/media-conversion.js +87 -0
  26. package/dist/lib/media-conversion.js.map +1 -0
  27. package/dist/lib/media-utils.d.ts +35 -0
  28. package/dist/lib/media-utils.d.ts.map +1 -0
  29. package/dist/lib/media-utils.js +173 -0
  30. package/dist/lib/media-utils.js.map +1 -0
  31. package/dist/lib/output-limit.d.ts +38 -0
  32. package/dist/lib/output-limit.d.ts.map +1 -0
  33. package/dist/lib/output-limit.js +54 -0
  34. package/dist/lib/output-limit.js.map +1 -0
  35. package/dist/lib/prompt-builder.d.ts +94 -0
  36. package/dist/lib/prompt-builder.d.ts.map +1 -0
  37. package/dist/lib/prompt-builder.js +408 -0
  38. package/dist/lib/prompt-builder.js.map +1 -0
  39. package/dist/lib/reserved-fields.d.ts +41 -0
  40. package/dist/lib/reserved-fields.d.ts.map +1 -0
  41. package/dist/lib/reserved-fields.js +47 -0
  42. package/dist/lib/reserved-fields.js.map +1 -0
  43. package/dist/services/llm/anthropic.d.ts +16 -0
  44. package/dist/services/llm/anthropic.d.ts.map +1 -0
  45. package/dist/services/llm/anthropic.js +154 -0
  46. package/dist/services/llm/anthropic.js.map +1 -0
  47. package/dist/services/llm/custom.d.ts +51 -0
  48. package/dist/services/llm/custom.d.ts.map +1 -0
  49. package/dist/services/llm/custom.js +419 -0
  50. package/dist/services/llm/custom.js.map +1 -0
  51. package/dist/services/llm/extract-json.d.ts +17 -0
  52. package/dist/services/llm/extract-json.d.ts.map +1 -0
  53. package/dist/services/llm/extract-json.js +60 -0
  54. package/dist/services/llm/extract-json.js.map +1 -0
  55. package/dist/services/llm/finish-reason.d.ts +21 -0
  56. package/dist/services/llm/finish-reason.d.ts.map +1 -0
  57. package/dist/services/llm/finish-reason.js +55 -0
  58. package/dist/services/llm/finish-reason.js.map +1 -0
  59. package/dist/services/llm/gemini.d.ts +26 -0
  60. package/dist/services/llm/gemini.d.ts.map +1 -0
  61. package/dist/services/llm/gemini.js +178 -0
  62. package/dist/services/llm/gemini.js.map +1 -0
  63. package/dist/services/llm/groq.d.ts +28 -0
  64. package/dist/services/llm/groq.d.ts.map +1 -0
  65. package/dist/services/llm/groq.js +237 -0
  66. package/dist/services/llm/groq.js.map +1 -0
  67. package/dist/services/llm/index.d.ts +19 -0
  68. package/dist/services/llm/index.d.ts.map +1 -0
  69. package/dist/services/llm/index.js +88 -0
  70. package/dist/services/llm/index.js.map +1 -0
  71. package/dist/services/llm/openai.d.ts +101 -0
  72. package/dist/services/llm/openai.d.ts.map +1 -0
  73. package/dist/services/llm/openai.js +561 -0
  74. package/dist/services/llm/openai.js.map +1 -0
  75. package/dist/services/llm/types.d.ts +110 -0
  76. package/dist/services/llm/types.d.ts.map +1 -0
  77. package/dist/services/llm/types.js +16 -0
  78. package/dist/services/llm/types.js.map +1 -0
  79. package/dist/types/index.d.ts +812 -0
  80. package/dist/types/index.d.ts.map +1 -0
  81. package/dist/types/index.js +366 -0
  82. package/dist/types/index.js.map +1 -0
  83. package/package.json +93 -0
@@ -0,0 +1,154 @@
1
+ /**
2
+ * @fileoverview Anthropic LLM provider
3
+ * @description Implements the ILLMProvider interface for Anthropic Claude models.
4
+ * Uses tool_use for structured output extraction. Supports image input
5
+ * via base64 and URL formats.
6
+ */
7
+ import Anthropic from "@anthropic-ai/sdk";
8
+ import { normalizeFinishReason } from "./finish-reason.js";
9
+ // Fallback when an endpoint doesn't pin a model. claude-sonnet-4-20250514 is
10
+ // deprecated (retires 2026-06-15); use a current, non-deprecated Sonnet-tier id.
11
+ // (Bump to "claude-sonnet-5" or "claude-opus-4-8" for the latest tier.)
12
+ const DEFAULT_MODEL = "claude-sonnet-4-6";
13
+ export class AnthropicProvider {
14
+ providerName = "anthropic";
15
+ client;
16
+ defaultModel;
17
+ constructor(config) {
18
+ if (!config.apiKey) {
19
+ throw new Error("Anthropic API key is required");
20
+ }
21
+ this.client = new Anthropic({ apiKey: config.apiKey });
22
+ this.defaultModel = config.model ?? DEFAULT_MODEL;
23
+ }
24
+ async generate(request) {
25
+ const model = request.model ?? this.defaultModel;
26
+ const startTime = Date.now();
27
+ // Use tool_use for structured output
28
+ const tools = [
29
+ {
30
+ name: "structured_response",
31
+ description: "Generate structured response matching the schema",
32
+ input_schema: request.outputSchema,
33
+ },
34
+ ];
35
+ // Build multimodal content blocks
36
+ const userContent = [];
37
+ // Add media blocks first (images only for Claude)
38
+ if (request.media?.length) {
39
+ for (const m of request.media) {
40
+ if (m.type === "image") {
41
+ if (m.format === "base64") {
42
+ userContent.push({
43
+ type: "image",
44
+ source: {
45
+ type: "base64",
46
+ media_type: m.mimeType,
47
+ data: m.data,
48
+ },
49
+ });
50
+ }
51
+ else if (m.format === "url") {
52
+ userContent.push({
53
+ type: "image",
54
+ source: {
55
+ type: "url",
56
+ url: m.data,
57
+ },
58
+ });
59
+ }
60
+ }
61
+ // Claude doesn't support audio/video input currently
62
+ }
63
+ }
64
+ // Add text prompt
65
+ userContent.push({ type: "text", text: request.prompt });
66
+ const response = await this.client.messages.create({
67
+ model,
68
+ max_tokens: request.maxTokens ?? 4096,
69
+ system: request.systemPrompt,
70
+ messages: [{ role: "user", content: userContent }],
71
+ tools,
72
+ tool_choice: { type: "tool", name: "structured_response" },
73
+ // `temperature`/`top_p` are rejected (400) on Opus 4.7+, Sonnet 5, and
74
+ // Fable 5. Only send it when a caller explicitly requests one; older
75
+ // models still accept it. Omitting it lets current models work.
76
+ ...(request.temperature !== undefined
77
+ ? { temperature: request.temperature }
78
+ : {}),
79
+ });
80
+ const latencyMs = Date.now() - startTime;
81
+ // Extract structured response from tool use
82
+ const toolUseBlock = response.content.find((block) => block.type === "tool_use");
83
+ if (!toolUseBlock || toolUseBlock.name !== "structured_response") {
84
+ throw new Error("Expected tool_use response from Anthropic");
85
+ }
86
+ const content = toolUseBlock.input;
87
+ const rawResponse = JSON.stringify(content);
88
+ return {
89
+ content,
90
+ rawResponse,
91
+ usage: {
92
+ promptTokens: response.usage.input_tokens,
93
+ completionTokens: response.usage.output_tokens,
94
+ totalTokens: response.usage.input_tokens + response.usage.output_tokens,
95
+ },
96
+ model: response.model,
97
+ provider: this.providerName,
98
+ latencyMs,
99
+ finishReason: normalizeFinishReason(response.stop_reason),
100
+ };
101
+ }
102
+ buildApiPayload(request) {
103
+ const model = request.model ?? this.defaultModel;
104
+ // Build multimodal content blocks
105
+ const userContent = [];
106
+ // Add media blocks first (images only for Claude)
107
+ if (request.media?.length) {
108
+ for (const m of request.media) {
109
+ if (m.type === "image") {
110
+ if (m.format === "base64") {
111
+ userContent.push({
112
+ type: "image",
113
+ source: {
114
+ type: "base64",
115
+ media_type: m.mimeType,
116
+ data: m.data,
117
+ },
118
+ });
119
+ }
120
+ else if (m.format === "url") {
121
+ userContent.push({
122
+ type: "image",
123
+ source: {
124
+ type: "url",
125
+ url: m.data,
126
+ },
127
+ });
128
+ }
129
+ }
130
+ }
131
+ }
132
+ // Add text prompt
133
+ userContent.push({ type: "text", text: request.prompt });
134
+ return {
135
+ model,
136
+ max_tokens: request.maxTokens ?? 4096,
137
+ system: request.systemPrompt,
138
+ messages: [{ role: "user", content: userContent }],
139
+ tools: [
140
+ {
141
+ name: "structured_response",
142
+ description: "Generate structured response matching the schema",
143
+ input_schema: request.outputSchema,
144
+ },
145
+ ],
146
+ tool_choice: { type: "tool", name: "structured_response" },
147
+ // See generate(): only include temperature when explicitly requested.
148
+ ...(request.temperature !== undefined
149
+ ? { temperature: request.temperature }
150
+ : {}),
151
+ };
152
+ }
153
+ }
154
+ //# sourceMappingURL=anthropic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../../src/services/llm/anthropic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ import type { ILLMProvider, LLMRequest, LLMResponse, ProviderConfig } from "./types.js";
2
+ /**
3
+ * Custom LLM Server provider that forwards requests to user's endpoint.
4
+ * Expects the endpoint to follow OpenAI-compatible format.
5
+ * Streams the response so the connection is never idle, which is what keeps
6
+ * LM Studio from closing a long generation at its 300s limit.
7
+ */
8
+ export declare class CustomLLMProvider implements ILLMProvider {
9
+ readonly providerName: "lm_studio";
10
+ private endpointUrl;
11
+ private timeout;
12
+ constructor(config: ProviderConfig);
13
+ generate(request: LLMRequest): Promise<LLMResponse>;
14
+ /**
15
+ * The provider's answer, whether it streamed or not.
16
+ *
17
+ * A stream is reassembled into the same object a non-streamed reply would
18
+ * have produced, so everything downstream — the stop-reason check, the JSON
19
+ * extraction, the usage — is unchanged and unaware. Servers that ignore
20
+ * `stream` and answer with plain JSON still work: the content type decides.
21
+ */
22
+ private readBody;
23
+ /**
24
+ * Parse response from custom endpoint - supports multiple formats
25
+ */
26
+ private parseResponse;
27
+ /**
28
+ * Parse JSON with recovery for common LLM output issues:
29
+ * - Unescaped double quotes inside string values
30
+ * - Literal newlines/tabs inside string values
31
+ * - Invalid escape sequences
32
+ */
33
+ private safeJsonParse;
34
+ /**
35
+ * Repair unescaped double quotes inside JSON string values.
36
+ * Uses a heuristic: a quote inside a string is structural (closing) if followed
37
+ * by JSON structural characters (:, }, ], ,) after optional whitespace.
38
+ * Otherwise it's an unescaped content quote and gets escaped.
39
+ */
40
+ private repairJsonQuotes;
41
+ /**
42
+ * Extract usage information from response
43
+ */
44
+ private extractUsage;
45
+ buildApiPayload(request: LLMRequest): Record<string, unknown>;
46
+ /**
47
+ * Get the actual endpoint URL (after auto-append)
48
+ */
49
+ getEndpointUrl(): string;
50
+ }
51
+ //# sourceMappingURL=custom.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom.d.ts","sourceRoot":"","sources":["../../../src/services/llm/custom.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,UAAU,EACV,WAAW,EACX,cAAc,EACf,MAAM,YAAY,CAAC;AAIpB;;;;;GAKG;AACH,qBAAa,iBAAkB,YAAW,YAAY;IACpD,QAAQ,CAAC,YAAY,EAAG,WAAW,CAAU;IAC7C,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,OAAO,CAAS;gBAEZ,MAAM,EAAE,cAAc;IA4B5B,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC;IAyEzD;;;;;;;OAOG;YACW,QAAQ;IAmEtB;;OAEG;IACH,OAAO,CAAC,aAAa;IA4ErB;;;;;OAKG;IACH,OAAO,CAAC,aAAa;IA0CrB;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAwDxB;;OAEG;IACH,OAAO,CAAC,YAAY;IAuBpB,eAAe,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAkE7D;;OAEG;IACH,cAAc,IAAI,MAAM;CAGzB"}
@@ -0,0 +1,419 @@
1
+ import { normalizeFinishReason } from "./finish-reason.js";
2
+ import { extractJson } from "./extract-json.js";
3
+ /**
4
+ * Custom LLM Server provider that forwards requests to user's endpoint.
5
+ * Expects the endpoint to follow OpenAI-compatible format.
6
+ * Streams the response so the connection is never idle, which is what keeps
7
+ * LM Studio from closing a long generation at its 300s limit.
8
+ */
9
+ export class CustomLLMProvider {
10
+ providerName = "lm_studio";
11
+ endpointUrl;
12
+ timeout;
13
+ constructor(config) {
14
+ if (!config.endpointUrl) {
15
+ throw new Error("LLM Server endpoint URL is required");
16
+ }
17
+ // Auto-append /chat/completions for OpenAI-compatible endpoints
18
+ let url = config.endpointUrl;
19
+ if (url.endsWith("/v1")) {
20
+ url = url + "/chat/completions";
21
+ }
22
+ else if (url.endsWith("/v1/")) {
23
+ url = url + "chat/completions";
24
+ }
25
+ this.endpointUrl = url;
26
+ /*
27
+ A local model's ceiling is wall-clock, not money.
28
+
29
+ Ten minutes suits a chat-sized answer. A dense structured one is a
30
+ different scale: measured against LM Studio, a drum-kit section produced
31
+ 6,274 tokens in 169 seconds — about 37 tokens a second — so a part twice
32
+ that size needs longer than the fixed ten minutes allowed, and the request
33
+ died mid-generation with the server perfectly healthy.
34
+
35
+ Configurable rather than simply raised, because the ceiling is a property
36
+ of the hardware behind the endpoint and only its operator knows it -- the
37
+ API shell passes it in as `timeoutMs`.
38
+ */
39
+ this.timeout = config.timeoutMs ?? 600_000;
40
+ }
41
+ async generate(request) {
42
+ const startTime = Date.now();
43
+ const payload = this.buildApiPayload(request);
44
+ let response;
45
+ try {
46
+ response = await fetch(this.endpointUrl, {
47
+ method: "POST",
48
+ headers: {
49
+ "Content-Type": "application/json",
50
+ },
51
+ body: JSON.stringify(payload),
52
+ signal: AbortSignal.timeout(this.timeout),
53
+ });
54
+ }
55
+ catch (fetchError) {
56
+ const errorMsg = fetchError instanceof Error ? fetchError.message : String(fetchError);
57
+ throw new Error(`Failed to connect to LLM Server at ${this.endpointUrl}: ${errorMsg}`);
58
+ }
59
+ if (!response.ok) {
60
+ const errorText = await response.text();
61
+ throw new Error(`LLM Server error (${response.status}): ${errorText}`);
62
+ }
63
+ const result = await this.readBody(response);
64
+ const latencyMs = Date.now() - startTime;
65
+ /*
66
+ Why the stop reason is read BEFORE parsing.
67
+
68
+ A model that runs out of room returns JSON cut off mid-value, and the
69
+ parse below then fails with a syntax error — so a truncation is reported
70
+ to the caller as a malformed model, which is the wrong diagnosis and, more
71
+ importantly, not an actionable one. A caller told the answer was merely
72
+ CUT can retry or ask for less; a caller told the JSON was broken has
73
+ nothing to act on.
74
+
75
+ This is the same ordering `openai.ts` needed. It cost real time here: four
76
+ local-model runs were diagnosed as "produces invalid JSON" and treated
77
+ with retries, when the model was producing valid JSON and simply being
78
+ truncated — the `finish_reason` saying so was already in the payload.
79
+ */
80
+ const finishReason = normalizeFinishReason((result.choices?.[0] ??
81
+ {}).finish_reason);
82
+ const usage = this.extractUsage(result);
83
+ if (finishReason === "length") {
84
+ throw new Error(`Model output was truncated at the token limit (finish_reason=length) after ${usage.completionTokens} completion tokens with ${usage.promptTokens} in the prompt. The answer is incomplete; raise the ceiling, enlarge the context, or ask for less in one call.`);
85
+ }
86
+ // Parse response - try multiple common formats
87
+ const { rawResponse, content } = this.parseResponse(result);
88
+ return {
89
+ content,
90
+ rawResponse,
91
+ usage,
92
+ model: request.model ?? "custom",
93
+ provider: this.providerName,
94
+ latencyMs,
95
+ finishReason,
96
+ };
97
+ }
98
+ /**
99
+ * The provider's answer, whether it streamed or not.
100
+ *
101
+ * A stream is reassembled into the same object a non-streamed reply would
102
+ * have produced, so everything downstream — the stop-reason check, the JSON
103
+ * extraction, the usage — is unchanged and unaware. Servers that ignore
104
+ * `stream` and answer with plain JSON still work: the content type decides.
105
+ */
106
+ async readBody(response) {
107
+ const contentType = response.headers.get("content-type") ?? "";
108
+ if (!contentType.includes("text/event-stream")) {
109
+ return (await response.json());
110
+ }
111
+ let content = "";
112
+ let finishReason = null;
113
+ let usage = null;
114
+ let buffer = "";
115
+ const reader = response.body?.getReader();
116
+ if (!reader)
117
+ throw new Error("LLM Server returned an empty stream");
118
+ const decoder = new TextDecoder();
119
+ for (;;) {
120
+ const { done, value } = await reader.read();
121
+ if (done)
122
+ break;
123
+ buffer += decoder.decode(value, { stream: true });
124
+ // SSE frames are newline-delimited; a partial one stays in the buffer.
125
+ let newline;
126
+ while ((newline = buffer.indexOf("\n")) >= 0) {
127
+ const line = buffer.slice(0, newline).trim();
128
+ buffer = buffer.slice(newline + 1);
129
+ if (!line.startsWith("data:"))
130
+ continue;
131
+ const data = line.slice(5).trim();
132
+ if (data === "[DONE]")
133
+ continue;
134
+ let chunk;
135
+ try {
136
+ chunk = JSON.parse(data);
137
+ }
138
+ catch {
139
+ continue; // a keep-alive or a comment frame, not a delta
140
+ }
141
+ const choice = chunk.choices?.[0];
142
+ const delta = choice?.delta;
143
+ if (typeof delta?.content === "string")
144
+ content += delta.content;
145
+ if (choice?.finish_reason)
146
+ finishReason = choice.finish_reason;
147
+ if (chunk.usage)
148
+ usage = chunk.usage;
149
+ }
150
+ }
151
+ /*
152
+ An empty stream is its own failure, and has to say so.
153
+
154
+ `parseResponse` tests `message?.content` for truthiness, so an empty
155
+ string falls through every branch and surfaces as "Unable to parse
156
+ OpenAI-format response" — which describes a shape problem when the shape
157
+ was fine and the model simply sent nothing. That is the same class of
158
+ misreport as parsing before reading `finish_reason`.
159
+ */
160
+ if (content === "") {
161
+ throw new Error(`LLM Server streamed no content${finishReason ? ` (finish_reason=${String(finishReason)})` : ""}. The connection stayed open but the model produced nothing.`);
162
+ }
163
+ return {
164
+ choices: [{ message: { content }, finish_reason: finishReason }],
165
+ ...(usage ? { usage } : {}),
166
+ };
167
+ }
168
+ /**
169
+ * Parse response from custom endpoint - supports multiple formats
170
+ */
171
+ parseResponse(result) {
172
+ let rawResponse;
173
+ // OpenAI format
174
+ if (result.choices && Array.isArray(result.choices)) {
175
+ const choice = result.choices[0];
176
+ const message = choice.message;
177
+ if (message?.tool_calls &&
178
+ Array.isArray(message.tool_calls) &&
179
+ message.tool_calls.length > 0) {
180
+ const toolCall = message.tool_calls[0];
181
+ const func = toolCall.function;
182
+ rawResponse = func.arguments;
183
+ }
184
+ else if (typeof message?.content === "string" &&
185
+ message.content !== "") {
186
+ rawResponse = message.content;
187
+ }
188
+ else if (choice.text) {
189
+ rawResponse = choice.text;
190
+ }
191
+ else {
192
+ throw new Error("Unable to parse OpenAI-format response");
193
+ }
194
+ }
195
+ // Anthropic format
196
+ else if (result.content && Array.isArray(result.content)) {
197
+ const contentBlock = result.content[0];
198
+ if (contentBlock.type === "tool_use") {
199
+ return {
200
+ rawResponse: JSON.stringify(contentBlock.input),
201
+ content: contentBlock.input,
202
+ };
203
+ }
204
+ else if (contentBlock.text) {
205
+ rawResponse = contentBlock.text;
206
+ }
207
+ else {
208
+ throw new Error("Unable to parse Anthropic-format response");
209
+ }
210
+ }
211
+ // Direct response format
212
+ else if (typeof result.response === "string") {
213
+ rawResponse = result.response;
214
+ }
215
+ else if (typeof result.text === "string") {
216
+ rawResponse = result.text;
217
+ }
218
+ else if (typeof result.output === "string") {
219
+ rawResponse = result.output;
220
+ }
221
+ // Assume entire result is the response
222
+ else {
223
+ rawResponse = JSON.stringify(result);
224
+ }
225
+ // Extract JSON from response
226
+ const extracted = extractJson(rawResponse);
227
+ try {
228
+ const content = this.safeJsonParse(extracted);
229
+ return { rawResponse, content };
230
+ }
231
+ catch (parseError) {
232
+ const msg = parseError instanceof Error ? parseError.message : String(parseError);
233
+ console.error(`[CustomLLM] JSON parse failed: ${msg}`);
234
+ console.error(`[CustomLLM] Raw response (first 500 chars): ${rawResponse.slice(0, 500)}`);
235
+ console.error(`[CustomLLM] Extracted (first 500 chars): ${extracted.slice(0, 500)}`);
236
+ throw new Error(`${msg}\n---RAW---\n${rawResponse.slice(0, 1000)}`);
237
+ }
238
+ }
239
+ /**
240
+ * Parse JSON with recovery for common LLM output issues:
241
+ * - Unescaped double quotes inside string values
242
+ * - Literal newlines/tabs inside string values
243
+ * - Invalid escape sequences
244
+ */
245
+ safeJsonParse(text) {
246
+ try {
247
+ return JSON.parse(text);
248
+ }
249
+ catch {
250
+ // Fix invalid escape sequences first:
251
+ // 1. \' -> ' (common LLM error — valid in JS/Python but invalid in JSON)
252
+ // 2. "\ n" -> "\n" (LLMs sometimes insert a space between backslash and escape char)
253
+ let repaired = text.replace(/\\'/g, "'");
254
+ repaired = repaired.replace(/\\ ([nrtbf"\\\/])/g, "\\$1");
255
+ try {
256
+ return JSON.parse(repaired);
257
+ }
258
+ catch {
259
+ // noop
260
+ }
261
+ // Repair unescaped quotes inside string values,
262
+ // since regex-based fixes can't match strings correctly with broken quotes
263
+ repaired = this.repairJsonQuotes(repaired);
264
+ try {
265
+ return JSON.parse(repaired);
266
+ }
267
+ catch {
268
+ // Fix literal newlines/tabs inside JSON string values
269
+ const fixed = repaired.replace(/"(?:[^"\\]|\\.)*"/g, match => match
270
+ .replace(/(?<!\\)\n/g, "\\n")
271
+ .replace(/(?<!\\)\r/g, "\\r")
272
+ .replace(/(?<!\\)\t/g, "\\t"));
273
+ try {
274
+ return JSON.parse(fixed);
275
+ }
276
+ catch {
277
+ // Last resort: strip all control characters inside strings
278
+ const stripped = repaired.replace(/"(?:[^"\\]|\\.)*"/g, match => match.replace(/[\x00-\x1f]/g, " "));
279
+ return JSON.parse(stripped);
280
+ }
281
+ }
282
+ }
283
+ }
284
+ /**
285
+ * Repair unescaped double quotes inside JSON string values.
286
+ * Uses a heuristic: a quote inside a string is structural (closing) if followed
287
+ * by JSON structural characters (:, }, ], ,) after optional whitespace.
288
+ * Otherwise it's an unescaped content quote and gets escaped.
289
+ */
290
+ repairJsonQuotes(text) {
291
+ const result = [];
292
+ let i = 0;
293
+ let inString = false;
294
+ while (i < text.length) {
295
+ const ch = text[i];
296
+ if (inString) {
297
+ if (ch === "\\") {
298
+ // Escape sequence — pass through both characters
299
+ result.push(ch);
300
+ i++;
301
+ if (i < text.length) {
302
+ result.push(text[i]);
303
+ i++;
304
+ }
305
+ continue;
306
+ }
307
+ if (ch === '"') {
308
+ // Look ahead past whitespace for a JSON structural character
309
+ let j = i + 1;
310
+ while (j < text.length && " \t\n\r".includes(text[j])) {
311
+ j++;
312
+ }
313
+ const next = j < text.length ? text[j] : "";
314
+ if (next === ":" ||
315
+ next === "," ||
316
+ next === "}" ||
317
+ next === "]" ||
318
+ next === "") {
319
+ // Structural closing quote
320
+ inString = false;
321
+ result.push(ch);
322
+ }
323
+ else {
324
+ // Unescaped inner quote — escape it
325
+ result.push("\\", '"');
326
+ }
327
+ i++;
328
+ continue;
329
+ }
330
+ result.push(ch);
331
+ }
332
+ else {
333
+ if (ch === '"') {
334
+ inString = true;
335
+ }
336
+ result.push(ch);
337
+ }
338
+ i++;
339
+ }
340
+ return result.join("");
341
+ }
342
+ /**
343
+ * Extract usage information from response
344
+ */
345
+ extractUsage(result) {
346
+ const usage = result.usage;
347
+ if (!usage) {
348
+ return { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
349
+ }
350
+ const promptTokens = usage.prompt_tokens ?? usage.input_tokens ?? 0;
351
+ const completionTokens = usage.completion_tokens ??
352
+ usage.output_tokens ??
353
+ 0;
354
+ const totalTokens = usage.total_tokens ?? promptTokens + completionTokens;
355
+ return { promptTokens, completionTokens, totalTokens };
356
+ }
357
+ buildApiPayload(request) {
358
+ // Build OpenAI-compatible payload with multimodal support
359
+ const messages = [];
360
+ if (request.systemPrompt) {
361
+ messages.push({ role: "system", content: request.systemPrompt });
362
+ }
363
+ // Build user message content (multimodal if images present)
364
+ if (request.media?.length) {
365
+ const userContent = [];
366
+ for (const m of request.media) {
367
+ if (m.type === "image") {
368
+ userContent.push({
369
+ type: "image_url",
370
+ image_url: {
371
+ url: m.format === "base64"
372
+ ? `data:${m.mimeType};base64,${m.data}`
373
+ : m.data,
374
+ },
375
+ });
376
+ }
377
+ }
378
+ userContent.push({ type: "text", text: request.prompt });
379
+ messages.push({ role: "user", content: userContent });
380
+ }
381
+ else {
382
+ messages.push({ role: "user", content: request.prompt });
383
+ }
384
+ // Use simple payload for custom LLM servers - rely on system prompt for JSON formatting
385
+ // Many servers don't support response_format or tools
386
+ const payload = {
387
+ messages,
388
+ temperature: request.temperature ?? 0,
389
+ max_tokens: request.maxTokens,
390
+ /*
391
+ Streamed so the connection is never idle.
392
+
393
+ LM Studio closes a request that has sent nothing for 300 seconds, and a
394
+ non-streamed call sends nothing at all until the model has finished — so
395
+ any answer taking longer than five minutes died mid-generation with the
396
+ server working normally. Measured: ordinary parts returned in 86-175s
397
+ while a drum kit, three times denser per bar, ran past it every time.
398
+
399
+ This file's header has claimed streaming since it was written; it was
400
+ never actually turned on. `include_usage` asks for the token counts in
401
+ the final chunk, which a stream otherwise omits.
402
+ */
403
+ stream: true,
404
+ stream_options: { include_usage: true },
405
+ };
406
+ // Include model if specified (required when multiple models are loaded, e.g., LM Studio)
407
+ if (request.model) {
408
+ payload.model = request.model;
409
+ }
410
+ return payload;
411
+ }
412
+ /**
413
+ * Get the actual endpoint URL (after auto-append)
414
+ */
415
+ getEndpointUrl() {
416
+ return this.endpointUrl;
417
+ }
418
+ }
419
+ //# sourceMappingURL=custom.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom.js","sourceRoot":"","sources":["../../../src/services/llm/custom.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD;;;;;GAKG;AACH,MAAM,OAAO,iBAAiB;IACnB,YAAY,GAAG,WAAoB,CAAC;IACrC,WAAW,CAAS;IACpB,OAAO,CAAS;IAExB,YAAY,MAAsB;QAChC,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACzD,CAAC;QACD,gEAAgE;QAChE,IAAI,GAAG,GAAG,MAAM,CAAC,WAAW,CAAC;QAC7B,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACxB,GAAG,GAAG,GAAG,GAAG,mBAAmB,CAAC;QAClC,CAAC;aAAM,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAChC,GAAG,GAAG,GAAG,GAAG,kBAAkB,CAAC;QACjC,CAAC;QACD,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC;QACvB;;;;;;;;;;;;UAYE;QACF,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,SAAS,IAAI,OAAO,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,OAAmB;QAChC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAE7B,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QAE9C,IAAI,QAAkB,CAAC;QACvB,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE;gBACvC,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;iBACnC;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;gBAC7B,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;aAC1C,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,UAAU,EAAE,CAAC;YACpB,MAAM,QAAQ,GACZ,UAAU,YAAY,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YACxE,MAAM,IAAI,KAAK,CACb,sCAAsC,IAAI,CAAC,WAAW,KAAK,QAAQ,EAAE,CACtE,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACxC,MAAM,IAAI,KAAK,CAAC,qBAAqB,QAAQ,CAAC,MAAM,MAAM,SAAS,EAAE,CAAC,CAAC;QACzE,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC7C,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;QAEzC;;;;;;;;;;;;;;UAcE;QACF,MAAM,YAAY,GAAG,qBAAqB,CAEtC,CAAE,MAAM,CAAC,OAAsD,EAAE,CAAC,CAAC,CAAC;YAClE,EAAE,CACL,CAAC,aAAa,CAChB,CAAC;QACF,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QACxC,IAAI,YAAY,KAAK,QAAQ,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CACb,8EAA8E,KAAK,CAAC,gBAAgB,2BAA2B,KAAK,CAAC,YAAY,gHAAgH,CAClQ,CAAC;QACJ,CAAC;QAED,+CAA+C;QAC/C,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;QAE5D,OAAO;YACL,OAAO;YACP,WAAW;YACX,KAAK;YACL,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,QAAQ;YAChC,QAAQ,EAAE,IAAI,CAAC,YAAY;YAC3B,SAAS;YACT,YAAY;SACb,CAAC;IACJ,CAAC;IAED;;;;;;;OAOG;IACK,KAAK,CAAC,QAAQ,CAAC,QAAkB;QACvC,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;QAC/D,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;YAC/C,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA4B,CAAC;QAC5D,CAAC;QAED,IAAI,OAAO,GAAG,EAAE,CAAC;QACjB,IAAI,YAAY,GAAY,IAAI,CAAC;QACjC,IAAI,KAAK,GAAY,IAAI,CAAC;QAC1B,IAAI,MAAM,GAAG,EAAE,CAAC;QAEhB,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC;QAC1C,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACpE,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;QAElC,SAAS,CAAC;YACR,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YAC5C,IAAI,IAAI;gBAAE,MAAM;YAChB,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YAElD,uEAAuE;YACvE,IAAI,OAAe,CAAC;YACpB,OAAO,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC7C,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC7C,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC;gBACnC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;oBAAE,SAAS;gBACxC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;gBAClC,IAAI,IAAI,KAAK,QAAQ;oBAAE,SAAS;gBAChC,IAAI,KAA8B,CAAC;gBACnC,IAAI,CAAC;oBACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAA4B,CAAC;gBACtD,CAAC;gBAAC,MAAM,CAAC;oBACP,SAAS,CAAC,+CAA+C;gBAC3D,CAAC;gBACD,MAAM,MAAM,GACV,KAAK,CAAC,OACP,EAAE,CAAC,CAAC,CAAC,CAAC;gBACP,MAAM,KAAK,GAAG,MAAM,EAAE,KAAyC,CAAC;gBAChE,IAAI,OAAO,KAAK,EAAE,OAAO,KAAK,QAAQ;oBAAE,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC;gBACjE,IAAI,MAAM,EAAE,aAAa;oBAAE,YAAY,GAAG,MAAM,CAAC,aAAa,CAAC;gBAC/D,IAAI,KAAK,CAAC,KAAK;oBAAE,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;YACvC,CAAC;QACH,CAAC;QAED;;;;;;;;UAQE;QACF,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CACb,iCACE,YAAY,CAAC,CAAC,CAAC,mBAAmB,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAC9D,8DAA8D,CAC/D,CAAC;QACJ,CAAC;QAED,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,EAAE,aAAa,EAAE,YAAY,EAAE,CAAC;YAChE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC5B,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,aAAa,CAAC,MAA+B;QAInD,IAAI,WAAmB,CAAC;QAExB,gBAAgB;QAChB,IAAI,MAAM,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YACpD,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAA4B,CAAC;YAC5D,MAAM,OAAO,GAAG,MAAM,CAAC,OAA8C,CAAC;YAEtE,IACE,OAAO,EAAE,UAAU;gBACnB,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC;gBACjC,OAAO,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAC7B,CAAC;gBACD,MAAM,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAA4B,CAAC;gBAClE,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAmC,CAAC;gBAC1D,WAAW,GAAG,IAAI,CAAC,SAAmB,CAAC;YACzC,CAAC;iBAAM,IACL,OAAO,OAAO,EAAE,OAAO,KAAK,QAAQ;gBACpC,OAAO,CAAC,OAAO,KAAK,EAAE,EACtB,CAAC;gBACD,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;YAChC,CAAC;iBAAM,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;gBACvB,WAAW,GAAG,MAAM,CAAC,IAAc,CAAC;YACtC,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC;QACD,mBAAmB;aACd,IAAI,MAAM,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YACzD,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAA4B,CAAC;YAClE,IAAI,YAAY,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;gBACrC,OAAO;oBACL,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,KAAK,CAAC;oBAC/C,OAAO,EAAE,YAAY,CAAC,KAAK;iBAC5B,CAAC;YACJ,CAAC;iBAAM,IAAI,YAAY,CAAC,IAAI,EAAE,CAAC;gBAC7B,WAAW,GAAG,YAAY,CAAC,IAAc,CAAC;YAC5C,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;YAC/D,CAAC;QACH,CAAC;QACD,yBAAyB;aACpB,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC7C,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC;QAChC,CAAC;aAAM,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC3C,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC;QAC5B,CAAC;aAAM,IAAI,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC7C,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC;QAC9B,CAAC;QACD,uCAAuC;aAClC,CAAC;YACJ,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACvC,CAAC;QAED,6BAA6B;QAC7B,MAAM,SAAS,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;QAC3C,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;YAC9C,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;QAClC,CAAC;QAAC,OAAO,UAAU,EAAE,CAAC;YACpB,MAAM,GAAG,GACP,UAAU,YAAY,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YACxE,OAAO,CAAC,KAAK,CAAC,kCAAkC,GAAG,EAAE,CAAC,CAAC;YACvD,OAAO,CAAC,KAAK,CACX,+CAA+C,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAC3E,CAAC;YACF,OAAO,CAAC,KAAK,CACX,4CAA4C,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CACtE,CAAC;YACF,MAAM,IAAI,KAAK,CAAC,GAAG,GAAG,gBAAgB,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACK,aAAa,CAAC,IAAY;QAChC,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,sCAAsC;YACtC,yEAAyE;YACzE,qFAAqF;YACrF,IAAI,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;YACzC,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC;YAE1D,IAAI,CAAC;gBACH,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAC9B,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO;YACT,CAAC;YAED,gDAAgD;YAChD,2EAA2E;YAC3E,QAAQ,GAAG,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;YAC3C,IAAI,CAAC;gBACH,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAC9B,CAAC;YAAC,MAAM,CAAC;gBACP,sDAAsD;gBACtD,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,oBAAoB,EAAE,KAAK,CAAC,EAAE,CAC3D,KAAK;qBACF,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC;qBAC5B,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC;qBAC5B,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC,CAChC,CAAC;gBACF,IAAI,CAAC;oBACH,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBAC3B,CAAC;gBAAC,MAAM,CAAC;oBACP,2DAA2D;oBAC3D,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,oBAAoB,EAAE,KAAK,CAAC,EAAE,CAC9D,KAAK,CAAC,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC,CACnC,CAAC;oBACF,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;gBAC9B,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACK,gBAAgB,CAAC,IAAY;QACnC,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,IAAI,QAAQ,GAAG,KAAK,CAAC;QAErB,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;YACvB,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAE,CAAC;YAEpB,IAAI,QAAQ,EAAE,CAAC;gBACb,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;oBAChB,iDAAiD;oBACjD,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;oBAChB,CAAC,EAAE,CAAC;oBACJ,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;wBACpB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,CAAC;wBACtB,CAAC,EAAE,CAAC;oBACN,CAAC;oBACD,SAAS;gBACX,CAAC;gBACD,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;oBACf,6DAA6D;oBAC7D,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;oBACd,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,EAAE,CAAC;wBACvD,CAAC,EAAE,CAAC;oBACN,CAAC;oBACD,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC7C,IACE,IAAI,KAAK,GAAG;wBACZ,IAAI,KAAK,GAAG;wBACZ,IAAI,KAAK,GAAG;wBACZ,IAAI,KAAK,GAAG;wBACZ,IAAI,KAAK,EAAE,EACX,CAAC;wBACD,2BAA2B;wBAC3B,QAAQ,GAAG,KAAK,CAAC;wBACjB,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;oBAClB,CAAC;yBAAM,CAAC;wBACN,oCAAoC;wBACpC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;oBACzB,CAAC;oBACD,CAAC,EAAE,CAAC;oBACJ,SAAS;gBACX,CAAC;gBACD,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAClB,CAAC;iBAAM,CAAC;gBACN,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;oBACf,QAAQ,GAAG,IAAI,CAAC;gBAClB,CAAC;gBACD,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAClB,CAAC;YACD,CAAC,EAAE,CAAC;QACN,CAAC;QAED,OAAO,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACzB,CAAC;IAED;;OAEG;IACK,YAAY,CAAC,MAA+B;QAKlD,MAAM,KAAK,GAAG,MAAM,CAAC,KAA4C,CAAC;QAElE,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,EAAE,YAAY,EAAE,CAAC,EAAE,gBAAgB,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;QAClE,CAAC;QAED,MAAM,YAAY,GACf,KAAK,CAAC,aAAwB,IAAK,KAAK,CAAC,YAAuB,IAAI,CAAC,CAAC;QACzE,MAAM,gBAAgB,GACnB,KAAK,CAAC,iBAA4B;YAClC,KAAK,CAAC,aAAwB;YAC/B,CAAC,CAAC;QACJ,MAAM,WAAW,GACd,KAAK,CAAC,YAAuB,IAAI,YAAY,GAAG,gBAAgB,CAAC;QAEpE,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,CAAC;IACzD,CAAC;IAED,eAAe,CAAC,OAAmB;QACjC,0DAA0D;QAC1D,MAAM,QAAQ,GAGT,EAAE,CAAC;QAER,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;YACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;QACnE,CAAC;QAED,4DAA4D;QAC5D,IAAI,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;YAC1B,MAAM,WAAW,GAAmC,EAAE,CAAC;YAEvD,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;gBAC9B,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;oBACvB,WAAW,CAAC,IAAI,CAAC;wBACf,IAAI,EAAE,WAAW;wBACjB,SAAS,EAAE;4BACT,GAAG,EACD,CAAC,CAAC,MAAM,KAAK,QAAQ;gCACnB,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,WAAW,CAAC,CAAC,IAAI,EAAE;gCACvC,CAAC,CAAC,CAAC,CAAC,IAAI;yBACb;qBACF,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAED,WAAW,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;YACzD,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC;QACxD,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QAC3D,CAAC;QAED,wFAAwF;QACxF,sDAAsD;QACtD,MAAM,OAAO,GAA4B;YACvC,QAAQ;YACR,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,CAAC;YACrC,UAAU,EAAE,OAAO,CAAC,SAAS;YAC7B;;;;;;;;;;;;cAYE;YACF,MAAM,EAAE,IAAI;YACZ,cAAc,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE;SACxC,CAAC;QAEF,yFAAyF;QACzF,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAClB,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAChC,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;OAEG;IACH,cAAc;QACZ,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;CACF"}