@umituz/web-ai 1.0.1 → 1.0.3
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/dist/cancellation-error-CKcZRhFg.d.ts +129 -0
- package/dist/cancellation-error-DwHrDKBz.d.cts +129 -0
- package/dist/content/index.cjs +914 -37
- package/dist/content/index.cjs.map +1 -1
- package/dist/content/index.d.cts +1 -1
- package/dist/content/index.d.ts +1 -1
- package/dist/content/index.js +914 -37
- package/dist/content/index.js.map +1 -1
- package/dist/core/index.cjs +10 -0
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +13 -3
- package/dist/core/index.d.ts +13 -3
- package/dist/core/index.js +9 -1
- package/dist/core/index.js.map +1 -1
- package/dist/{create-ai-DXEoke8-.d.ts → create-ai-9PW752z8.d.ts} +11 -1
- package/dist/{create-ai-DOI9jD3v.d.cts → create-ai-CXYKPK6G.d.cts} +11 -1
- package/dist/hooks/index.cjs +1192 -39
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.d.cts +64 -2
- package/dist/hooks/index.d.ts +64 -2
- package/dist/hooks/index.js +1188 -40
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.cjs +1230 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -4
- package/dist/index.d.ts +5 -4
- package/dist/index.js +1201 -38
- package/dist/index.js.map +1 -1
- package/dist/providers/index.cjs +1505 -37
- package/dist/providers/index.cjs.map +1 -1
- package/dist/providers/index.d.cts +115 -8
- package/dist/providers/index.d.ts +115 -8
- package/dist/providers/index.js +1478 -38
- package/dist/providers/index.js.map +1 -1
- package/package.json +11 -1
package/dist/index.cjs
CHANGED
|
@@ -132,6 +132,14 @@ var AIError = class _AIError extends Error {
|
|
|
132
132
|
}
|
|
133
133
|
};
|
|
134
134
|
|
|
135
|
+
// src/core/errors/groq-error.ts
|
|
136
|
+
var GroqError = class extends AIError {
|
|
137
|
+
constructor(type, message, cause, metadata = {}) {
|
|
138
|
+
super(type, message, cause, metadata);
|
|
139
|
+
this.name = "GroqError";
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
135
143
|
// src/core/errors/error-classifier.ts
|
|
136
144
|
var DEFAULT_RETRYABLE_TYPES = /* @__PURE__ */ new Set([
|
|
137
145
|
"NETWORK_ERROR" /* NETWORK_ERROR */,
|
|
@@ -410,6 +418,8 @@ function calculateExponentialBackoff(params) {
|
|
|
410
418
|
// src/core/constants/identifier.constants.ts
|
|
411
419
|
var MESSAGE_ID_PREFIX = "msg-";
|
|
412
420
|
var CHAT_MESSAGE_ID_SUFFIX_LENGTH = 9;
|
|
421
|
+
var CACHE_KEY_TYPE_COMPLETION = "completion";
|
|
422
|
+
var CACHE_KEY_TYPE_CHAT = "chat";
|
|
413
423
|
var USER_ROLE = "user";
|
|
414
424
|
var ASSISTANT_ROLE = "assistant";
|
|
415
425
|
var QUEUE_PRIORITY_USER_REQUEST = 10;
|
|
@@ -1881,12 +1891,22 @@ var DEFAULT_GENERATION_CONFIG = {
|
|
|
1881
1891
|
maxTokens: 1024,
|
|
1882
1892
|
topP: 1
|
|
1883
1893
|
};
|
|
1894
|
+
var DEFAULT_MODELS = {
|
|
1895
|
+
TEXT: GROQ_MODELS.LLAMA_3_1_8B_INSTANT,
|
|
1896
|
+
FAST: GROQ_MODELS.LLAMA_3_1_8B_INSTANT,
|
|
1897
|
+
EXPERIMENTAL: GROQ_MODELS.GPT_OSS_20B
|
|
1898
|
+
};
|
|
1884
1899
|
var GROQ_BASE_URL = "https://api.groq.com/openai/v1";
|
|
1900
|
+
var DEFAULT_BASE_URL = GROQ_BASE_URL;
|
|
1885
1901
|
var CHAT_COMPLETIONS_PATH = "/chat/completions";
|
|
1902
|
+
var API_ENDPOINTS = {
|
|
1903
|
+
CHAT: CHAT_COMPLETIONS_PATH
|
|
1904
|
+
};
|
|
1886
1905
|
var GROQ_TIMEOUTS = {
|
|
1887
1906
|
DEFAULT: 3e4,
|
|
1888
1907
|
STREAMING: 6e4
|
|
1889
1908
|
};
|
|
1909
|
+
var TIMEOUTS = GROQ_TIMEOUTS;
|
|
1890
1910
|
var HTTP_HEADERS = {
|
|
1891
1911
|
CONTENT_TYPE: "Content-Type",
|
|
1892
1912
|
APPLICATION_JSON: "application/json",
|
|
@@ -1945,13 +1965,65 @@ var GroqChatRequestBuilder = class _GroqChatRequestBuilder {
|
|
|
1945
1965
|
}
|
|
1946
1966
|
};
|
|
1947
1967
|
|
|
1968
|
+
// src/providers/groq/groq-config-store.ts
|
|
1969
|
+
var GroqConfigStore = class {
|
|
1970
|
+
config = null;
|
|
1971
|
+
initialize(config) {
|
|
1972
|
+
const validation = validateApiKey(config.apiKey, { minLength: 1 });
|
|
1973
|
+
if (!validation.valid) {
|
|
1974
|
+
throw new GroqError("INVALID_API_KEY" /* INVALID_API_KEY */, validation.reason ?? "Invalid API key");
|
|
1975
|
+
}
|
|
1976
|
+
const normalized = {
|
|
1977
|
+
apiKey: config.apiKey.trim(),
|
|
1978
|
+
baseUrl: config.baseUrl ?? DEFAULT_BASE_URL,
|
|
1979
|
+
timeoutMs: config.timeoutMs ?? TIMEOUTS.DEFAULT,
|
|
1980
|
+
...config.models !== void 0 ? { models: config.models } : {},
|
|
1981
|
+
...config.textModel !== void 0 ? { textModel: config.textModel } : {}
|
|
1982
|
+
};
|
|
1983
|
+
this.config = normalized;
|
|
1984
|
+
return normalized;
|
|
1985
|
+
}
|
|
1986
|
+
get() {
|
|
1987
|
+
if (!this.config) {
|
|
1988
|
+
throw new GroqError(
|
|
1989
|
+
"MISSING_CONFIG" /* MISSING_CONFIG */,
|
|
1990
|
+
"Client not initialized. Call configureProvider first."
|
|
1991
|
+
);
|
|
1992
|
+
}
|
|
1993
|
+
return this.config;
|
|
1994
|
+
}
|
|
1995
|
+
getOrNull() {
|
|
1996
|
+
return this.config;
|
|
1997
|
+
}
|
|
1998
|
+
isInitialized() {
|
|
1999
|
+
return this.config !== null;
|
|
2000
|
+
}
|
|
2001
|
+
setApiKey(apiKey) {
|
|
2002
|
+
const current = this.get();
|
|
2003
|
+
const validation = validateApiKey(apiKey, { minLength: 1 });
|
|
2004
|
+
if (!validation.valid) {
|
|
2005
|
+
throw new GroqError("INVALID_API_KEY" /* INVALID_API_KEY */, validation.reason ?? "Invalid API key");
|
|
2006
|
+
}
|
|
2007
|
+
this.config = { ...current, apiKey: apiKey.trim() };
|
|
2008
|
+
}
|
|
2009
|
+
reset() {
|
|
2010
|
+
this.config = null;
|
|
2011
|
+
}
|
|
2012
|
+
};
|
|
2013
|
+
|
|
1948
2014
|
// src/providers/groq/groq-http-transport.ts
|
|
1949
2015
|
var GroqHttpTransport = class {
|
|
1950
|
-
constructor(
|
|
1951
|
-
this.
|
|
2016
|
+
constructor(configSource) {
|
|
2017
|
+
this.configSource = configSource;
|
|
1952
2018
|
}
|
|
1953
|
-
|
|
2019
|
+
configSource;
|
|
1954
2020
|
pendingAbortControllers = /* @__PURE__ */ new Set();
|
|
2021
|
+
getConfig() {
|
|
2022
|
+
if ("get" in this.configSource && typeof this.configSource.get === "function") {
|
|
2023
|
+
return this.configSource.get();
|
|
2024
|
+
}
|
|
2025
|
+
return this.configSource;
|
|
2026
|
+
}
|
|
1955
2027
|
cancelPendingRequests() {
|
|
1956
2028
|
for (const controller of this.pendingAbortControllers) {
|
|
1957
2029
|
controller.abort();
|
|
@@ -1966,7 +2038,7 @@ var GroqHttpTransport = class {
|
|
|
1966
2038
|
}
|
|
1967
2039
|
async streamChatCompletion(request, options = {}) {
|
|
1968
2040
|
const url = `${this.baseUrl()}${CHAT_COMPLETIONS_PATH}`;
|
|
1969
|
-
const timeout = this.
|
|
2041
|
+
const timeout = this.getConfig().timeoutMs ?? GROQ_TIMEOUTS.STREAMING;
|
|
1970
2042
|
const controller = new AbortController();
|
|
1971
2043
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
1972
2044
|
const onExternalAbort = () => controller.abort();
|
|
@@ -1993,11 +2065,11 @@ var GroqHttpTransport = class {
|
|
|
1993
2065
|
}
|
|
1994
2066
|
}
|
|
1995
2067
|
baseUrl() {
|
|
1996
|
-
return this.
|
|
2068
|
+
return this.getConfig().baseUrl ?? GROQ_BASE_URL;
|
|
1997
2069
|
}
|
|
1998
2070
|
async request(endpoint, body) {
|
|
1999
2071
|
const url = `${this.baseUrl()}${endpoint}`;
|
|
2000
|
-
const timeout = this.
|
|
2072
|
+
const timeout = this.getConfig().timeoutMs ?? GROQ_TIMEOUTS.DEFAULT;
|
|
2001
2073
|
const controller = new AbortController();
|
|
2002
2074
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
2003
2075
|
this.pendingAbortControllers.add(controller);
|
|
@@ -2020,7 +2092,7 @@ var GroqHttpTransport = class {
|
|
|
2020
2092
|
buildHeaders() {
|
|
2021
2093
|
return {
|
|
2022
2094
|
[HTTP_HEADERS.CONTENT_TYPE]: HTTP_HEADERS.APPLICATION_JSON,
|
|
2023
|
-
[HTTP_HEADERS.AUTHORIZATION]: `${HTTP_HEADERS.BEARER_PREFIX}${this.
|
|
2095
|
+
[HTTP_HEADERS.AUTHORIZATION]: `${HTTP_HEADERS.BEARER_PREFIX}${this.getConfig().apiKey}`
|
|
2024
2096
|
};
|
|
2025
2097
|
}
|
|
2026
2098
|
async throwFromErrorResponse(response) {
|
|
@@ -2064,21 +2136,40 @@ var GroqHttpTransport = class {
|
|
|
2064
2136
|
|
|
2065
2137
|
// src/providers/groq/groq-http-client.ts
|
|
2066
2138
|
var GroqHttpClient = class {
|
|
2139
|
+
configStore;
|
|
2067
2140
|
transport;
|
|
2068
2141
|
executor;
|
|
2069
|
-
constructor(
|
|
2070
|
-
|
|
2142
|
+
constructor(configOrDeps, maybeDeps = {}) {
|
|
2143
|
+
const isConfig = typeof configOrDeps === "object" && configOrDeps !== null && "apiKey" in configOrDeps && typeof configOrDeps.apiKey === "string";
|
|
2144
|
+
const deps = isConfig ? maybeDeps : configOrDeps ?? {};
|
|
2145
|
+
this.configStore = deps.configStore ?? new GroqConfigStore();
|
|
2146
|
+
if (isConfig) {
|
|
2147
|
+
this.configStore.initialize(configOrDeps);
|
|
2148
|
+
}
|
|
2149
|
+
this.transport = deps.transport ?? new GroqHttpTransport(this.configStore);
|
|
2071
2150
|
const retryManager2 = deps.retryManager ?? new RetryManager();
|
|
2072
2151
|
const queue = deps.queue ?? new RequestQueue();
|
|
2073
2152
|
const deduplicator = deps.deduplicator ?? new RequestDeduplicator();
|
|
2074
2153
|
this.executor = new CompositeExecutor([
|
|
2075
2154
|
new DedupeExecutor(deduplicator, {
|
|
2076
|
-
generateKey: (request) => deduplicatorKey(request,
|
|
2155
|
+
generateKey: (request) => deduplicatorKey(request, this.configStore.isInitialized() ? this.configStore.get().apiKey : "")
|
|
2077
2156
|
}),
|
|
2078
2157
|
new RetryExecutor(retryManager2),
|
|
2079
2158
|
new QueueExecutor(queue, QUEUE_PRIORITY_USER_REQUEST)
|
|
2080
2159
|
]);
|
|
2081
2160
|
}
|
|
2161
|
+
initialize(config) {
|
|
2162
|
+
this.configStore.initialize(config);
|
|
2163
|
+
}
|
|
2164
|
+
isInitialized() {
|
|
2165
|
+
return this.configStore.isInitialized();
|
|
2166
|
+
}
|
|
2167
|
+
getApiKey() {
|
|
2168
|
+
return this.configStore.get().apiKey;
|
|
2169
|
+
}
|
|
2170
|
+
setApiKey(apiKey) {
|
|
2171
|
+
this.configStore.setApiKey(apiKey);
|
|
2172
|
+
}
|
|
2082
2173
|
cancelPendingRequests() {
|
|
2083
2174
|
this.transport.cancelPendingRequests();
|
|
2084
2175
|
}
|
|
@@ -2091,6 +2182,10 @@ var GroqHttpClient = class {
|
|
|
2091
2182
|
streamChatCompletion(request, options) {
|
|
2092
2183
|
return this.transport.streamChatCompletion(request, options);
|
|
2093
2184
|
}
|
|
2185
|
+
reset() {
|
|
2186
|
+
this.transport.cancelPendingRequests();
|
|
2187
|
+
this.configStore.reset();
|
|
2188
|
+
}
|
|
2094
2189
|
};
|
|
2095
2190
|
function deduplicatorKey(request, apiKey) {
|
|
2096
2191
|
const { model, messages, stream: _stream, ...rest } = request;
|
|
@@ -2822,39 +2917,811 @@ function createFalProvider(config) {
|
|
|
2822
2917
|
return new FalProvider(config);
|
|
2823
2918
|
}
|
|
2824
2919
|
|
|
2825
|
-
// src/providers/pruna/
|
|
2826
|
-
var
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2920
|
+
// src/providers/pruna/client/core/constants/limits.ts
|
|
2921
|
+
var DEFAULT_ASPECT_RATIO = "16:9";
|
|
2922
|
+
var P_VIDEO_DEFAULTS = {
|
|
2923
|
+
duration: 5,
|
|
2924
|
+
resolution: "720p",
|
|
2925
|
+
fps: 24,
|
|
2926
|
+
draft: false,
|
|
2927
|
+
promptUpsampling: true
|
|
2928
|
+
};
|
|
2929
|
+
var POLL_DEFAULTS = {
|
|
2930
|
+
intervalMs: 3e3,
|
|
2931
|
+
maxAttempts: 120,
|
|
2932
|
+
maxIntervalMs: 1e4,
|
|
2933
|
+
backoffBase: 1.5,
|
|
2934
|
+
jitterPercent: 0.1
|
|
2935
|
+
};
|
|
2936
|
+
var DEFAULT_DEDUPLICATOR_TTL_MS = 3e4;
|
|
2937
|
+
var DEDUPLICATOR_CLEANUP_GRACE_MS = 100;
|
|
2938
|
+
|
|
2939
|
+
// src/providers/pruna/client/core/constants/network.ts
|
|
2940
|
+
var HTTPS_PREFIX = "https://";
|
|
2941
|
+
var HTTP_PREFIX = "http://";
|
|
2942
|
+
var BASE64_MARKER = "base64,";
|
|
2943
|
+
var PRUNA_BASE_URL = "https://api.pruna.ai";
|
|
2944
|
+
var PRUNA_PREDICTIONS_URL = `${PRUNA_BASE_URL}/v1/predictions`;
|
|
2945
|
+
var PRUNA_FILES_URL = `${PRUNA_BASE_URL}/v1/files`;
|
|
2946
|
+
var AUTH_FAILURE_STATUSES = [401, 403];
|
|
2947
|
+
var MIN_API_KEY_LENGTH = 1;
|
|
2948
|
+
var MAX_API_KEY_LENGTH = 256;
|
|
2949
|
+
|
|
2950
|
+
// src/providers/pruna/client/core/constants/mime-types.ts
|
|
2951
|
+
var MIME_PNG = "image/png";
|
|
2952
|
+
var MIME_JPEG = "image/jpeg";
|
|
2953
|
+
var MIME_WEBP = "image/webp";
|
|
2954
|
+
var DEFAULT_IMAGE_MIME = MIME_PNG;
|
|
2955
|
+
var MIME_FILE_EXTENSION = {
|
|
2956
|
+
[MIME_PNG]: "png",
|
|
2957
|
+
[MIME_JPEG]: "jpg",
|
|
2958
|
+
[MIME_WEBP]: "webp"
|
|
2959
|
+
};
|
|
2960
|
+
var IMAGE_SIGNATURES = {
|
|
2961
|
+
PNG: [137, 80],
|
|
2962
|
+
JPEG: [255, 216],
|
|
2963
|
+
WEBP_RIFF: [82, 73]
|
|
2964
|
+
};
|
|
2965
|
+
|
|
2966
|
+
// src/providers/pruna/client/core/errors/cancellation-error.ts
|
|
2967
|
+
var CANCELLATION_MESSAGE = "Request cancelled by user";
|
|
2968
|
+
var CancellationError = class extends Error {
|
|
2969
|
+
constructor(message = CANCELLATION_MESSAGE) {
|
|
2970
|
+
super(message);
|
|
2971
|
+
this.name = "CancellationError";
|
|
2831
2972
|
}
|
|
2832
|
-
|
|
2973
|
+
};
|
|
2974
|
+
|
|
2975
|
+
// src/providers/pruna/client/core/errors/authentication-error.ts
|
|
2976
|
+
var AUTHENTICATION_MESSAGE = "Authentication failed. Please check your API key.";
|
|
2977
|
+
var AuthenticationError = class extends Error {
|
|
2978
|
+
constructor(message = AUTHENTICATION_MESSAGE) {
|
|
2979
|
+
super(message);
|
|
2980
|
+
this.name = "AuthenticationError";
|
|
2981
|
+
}
|
|
2982
|
+
};
|
|
2983
|
+
|
|
2984
|
+
// src/providers/pruna/client/core/errors/timeout-error.ts
|
|
2985
|
+
var TIMEOUT_MESSAGE = "Generation timed out. Maximum polling attempts reached.";
|
|
2986
|
+
var TimeoutError = class extends Error {
|
|
2987
|
+
constructor(message = TIMEOUT_MESSAGE) {
|
|
2988
|
+
super(message);
|
|
2989
|
+
this.name = "TimeoutError";
|
|
2990
|
+
}
|
|
2991
|
+
};
|
|
2992
|
+
|
|
2993
|
+
// src/providers/pruna/client/core/errors/validation-error.ts
|
|
2994
|
+
var ValidationError = class extends Error {
|
|
2995
|
+
field;
|
|
2996
|
+
reason;
|
|
2997
|
+
receivedValue;
|
|
2998
|
+
constructor(context) {
|
|
2999
|
+
super(`Invalid ${context.field}: ${context.reason}`);
|
|
3000
|
+
this.name = "ValidationError";
|
|
3001
|
+
this.field = context.field;
|
|
3002
|
+
this.reason = context.reason;
|
|
3003
|
+
this.receivedValue = context.receivedValue;
|
|
3004
|
+
}
|
|
3005
|
+
};
|
|
3006
|
+
|
|
3007
|
+
// src/providers/pruna/client/core/utils/base64.ts
|
|
3008
|
+
var globalScope = globalThis;
|
|
3009
|
+
var hasBuffer = typeof globalScope.Buffer?.from === "function";
|
|
3010
|
+
function base64ToBytes(raw) {
|
|
3011
|
+
if (hasBuffer) {
|
|
3012
|
+
const bufferFactory = globalScope.Buffer;
|
|
3013
|
+
if (!bufferFactory) {
|
|
3014
|
+
throw new Error("Buffer unavailable despite detection flag");
|
|
3015
|
+
}
|
|
3016
|
+
const buf = bufferFactory.from(raw, "base64");
|
|
3017
|
+
return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
3018
|
+
}
|
|
3019
|
+
const decoded = atob(raw);
|
|
3020
|
+
return Uint8Array.from(decoded, (char) => char.charCodeAt(0));
|
|
2833
3021
|
}
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
if (
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
3022
|
+
|
|
3023
|
+
// src/providers/pruna/client/core/utils/mime-detector.ts
|
|
3024
|
+
var MIN_SIGNATURE_BYTES = 2;
|
|
3025
|
+
function matchesSignature(bytes, signature) {
|
|
3026
|
+
if (bytes.length < signature.length) return false;
|
|
3027
|
+
return signature.every((value, index) => bytes[index] === value);
|
|
3028
|
+
}
|
|
3029
|
+
function detectImageMime(bytes) {
|
|
3030
|
+
if (bytes.length < MIN_SIGNATURE_BYTES) return DEFAULT_IMAGE_MIME;
|
|
3031
|
+
if (matchesSignature(bytes, IMAGE_SIGNATURES.PNG)) return MIME_PNG;
|
|
3032
|
+
if (matchesSignature(bytes, IMAGE_SIGNATURES.JPEG)) return MIME_JPEG;
|
|
3033
|
+
if (matchesSignature(bytes, IMAGE_SIGNATURES.WEBP_RIFF)) return MIME_WEBP;
|
|
3034
|
+
return DEFAULT_IMAGE_MIME;
|
|
3035
|
+
}
|
|
3036
|
+
|
|
3037
|
+
// src/providers/pruna/client/core/utils/api-error-parser.ts
|
|
3038
|
+
async function parseApiError(response, fallback) {
|
|
3039
|
+
const payload = await response.json().catch(() => ({ message: response.statusText }));
|
|
3040
|
+
const message = payload.message ?? `${fallback}: ${response.status}`;
|
|
3041
|
+
const error = new Error(message);
|
|
3042
|
+
error.statusCode = response.status;
|
|
3043
|
+
return error;
|
|
3044
|
+
}
|
|
3045
|
+
|
|
3046
|
+
// src/providers/pruna/client/core/protocol/http-status.ts
|
|
3047
|
+
function isAuthFailureStatus(status) {
|
|
3048
|
+
return AUTH_FAILURE_STATUSES.includes(status);
|
|
3049
|
+
}
|
|
3050
|
+
|
|
3051
|
+
// src/providers/pruna/client/core/protocol/job-status.ts
|
|
3052
|
+
var TERMINAL_SUCCESS_STATUSES = ["succeeded", "completed"];
|
|
3053
|
+
var TERMINAL_FAILURE_STATUSES = ["failed"];
|
|
3054
|
+
function isTerminalSuccessStatus(status) {
|
|
3055
|
+
if (!status) return false;
|
|
3056
|
+
return TERMINAL_SUCCESS_STATUSES.includes(status);
|
|
3057
|
+
}
|
|
3058
|
+
function isTerminalFailureStatus(status) {
|
|
3059
|
+
if (!status) return false;
|
|
3060
|
+
return TERMINAL_FAILURE_STATUSES.includes(status);
|
|
3061
|
+
}
|
|
3062
|
+
|
|
3063
|
+
// src/providers/pruna/client/core/protocol/abort-signal-classifier.ts
|
|
3064
|
+
function isAbortError(err) {
|
|
3065
|
+
if (err instanceof DOMException && err.code === DOMException.ABORT_ERR) return true;
|
|
3066
|
+
if (err instanceof Error && err.name === "AbortError") return true;
|
|
3067
|
+
return false;
|
|
3068
|
+
}
|
|
3069
|
+
|
|
3070
|
+
// src/providers/pruna/client/core/protocol/pruna-url-builder.ts
|
|
3071
|
+
function isAbsoluteHttpUrl(value) {
|
|
3072
|
+
return value.startsWith(HTTPS_PREFIX) || value.startsWith(HTTP_PREFIX);
|
|
3073
|
+
}
|
|
3074
|
+
function isHttpsUrl(value) {
|
|
3075
|
+
return value.startsWith(HTTPS_PREFIX);
|
|
3076
|
+
}
|
|
3077
|
+
function buildPrunaApiUrl(pathOrUrl) {
|
|
3078
|
+
if (isAbsoluteHttpUrl(pathOrUrl)) return pathOrUrl;
|
|
3079
|
+
return `${PRUNA_BASE_URL}${pathOrUrl}`;
|
|
3080
|
+
}
|
|
3081
|
+
function buildFileUrlById(fileId) {
|
|
3082
|
+
return `${PRUNA_FILES_URL}/${fileId}`;
|
|
3083
|
+
}
|
|
3084
|
+
|
|
3085
|
+
// src/providers/pruna/client/core/math/exponential-backoff.ts
|
|
3086
|
+
function calculateExponentialBackoff2(inputs) {
|
|
3087
|
+
const {
|
|
3088
|
+
attempt,
|
|
3089
|
+
baseIntervalMs,
|
|
3090
|
+
maxIntervalMs = POLL_DEFAULTS.maxIntervalMs,
|
|
3091
|
+
jitterPercent = POLL_DEFAULTS.jitterPercent,
|
|
3092
|
+
growthBase = POLL_DEFAULTS.backoffBase
|
|
3093
|
+
} = inputs;
|
|
3094
|
+
const safeAttempt = Math.max(0, attempt);
|
|
3095
|
+
const safeBase = Math.max(0, baseIntervalMs);
|
|
3096
|
+
const exponentialDelay = Math.min(safeBase * Math.pow(growthBase, safeAttempt), maxIntervalMs);
|
|
3097
|
+
const jitter = exponentialDelay * jitterPercent * Math.random();
|
|
3098
|
+
return exponentialDelay + jitter;
|
|
3099
|
+
}
|
|
3100
|
+
|
|
3101
|
+
// src/providers/pruna/client/core/utils/uri-resolver.ts
|
|
3102
|
+
var uriCache = /* @__PURE__ */ new WeakMap();
|
|
3103
|
+
function stripBase64Prefix(image) {
|
|
3104
|
+
if (isHttpsUrl(image)) return image;
|
|
3105
|
+
const base64Index = image.indexOf(BASE64_MARKER);
|
|
3106
|
+
return base64Index !== -1 ? image.slice(base64Index + BASE64_MARKER.length) : image;
|
|
3107
|
+
}
|
|
3108
|
+
function readOutput(output) {
|
|
3109
|
+
if (typeof output === "string") return output;
|
|
3110
|
+
if (Array.isArray(output) && output.length > 0) return output[0] ?? null;
|
|
3111
|
+
if (output !== null && typeof output === "object" && "url" in output) {
|
|
3112
|
+
return output.url;
|
|
3113
|
+
}
|
|
3114
|
+
return null;
|
|
3115
|
+
}
|
|
3116
|
+
function extractUri(data) {
|
|
3117
|
+
const cached = uriCache.get(data);
|
|
3118
|
+
if (cached !== void 0) return cached;
|
|
3119
|
+
if (!isTerminalSuccessStatus(data.status)) {
|
|
3120
|
+
uriCache.set(data, null);
|
|
3121
|
+
return null;
|
|
3122
|
+
}
|
|
3123
|
+
const result = data.generation_url ?? readOutput(data.output) ?? data.video_url ?? data.data ?? null;
|
|
3124
|
+
uriCache.set(data, result);
|
|
3125
|
+
return result;
|
|
3126
|
+
}
|
|
3127
|
+
function resolveUri(uri) {
|
|
3128
|
+
return uri.startsWith("/") ? `${PRUNA_BASE_URL}${uri}` : uri;
|
|
3129
|
+
}
|
|
3130
|
+
|
|
3131
|
+
// src/providers/pruna/client/core/services/pruna-client.service.ts
|
|
3132
|
+
function buildFileExtension(mime) {
|
|
3133
|
+
return MIME_FILE_EXTENSION[mime] ?? "bin";
|
|
3134
|
+
}
|
|
3135
|
+
async function uploadImage(base64OrUrl, apiKey, onProgress) {
|
|
3136
|
+
if (isHttpsUrl(base64OrUrl)) return base64OrUrl;
|
|
3137
|
+
if (base64OrUrl.startsWith(HTTP_PREFIX)) {
|
|
3138
|
+
throw new Error("Only HTTPS URLs are supported for security.");
|
|
3139
|
+
}
|
|
3140
|
+
onProgress?.("uploading");
|
|
3141
|
+
const raw = stripBase64Prefix(base64OrUrl);
|
|
3142
|
+
let bytes;
|
|
3143
|
+
try {
|
|
3144
|
+
bytes = base64ToBytes(raw);
|
|
3145
|
+
} catch {
|
|
3146
|
+
throw new Error("Invalid image format. Provide base64 or a valid HTTPS URL.");
|
|
3147
|
+
}
|
|
3148
|
+
const mime = detectImageMime(bytes);
|
|
3149
|
+
const buffer = bytes.buffer.slice(
|
|
3150
|
+
bytes.byteOffset,
|
|
3151
|
+
bytes.byteOffset + bytes.byteLength
|
|
3152
|
+
);
|
|
3153
|
+
const blob = new Blob([buffer], { type: mime });
|
|
3154
|
+
const formData = new FormData();
|
|
3155
|
+
formData.append("content", blob, `upload.${buildFileExtension(mime)}`);
|
|
3156
|
+
const res = await fetch(PRUNA_FILES_URL, {
|
|
3157
|
+
method: "POST",
|
|
3158
|
+
headers: { apikey: apiKey },
|
|
3159
|
+
body: formData
|
|
3160
|
+
});
|
|
3161
|
+
if (!res.ok) {
|
|
3162
|
+
throw await parseApiError(res, "File upload error");
|
|
3163
|
+
}
|
|
3164
|
+
const data = await res.json();
|
|
3165
|
+
return data.urls?.get ?? buildFileUrlById(data.id ?? "");
|
|
3166
|
+
}
|
|
3167
|
+
async function submitPrediction(model, input, apiKey, signal, onProgress) {
|
|
3168
|
+
onProgress?.("predicting");
|
|
3169
|
+
const res = await fetch(PRUNA_PREDICTIONS_URL, {
|
|
3170
|
+
method: "POST",
|
|
3171
|
+
headers: {
|
|
3172
|
+
apikey: apiKey,
|
|
3173
|
+
Model: model,
|
|
3174
|
+
"Try-Sync": "true",
|
|
3175
|
+
"Content-Type": "application/json"
|
|
3176
|
+
},
|
|
3177
|
+
body: JSON.stringify({ input }),
|
|
3178
|
+
signal
|
|
3179
|
+
});
|
|
3180
|
+
if (!res.ok) {
|
|
3181
|
+
throw await parseApiError(res, "API error");
|
|
3182
|
+
}
|
|
3183
|
+
return res.json();
|
|
3184
|
+
}
|
|
3185
|
+
var GenerationFailedError = class extends Error {
|
|
3186
|
+
constructor(message) {
|
|
3187
|
+
super(message);
|
|
3188
|
+
this.name = "GenerationFailedError";
|
|
3189
|
+
}
|
|
3190
|
+
};
|
|
3191
|
+
async function attemptPoll(fullUrl, apiKey, signal) {
|
|
3192
|
+
let res;
|
|
3193
|
+
try {
|
|
3194
|
+
res = await fetch(fullUrl, { headers: { apikey: apiKey }, signal });
|
|
3195
|
+
} catch (err) {
|
|
3196
|
+
if (isAbortError(err)) throw new CancellationError();
|
|
3197
|
+
return { outcome: "transient-failure" };
|
|
3198
|
+
}
|
|
3199
|
+
if (!res.ok) {
|
|
3200
|
+
if (isAuthFailureStatus(res.status)) throw new AuthenticationError();
|
|
3201
|
+
return { outcome: "transient-failure" };
|
|
3202
|
+
}
|
|
3203
|
+
let data;
|
|
3204
|
+
try {
|
|
3205
|
+
data = await res.json();
|
|
3206
|
+
} catch {
|
|
3207
|
+
return { outcome: "transient-failure" };
|
|
3208
|
+
}
|
|
3209
|
+
if (isTerminalSuccessStatus(data.status)) {
|
|
3210
|
+
const uri = extractUri(data);
|
|
3211
|
+
if (uri) return { outcome: "succeeded", uri: resolveUri(uri) };
|
|
3212
|
+
return { outcome: "transient-failure" };
|
|
3213
|
+
}
|
|
3214
|
+
if (isTerminalFailureStatus(data.status)) {
|
|
3215
|
+
throw new GenerationFailedError(data.error ?? "Generation failed during processing.");
|
|
3216
|
+
}
|
|
3217
|
+
return { outcome: "continue" };
|
|
3218
|
+
}
|
|
3219
|
+
async function pollForResult(pollUrl, apiKey, maxAttempts = POLL_DEFAULTS.maxAttempts, intervalMs = POLL_DEFAULTS.intervalMs, signal, onProgress) {
|
|
3220
|
+
const fullUrl = isAbsoluteHttpUrl(pollUrl) ? pollUrl : buildPrunaApiUrl(pollUrl);
|
|
3221
|
+
const controller = new AbortController();
|
|
3222
|
+
const onExternalAbort = () => controller.abort();
|
|
3223
|
+
signal?.addEventListener("abort", onExternalAbort);
|
|
3224
|
+
try {
|
|
3225
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
3226
|
+
if (signal?.aborted) throw new CancellationError();
|
|
3227
|
+
const delay = calculateExponentialBackoff2({ attempt: i, baseIntervalMs: intervalMs });
|
|
3228
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
3229
|
+
if (signal?.aborted) throw new CancellationError();
|
|
3230
|
+
onProgress?.("polling", i + 1);
|
|
3231
|
+
const result = await attemptPoll(fullUrl, apiKey, controller.signal);
|
|
3232
|
+
if (result.outcome === "succeeded" && result.uri) {
|
|
3233
|
+
return result.uri;
|
|
3234
|
+
}
|
|
3235
|
+
}
|
|
3236
|
+
throw new TimeoutError();
|
|
3237
|
+
} finally {
|
|
3238
|
+
signal?.removeEventListener("abort", onExternalAbort);
|
|
3239
|
+
controller.abort();
|
|
3240
|
+
}
|
|
3241
|
+
}
|
|
3242
|
+
|
|
3243
|
+
// src/providers/pruna/client/core/math/comparison-hash.ts
|
|
3244
|
+
function sortKeysDeep(value) {
|
|
3245
|
+
if (Array.isArray(value)) {
|
|
3246
|
+
return value.map(sortKeysDeep);
|
|
3247
|
+
}
|
|
3248
|
+
if (value !== null && typeof value === "object") {
|
|
3249
|
+
const record = value;
|
|
3250
|
+
return Object.keys(record).sort().reduce((acc, key) => {
|
|
3251
|
+
acc[key] = sortKeysDeep(record[key]);
|
|
3252
|
+
return acc;
|
|
3253
|
+
}, {});
|
|
3254
|
+
}
|
|
3255
|
+
return value;
|
|
3256
|
+
}
|
|
3257
|
+
function hashValue(value) {
|
|
3258
|
+
if (value === null || typeof value !== "object") {
|
|
3259
|
+
return String(value);
|
|
3260
|
+
}
|
|
3261
|
+
try {
|
|
3262
|
+
return JSON.stringify(sortKeysDeep(value));
|
|
3263
|
+
} catch {
|
|
3264
|
+
return String(value);
|
|
3265
|
+
}
|
|
3266
|
+
}
|
|
3267
|
+
function compositeHash(...values) {
|
|
3268
|
+
return values.map(hashValue).join("|");
|
|
3269
|
+
}
|
|
3270
|
+
|
|
3271
|
+
// src/providers/pruna/client/core/math/cache-key-derivation.ts
|
|
3272
|
+
function deriveGenerationCacheKey(apiKey, input) {
|
|
3273
|
+
return `${apiKey}:${compositeHash(input)}`;
|
|
3274
|
+
}
|
|
3275
|
+
|
|
3276
|
+
// src/providers/pruna/client/core/validation/api-key-validator.ts
|
|
3277
|
+
function assertValidApiKey(apiKey) {
|
|
3278
|
+
if (typeof apiKey !== "string") {
|
|
3279
|
+
throw new ValidationError({
|
|
3280
|
+
field: "apiKey",
|
|
3281
|
+
reason: "must be a string",
|
|
3282
|
+
receivedValue: typeof apiKey
|
|
3283
|
+
});
|
|
3284
|
+
}
|
|
3285
|
+
if (apiKey.length < MIN_API_KEY_LENGTH) {
|
|
3286
|
+
throw new ValidationError({
|
|
3287
|
+
field: "apiKey",
|
|
3288
|
+
reason: "must not be empty",
|
|
3289
|
+
receivedValue: apiKey
|
|
3290
|
+
});
|
|
3291
|
+
}
|
|
3292
|
+
if (apiKey.length > MAX_API_KEY_LENGTH) {
|
|
3293
|
+
throw new ValidationError({
|
|
3294
|
+
field: "apiKey",
|
|
3295
|
+
reason: `exceeds maximum length of ${MAX_API_KEY_LENGTH}`,
|
|
3296
|
+
receivedValue: apiKey.length
|
|
3297
|
+
});
|
|
3298
|
+
}
|
|
3299
|
+
}
|
|
3300
|
+
|
|
3301
|
+
// src/providers/pruna/client/core/validation/image-source-validator.ts
|
|
3302
|
+
function classifyImageSource(source) {
|
|
3303
|
+
if (source.startsWith(HTTPS_PREFIX)) return "https-url";
|
|
3304
|
+
if (source.startsWith(HTTP_PREFIX)) return "http-url";
|
|
3305
|
+
if (source.includes(BASE64_MARKER) || /^[A-Za-z0-9+/=]+$/.test(source)) return "base64";
|
|
3306
|
+
return "unknown";
|
|
3307
|
+
}
|
|
3308
|
+
function assertSecureImageSource(image, fieldName = "image") {
|
|
3309
|
+
if (typeof image !== "string") {
|
|
3310
|
+
throw new ValidationError({
|
|
3311
|
+
field: fieldName,
|
|
3312
|
+
reason: "must be a string",
|
|
3313
|
+
receivedValue: typeof image
|
|
3314
|
+
});
|
|
3315
|
+
}
|
|
3316
|
+
const kind = classifyImageSource(image);
|
|
3317
|
+
if (kind === "http-url") {
|
|
3318
|
+
throw new ValidationError({
|
|
3319
|
+
field: fieldName,
|
|
3320
|
+
reason: "HTTP URLs are not supported; use HTTPS or a base64 data URI",
|
|
3321
|
+
receivedValue: image
|
|
3322
|
+
});
|
|
3323
|
+
}
|
|
3324
|
+
if (kind === "unknown") {
|
|
3325
|
+
throw new ValidationError({
|
|
3326
|
+
field: fieldName,
|
|
3327
|
+
reason: "must be an HTTPS URL or a base64 data URI",
|
|
3328
|
+
receivedValue: typeof image
|
|
3329
|
+
});
|
|
3330
|
+
}
|
|
3331
|
+
}
|
|
3332
|
+
|
|
3333
|
+
// src/providers/pruna/client/core/validation/aspect-ratio-validator.ts
|
|
3334
|
+
var SUPPORTED_ASPECT_RATIOS = [
|
|
3335
|
+
"16:9",
|
|
3336
|
+
"9:16",
|
|
3337
|
+
"1:1",
|
|
3338
|
+
"4:3",
|
|
3339
|
+
"3:4",
|
|
3340
|
+
"3:2",
|
|
3341
|
+
"2:3"
|
|
3342
|
+
];
|
|
3343
|
+
function isSupportedAspectRatio(value) {
|
|
3344
|
+
return typeof value === "string" && SUPPORTED_ASPECT_RATIOS.includes(value);
|
|
3345
|
+
}
|
|
3346
|
+
function assertValidAspectRatio(value, fieldName = "aspect_ratio") {
|
|
3347
|
+
if (value === void 0 || value === null) return;
|
|
3348
|
+
if (!isSupportedAspectRatio(value)) {
|
|
3349
|
+
throw new ValidationError({
|
|
3350
|
+
field: fieldName,
|
|
3351
|
+
reason: `must be one of ${SUPPORTED_ASPECT_RATIOS.join(", ")}`,
|
|
3352
|
+
receivedValue: value
|
|
3353
|
+
});
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3356
|
+
|
|
3357
|
+
// src/providers/pruna/client/core/validation/resolution-validator.ts
|
|
3358
|
+
var SUPPORTED_RESOLUTIONS = ["720p", "1080p"];
|
|
3359
|
+
function isSupportedResolution(value) {
|
|
3360
|
+
return typeof value === "string" && SUPPORTED_RESOLUTIONS.includes(value);
|
|
3361
|
+
}
|
|
3362
|
+
function assertValidResolution(value, fieldName = "resolution") {
|
|
3363
|
+
if (value === void 0 || value === null) return;
|
|
3364
|
+
if (!isSupportedResolution(value)) {
|
|
3365
|
+
throw new ValidationError({
|
|
3366
|
+
field: fieldName,
|
|
3367
|
+
reason: `must be one of ${SUPPORTED_RESOLUTIONS.join(", ")}`,
|
|
3368
|
+
receivedValue: value
|
|
3369
|
+
});
|
|
3370
|
+
}
|
|
3371
|
+
}
|
|
3372
|
+
|
|
3373
|
+
// src/providers/pruna/client/core/validation/duration-validator.ts
|
|
3374
|
+
var MIN_VIDEO_DURATION_SECONDS = 1;
|
|
3375
|
+
var MAX_VIDEO_DURATION_SECONDS = 30;
|
|
3376
|
+
function isValidDuration(value) {
|
|
3377
|
+
return typeof value === "number" && Number.isFinite(value) && value >= MIN_VIDEO_DURATION_SECONDS && value <= MAX_VIDEO_DURATION_SECONDS && Number.isInteger(value);
|
|
3378
|
+
}
|
|
3379
|
+
function assertValidDuration(value, fieldName = "duration") {
|
|
3380
|
+
if (value === void 0 || value === null) return;
|
|
3381
|
+
if (!isValidDuration(value)) {
|
|
3382
|
+
throw new ValidationError({
|
|
3383
|
+
field: fieldName,
|
|
3384
|
+
reason: `must be an integer between ${MIN_VIDEO_DURATION_SECONDS} and ${MAX_VIDEO_DURATION_SECONDS} seconds`,
|
|
3385
|
+
receivedValue: value
|
|
3386
|
+
});
|
|
3387
|
+
}
|
|
3388
|
+
}
|
|
3389
|
+
|
|
3390
|
+
// src/providers/pruna/client/core/validation/prompt-validator.ts
|
|
3391
|
+
var MIN_PROMPT_LENGTH = 1;
|
|
3392
|
+
var MAX_PROMPT_LENGTH = 4e3;
|
|
3393
|
+
function assertValidPrompt(value, fieldName = "prompt") {
|
|
3394
|
+
if (typeof value !== "string") {
|
|
3395
|
+
throw new ValidationError({
|
|
3396
|
+
field: fieldName,
|
|
3397
|
+
reason: "must be a string",
|
|
3398
|
+
receivedValue: typeof value
|
|
3399
|
+
});
|
|
3400
|
+
}
|
|
3401
|
+
if (value.trim().length < MIN_PROMPT_LENGTH) {
|
|
3402
|
+
throw new ValidationError({
|
|
3403
|
+
field: fieldName,
|
|
3404
|
+
reason: "must not be empty or whitespace-only",
|
|
3405
|
+
receivedValue: value
|
|
3406
|
+
});
|
|
3407
|
+
}
|
|
3408
|
+
if (value.length > MAX_PROMPT_LENGTH) {
|
|
3409
|
+
throw new ValidationError({
|
|
3410
|
+
field: fieldName,
|
|
3411
|
+
reason: `exceeds maximum length of ${MAX_PROMPT_LENGTH} characters`,
|
|
3412
|
+
receivedValue: value.length
|
|
3413
|
+
});
|
|
3414
|
+
}
|
|
3415
|
+
}
|
|
3416
|
+
|
|
3417
|
+
// src/providers/pruna/client/core/validation/seed-validator.ts
|
|
3418
|
+
var MIN_SEED = -2147483648;
|
|
3419
|
+
var MAX_SEED = 2147483647;
|
|
3420
|
+
function isValidSeed(value) {
|
|
3421
|
+
return typeof value === "number" && Number.isFinite(value) && Number.isInteger(value) && value >= MIN_SEED && value <= MAX_SEED;
|
|
3422
|
+
}
|
|
3423
|
+
function assertValidSeed(value, fieldName = "seed") {
|
|
3424
|
+
if (value === void 0 || value === null) return;
|
|
3425
|
+
if (!isValidSeed(value)) {
|
|
3426
|
+
throw new ValidationError({
|
|
3427
|
+
field: fieldName,
|
|
3428
|
+
reason: `must be an integer in [${MIN_SEED}, ${MAX_SEED}]`,
|
|
3429
|
+
receivedValue: value
|
|
3430
|
+
});
|
|
3431
|
+
}
|
|
3432
|
+
}
|
|
3433
|
+
|
|
3434
|
+
// src/providers/pruna/client/core/protocol/input-type-guard.ts
|
|
3435
|
+
function isTextToImageInput(input) {
|
|
3436
|
+
return input.model === "p-image";
|
|
3437
|
+
}
|
|
3438
|
+
function isImageToImageInput(input) {
|
|
3439
|
+
return input.model === "p-image-edit";
|
|
3440
|
+
}
|
|
3441
|
+
function isImageToVideoInput(input) {
|
|
3442
|
+
return input.model === "p-video";
|
|
3443
|
+
}
|
|
3444
|
+
|
|
3445
|
+
// src/providers/pruna/client/core/utils/request-deduplicator.ts
|
|
3446
|
+
var RequestDeduplicator2 = class {
|
|
3447
|
+
pendingRequests = /* @__PURE__ */ new Map();
|
|
3448
|
+
ttl;
|
|
3449
|
+
constructor(ttl = DEFAULT_DEDUPLICATOR_TTL_MS) {
|
|
3450
|
+
this.ttl = ttl;
|
|
3451
|
+
}
|
|
3452
|
+
execute(key, fn) {
|
|
3453
|
+
const existing = this.pendingRequests.get(key);
|
|
3454
|
+
if (existing) {
|
|
3455
|
+
if (Date.now() - existing.timestamp < this.ttl) {
|
|
3456
|
+
return existing.promise;
|
|
3457
|
+
}
|
|
3458
|
+
this.pendingRequests.delete(key);
|
|
3459
|
+
}
|
|
3460
|
+
const promise = fn().finally(() => {
|
|
3461
|
+
setTimeout(() => {
|
|
3462
|
+
this.pendingRequests.delete(key);
|
|
3463
|
+
}, DEDUPLICATOR_CLEANUP_GRACE_MS);
|
|
3464
|
+
});
|
|
3465
|
+
this.pendingRequests.set(key, { promise, timestamp: Date.now() });
|
|
3466
|
+
return promise;
|
|
3467
|
+
}
|
|
3468
|
+
clear() {
|
|
3469
|
+
this.pendingRequests.clear();
|
|
3470
|
+
}
|
|
3471
|
+
size() {
|
|
3472
|
+
this.purgeExpired();
|
|
3473
|
+
return this.pendingRequests.size;
|
|
3474
|
+
}
|
|
3475
|
+
purgeExpired() {
|
|
3476
|
+
const now = Date.now();
|
|
3477
|
+
for (const [key, value] of this.pendingRequests) {
|
|
3478
|
+
if (now - value.timestamp >= this.ttl) {
|
|
3479
|
+
this.pendingRequests.delete(key);
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3482
|
+
}
|
|
3483
|
+
};
|
|
3484
|
+
var globalDeduplicator = new RequestDeduplicator2();
|
|
3485
|
+
|
|
3486
|
+
// src/providers/pruna/client/generation/builders/text-to-image.builder.ts
|
|
3487
|
+
var TextToImageInputBuilder = class {
|
|
3488
|
+
model = "p-image";
|
|
3489
|
+
build(input, _context) {
|
|
3490
|
+
const payload = {
|
|
3491
|
+
prompt: input.prompt,
|
|
3492
|
+
aspect_ratio: input.aspect_ratio ?? DEFAULT_ASPECT_RATIO
|
|
3493
|
+
};
|
|
3494
|
+
if (input.seed !== void 0) {
|
|
3495
|
+
payload.seed = input.seed;
|
|
3496
|
+
}
|
|
3497
|
+
return Promise.resolve(payload);
|
|
3498
|
+
}
|
|
3499
|
+
};
|
|
3500
|
+
|
|
3501
|
+
// src/providers/pruna/client/generation/builders/image-to-image.builder.ts
|
|
3502
|
+
var ImageToImageInputBuilder = class {
|
|
3503
|
+
model = "p-image-edit";
|
|
3504
|
+
build(input, _context) {
|
|
3505
|
+
const payload = {
|
|
3506
|
+
images: [stripBase64Prefix(input.image)],
|
|
3507
|
+
prompt: input.prompt,
|
|
3508
|
+
aspect_ratio: input.aspect_ratio ?? DEFAULT_ASPECT_RATIO
|
|
3509
|
+
};
|
|
3510
|
+
if (input.seed !== void 0) {
|
|
3511
|
+
payload.seed = input.seed;
|
|
3512
|
+
}
|
|
3513
|
+
return Promise.resolve(payload);
|
|
3514
|
+
}
|
|
3515
|
+
};
|
|
3516
|
+
|
|
3517
|
+
// src/providers/pruna/client/generation/builders/image-to-video.builder.ts
|
|
3518
|
+
var ImageToVideoInputBuilder = class {
|
|
3519
|
+
model = "p-video";
|
|
3520
|
+
async build(input, context) {
|
|
3521
|
+
if (context.signal?.aborted) {
|
|
3522
|
+
throw new CancellationError();
|
|
3523
|
+
}
|
|
3524
|
+
const fileUrl = await uploadImage(
|
|
3525
|
+
input.image,
|
|
3526
|
+
context.apiKey,
|
|
3527
|
+
context.onProgress
|
|
3528
|
+
);
|
|
3529
|
+
return {
|
|
3530
|
+
image: fileUrl,
|
|
3531
|
+
prompt: input.prompt,
|
|
3532
|
+
duration: input.duration ?? P_VIDEO_DEFAULTS.duration,
|
|
3533
|
+
resolution: input.resolution ?? P_VIDEO_DEFAULTS.resolution,
|
|
3534
|
+
fps: P_VIDEO_DEFAULTS.fps,
|
|
3535
|
+
draft: input.draft ?? P_VIDEO_DEFAULTS.draft,
|
|
3536
|
+
aspect_ratio: input.aspect_ratio ?? DEFAULT_ASPECT_RATIO,
|
|
3537
|
+
prompt_upsampling: P_VIDEO_DEFAULTS.promptUpsampling
|
|
2849
3538
|
};
|
|
2850
|
-
return;
|
|
2851
3539
|
}
|
|
2852
|
-
|
|
3540
|
+
};
|
|
3541
|
+
|
|
3542
|
+
// src/providers/pruna/client/generation/builders/model-input-builder.ts
|
|
3543
|
+
var ModelInputBuilderRegistry = class {
|
|
3544
|
+
builders = /* @__PURE__ */ new Map();
|
|
3545
|
+
register(builder) {
|
|
3546
|
+
this.builders.set(builder.model, builder);
|
|
3547
|
+
return this;
|
|
3548
|
+
}
|
|
3549
|
+
resolve(model) {
|
|
3550
|
+
const builder = this.builders.get(model);
|
|
3551
|
+
if (!builder) {
|
|
3552
|
+
throw new Error(`No input builder registered for model: ${model}`);
|
|
3553
|
+
}
|
|
3554
|
+
return builder;
|
|
3555
|
+
}
|
|
3556
|
+
};
|
|
3557
|
+
|
|
3558
|
+
// src/providers/pruna/client/generation/builders/builder-factory.ts
|
|
3559
|
+
function createDefaultBuilderRegistry() {
|
|
3560
|
+
return new ModelInputBuilderRegistry().register(new TextToImageInputBuilder()).register(new ImageToImageInputBuilder()).register(new ImageToVideoInputBuilder());
|
|
3561
|
+
}
|
|
3562
|
+
|
|
3563
|
+
// src/providers/pruna/client/generation/services/generation.service.ts
|
|
3564
|
+
var builderRegistry = createDefaultBuilderRegistry();
|
|
3565
|
+
function validateGenerateInput(input) {
|
|
3566
|
+
assertValidPrompt(input.prompt, `${input.model}.prompt`);
|
|
3567
|
+
assertValidAspectRatio(input.aspect_ratio, `${input.model}.aspect_ratio`);
|
|
3568
|
+
if (isTextToImageInput(input)) {
|
|
3569
|
+
assertValidSeed(input.seed, `${input.model}.seed`);
|
|
3570
|
+
} else if (isImageToImageInput(input)) {
|
|
3571
|
+
assertSecureImageSource(input.image, `${input.model}.image`);
|
|
3572
|
+
assertValidSeed(input.seed, `${input.model}.seed`);
|
|
3573
|
+
} else if (isImageToVideoInput(input)) {
|
|
3574
|
+
assertSecureImageSource(input.image, `${input.model}.image`);
|
|
3575
|
+
assertValidDuration(input.duration, `${input.model}.duration`);
|
|
3576
|
+
assertValidResolution(input.resolution, `${input.model}.resolution`);
|
|
3577
|
+
}
|
|
3578
|
+
}
|
|
3579
|
+
async function generate(apiKey, input, options = {}) {
|
|
3580
|
+
assertValidApiKey(apiKey);
|
|
3581
|
+
validateGenerateInput(input);
|
|
3582
|
+
const { signal, onProgress, useCache = true } = options;
|
|
3583
|
+
if (useCache) {
|
|
3584
|
+
const cacheKey = deriveGenerationCacheKey(apiKey, input);
|
|
3585
|
+
return globalDeduplicator.execute(
|
|
3586
|
+
cacheKey,
|
|
3587
|
+
() => generateInternal(apiKey, input, { signal, onProgress })
|
|
3588
|
+
);
|
|
3589
|
+
}
|
|
3590
|
+
return generateInternal(apiKey, input, { signal, onProgress });
|
|
3591
|
+
}
|
|
3592
|
+
async function generateInternal(apiKey, input, options) {
|
|
3593
|
+
const { signal, onProgress } = options;
|
|
3594
|
+
const builder = builderRegistry.resolve(input.model);
|
|
3595
|
+
const modelInput = await builder.build(input, { apiKey, signal, onProgress });
|
|
3596
|
+
const response = await submitPrediction(input.model, modelInput, apiKey, signal, onProgress);
|
|
3597
|
+
const syncUri = extractUri(response);
|
|
3598
|
+
if (syncUri) {
|
|
3599
|
+
return { url: resolveUri(syncUri), model: input.model };
|
|
3600
|
+
}
|
|
3601
|
+
const pollUrl = response.get_url ?? response.status_url;
|
|
2853
3602
|
if (!pollUrl) {
|
|
2854
|
-
throw new Error("Pruna
|
|
3603
|
+
throw new Error("Pruna API returned no result and no polling URL.");
|
|
3604
|
+
}
|
|
3605
|
+
const url = await pollForResult(
|
|
3606
|
+
pollUrl,
|
|
3607
|
+
apiKey,
|
|
3608
|
+
POLL_DEFAULTS.maxAttempts,
|
|
3609
|
+
POLL_DEFAULTS.intervalMs,
|
|
3610
|
+
signal,
|
|
3611
|
+
onProgress
|
|
3612
|
+
);
|
|
3613
|
+
return { url, model: input.model };
|
|
3614
|
+
}
|
|
3615
|
+
async function generateImageThenVideo(apiKey, input, options = {}) {
|
|
3616
|
+
assertValidApiKey(apiKey);
|
|
3617
|
+
assertValidPrompt(input.prompt, "text-to-video.prompt");
|
|
3618
|
+
assertValidAspectRatio(input.aspect_ratio, "text-to-video.aspect_ratio");
|
|
3619
|
+
assertValidDuration(input.duration, "text-to-video.duration");
|
|
3620
|
+
assertValidResolution(input.resolution, "text-to-video.resolution");
|
|
3621
|
+
const { signal, onProgress } = options;
|
|
3622
|
+
const aspectRatio = input.aspect_ratio ?? DEFAULT_ASPECT_RATIO;
|
|
3623
|
+
onProgress?.("predicting", 1);
|
|
3624
|
+
const imageResult = await generate(
|
|
3625
|
+
apiKey,
|
|
3626
|
+
{ model: "p-image", prompt: input.prompt, aspect_ratio: aspectRatio },
|
|
3627
|
+
{ signal }
|
|
3628
|
+
);
|
|
3629
|
+
onProgress?.("predicting", 2);
|
|
3630
|
+
return generate(
|
|
3631
|
+
apiKey,
|
|
3632
|
+
{
|
|
3633
|
+
model: "p-video",
|
|
3634
|
+
prompt: input.prompt,
|
|
3635
|
+
image: imageResult.url,
|
|
3636
|
+
duration: input.duration ?? P_VIDEO_DEFAULTS.duration,
|
|
3637
|
+
resolution: input.resolution ?? P_VIDEO_DEFAULTS.resolution,
|
|
3638
|
+
aspect_ratio: aspectRatio,
|
|
3639
|
+
draft: input.draft ?? P_VIDEO_DEFAULTS.draft
|
|
3640
|
+
},
|
|
3641
|
+
{ signal, onProgress }
|
|
3642
|
+
);
|
|
3643
|
+
}
|
|
3644
|
+
|
|
3645
|
+
// src/providers/pruna/pruna-generate.ts
|
|
3646
|
+
async function* generateWithPruna(request, apiKey, signal, onProgress) {
|
|
3647
|
+
const stages = [];
|
|
3648
|
+
let notify = null;
|
|
3649
|
+
let done = false;
|
|
3650
|
+
const wake = () => {
|
|
3651
|
+
const fn = notify;
|
|
3652
|
+
if (fn) {
|
|
3653
|
+
notify = null;
|
|
3654
|
+
fn();
|
|
3655
|
+
}
|
|
3656
|
+
};
|
|
3657
|
+
const handleProgress = (stage) => {
|
|
3658
|
+
stages.push({ stage });
|
|
3659
|
+
onProgress?.(stage);
|
|
3660
|
+
wake();
|
|
3661
|
+
};
|
|
3662
|
+
const promise = (async () => {
|
|
3663
|
+
try {
|
|
3664
|
+
const model = request.model ?? (request.image ? "p-image-edit" : "p-image");
|
|
3665
|
+
if (model === "p-video" && !request.image && request.prompt) {
|
|
3666
|
+
const videoRes = await generateImageThenVideo(
|
|
3667
|
+
apiKey,
|
|
3668
|
+
{
|
|
3669
|
+
prompt: request.prompt,
|
|
3670
|
+
duration: request.duration ?? 5,
|
|
3671
|
+
aspect_ratio: request.aspectRatio ?? request.aspect_ratio ?? "16:9"
|
|
3672
|
+
},
|
|
3673
|
+
{ signal, onProgress: handleProgress }
|
|
3674
|
+
);
|
|
3675
|
+
stages.push({
|
|
3676
|
+
stage: "polling",
|
|
3677
|
+
result: { url: videoRes.url, model: "p-video" }
|
|
3678
|
+
});
|
|
3679
|
+
return;
|
|
3680
|
+
}
|
|
3681
|
+
const input = {
|
|
3682
|
+
model,
|
|
3683
|
+
prompt: request.prompt ?? "",
|
|
3684
|
+
aspect_ratio: request.aspectRatio ?? request.aspect_ratio ?? "1:1",
|
|
3685
|
+
...request.image ? { image: request.image } : {},
|
|
3686
|
+
...request.duration ? { duration: request.duration } : {},
|
|
3687
|
+
...request.resolution ? { resolution: request.resolution } : {},
|
|
3688
|
+
...request.seed !== void 0 ? { seed: request.seed } : {}
|
|
3689
|
+
};
|
|
3690
|
+
const res = await generate(apiKey, input, {
|
|
3691
|
+
signal,
|
|
3692
|
+
onProgress: handleProgress
|
|
3693
|
+
});
|
|
3694
|
+
stages.push({
|
|
3695
|
+
stage: "polling",
|
|
3696
|
+
result: { url: res.url, model: res.model }
|
|
3697
|
+
});
|
|
3698
|
+
} catch (err) {
|
|
3699
|
+
stages.push({
|
|
3700
|
+
stage: "processing",
|
|
3701
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
3702
|
+
});
|
|
3703
|
+
} finally {
|
|
3704
|
+
done = true;
|
|
3705
|
+
wake();
|
|
3706
|
+
}
|
|
3707
|
+
})();
|
|
3708
|
+
while (!done || stages.length > 0) {
|
|
3709
|
+
if (stages.length === 0 && !done) {
|
|
3710
|
+
await new Promise((resolve) => {
|
|
3711
|
+
notify = resolve;
|
|
3712
|
+
});
|
|
3713
|
+
notify = null;
|
|
3714
|
+
}
|
|
3715
|
+
while (stages.length > 0) {
|
|
3716
|
+
const nextStage = stages.shift();
|
|
3717
|
+
if (!nextStage) continue;
|
|
3718
|
+
yield nextStage;
|
|
3719
|
+
if (nextStage.error) {
|
|
3720
|
+
throw nextStage.error;
|
|
3721
|
+
}
|
|
3722
|
+
}
|
|
2855
3723
|
}
|
|
2856
|
-
|
|
2857
|
-
yield { stage: "polling", result: { url: resultUrl, model: modelId } };
|
|
3724
|
+
await promise;
|
|
2858
3725
|
}
|
|
2859
3726
|
|
|
2860
3727
|
// src/providers/pruna/pruna.provider.ts
|
|
@@ -2967,6 +3834,278 @@ function createAI(config = {}) {
|
|
|
2967
3834
|
return factory;
|
|
2968
3835
|
}
|
|
2969
3836
|
|
|
3837
|
+
// src/providers/groq/groq-messages.ts
|
|
3838
|
+
var createUserMessage = (content) => ({
|
|
3839
|
+
role: "user",
|
|
3840
|
+
content
|
|
3841
|
+
});
|
|
3842
|
+
var createAssistantMessage = (content) => ({
|
|
3843
|
+
role: "assistant",
|
|
3844
|
+
content
|
|
3845
|
+
});
|
|
3846
|
+
var createSystemMessage = (content) => ({
|
|
3847
|
+
role: "system",
|
|
3848
|
+
content
|
|
3849
|
+
});
|
|
3850
|
+
var createMessage = (role, content) => ({
|
|
3851
|
+
role,
|
|
3852
|
+
content
|
|
3853
|
+
});
|
|
3854
|
+
var promptToMessages = (prompt) => [
|
|
3855
|
+
createUserMessage(prompt)
|
|
3856
|
+
];
|
|
3857
|
+
var extractTextFromMessages = (messages) => messages.map((m) => m.content).join("\n\n");
|
|
3858
|
+
var formatMessagesForDisplay = (messages) => messages.map((m) => `[${m.role.toUpperCase()}]: ${m.content}`).join("\n\n---\n\n");
|
|
3859
|
+
|
|
3860
|
+
// src/providers/groq/groq-structured-json.ts
|
|
3861
|
+
async function requestStructuredJson(client, request) {
|
|
3862
|
+
const chatRequest = {
|
|
3863
|
+
model: request.model,
|
|
3864
|
+
messages: [createUserMessage(request.instructions)],
|
|
3865
|
+
temperature: request.temperature,
|
|
3866
|
+
max_tokens: request.maxTokens,
|
|
3867
|
+
response_format: { type: "json_object" },
|
|
3868
|
+
...request.reasoningEffort !== void 0 ? { reasoning_effort: request.reasoningEffort } : {}
|
|
3869
|
+
};
|
|
3870
|
+
const response = await client.postChatCompletion(chatRequest);
|
|
3871
|
+
const content = response.choices[0]?.message?.content ?? "";
|
|
3872
|
+
const candidate = extractJsonCandidate(content);
|
|
3873
|
+
try {
|
|
3874
|
+
return JSON.parse(candidate);
|
|
3875
|
+
} catch (err) {
|
|
3876
|
+
throw new GroqError(
|
|
3877
|
+
"UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
|
|
3878
|
+
"model response contained no parsable json object",
|
|
3879
|
+
err instanceof Error ? err : void 0
|
|
3880
|
+
);
|
|
3881
|
+
}
|
|
3882
|
+
}
|
|
3883
|
+
|
|
3884
|
+
// src/providers/groq/text-generation.service.ts
|
|
3885
|
+
var STRUCTURED_OUTPUT_TEMPERATURE = 0.1;
|
|
3886
|
+
var TextGenerationService = class {
|
|
3887
|
+
constructor(httpClient, cache = new CacheManager(), defaultModel = DEFAULT_MODELS.TEXT) {
|
|
3888
|
+
this.httpClient = httpClient;
|
|
3889
|
+
this.cache = cache;
|
|
3890
|
+
this.defaultModel = defaultModel;
|
|
3891
|
+
}
|
|
3892
|
+
httpClient;
|
|
3893
|
+
cache;
|
|
3894
|
+
defaultModel;
|
|
3895
|
+
id = "groq-text-generation";
|
|
3896
|
+
name = "Groq Text Generation";
|
|
3897
|
+
capabilities = ["text"];
|
|
3898
|
+
jsonParser = new JsonResponseParser();
|
|
3899
|
+
generateCompletion(prompt, options = {}) {
|
|
3900
|
+
const model = options.model ?? this.defaultModel;
|
|
3901
|
+
return this.executeWithCache(
|
|
3902
|
+
{
|
|
3903
|
+
type: CACHE_KEY_TYPE_COMPLETION,
|
|
3904
|
+
model,
|
|
3905
|
+
prompt,
|
|
3906
|
+
config: options.generationConfig
|
|
3907
|
+
},
|
|
3908
|
+
async () => {
|
|
3909
|
+
const request = GroqChatRequestBuilder.for(model).withMessages([createUserMessage(prompt)]).withConfig(options.generationConfig).build();
|
|
3910
|
+
const response = await this.httpClient.postChatCompletion(request);
|
|
3911
|
+
return this.extractContent(response.choices[0]?.message?.content);
|
|
3912
|
+
}
|
|
3913
|
+
);
|
|
3914
|
+
}
|
|
3915
|
+
generateChatCompletion(messages, options = {}) {
|
|
3916
|
+
const model = options.model ?? this.defaultModel;
|
|
3917
|
+
return this.executeWithCache(
|
|
3918
|
+
{
|
|
3919
|
+
type: CACHE_KEY_TYPE_CHAT,
|
|
3920
|
+
model,
|
|
3921
|
+
messages: [...messages],
|
|
3922
|
+
config: options.generationConfig
|
|
3923
|
+
},
|
|
3924
|
+
async () => {
|
|
3925
|
+
const request = GroqChatRequestBuilder.for(model).withMessages(messages).withConfig(options.generationConfig).build();
|
|
3926
|
+
const response = await this.httpClient.postChatCompletion(request);
|
|
3927
|
+
return this.extractContent(response.choices[0]?.message?.content);
|
|
3928
|
+
}
|
|
3929
|
+
);
|
|
3930
|
+
}
|
|
3931
|
+
generateStructured(promptOrRequest, options) {
|
|
3932
|
+
if (typeof promptOrRequest === "string") {
|
|
3933
|
+
const model2 = options?.model ?? this.defaultModel;
|
|
3934
|
+
const jsonPrompt = StructuredPromptBuilder.for(promptOrRequest).withSchema(options?.schema).build();
|
|
3935
|
+
const request = GroqChatRequestBuilder.for(model2).withMessages([createUserMessage(jsonPrompt)]).withConfig({
|
|
3936
|
+
...options?.generationConfig,
|
|
3937
|
+
temperature: STRUCTURED_OUTPUT_TEMPERATURE
|
|
3938
|
+
}).build();
|
|
3939
|
+
return this.httpClient.postChatCompletion(request).then((response) => {
|
|
3940
|
+
const content = this.extractContent(response.choices[0]?.message?.content);
|
|
3941
|
+
return this.jsonParser.parse(content);
|
|
3942
|
+
});
|
|
3943
|
+
}
|
|
3944
|
+
const req = promptOrRequest;
|
|
3945
|
+
const model = req.model ?? (req.tier ? this.resolveModel(req.tier) : this.defaultModel);
|
|
3946
|
+
const schemaObj = req.schema;
|
|
3947
|
+
const messages = [];
|
|
3948
|
+
if (req.systemPrompt) {
|
|
3949
|
+
messages.push({ role: "system", content: req.systemPrompt });
|
|
3950
|
+
}
|
|
3951
|
+
for (const m of req.messages) {
|
|
3952
|
+
messages.push({ role: m.role, content: m.content });
|
|
3953
|
+
}
|
|
3954
|
+
const lastMsg = messages[messages.length - 1];
|
|
3955
|
+
if (lastMsg) {
|
|
3956
|
+
const structuredPrompt = StructuredPromptBuilder.for(lastMsg.content).withSchema(schemaObj).build();
|
|
3957
|
+
messages[messages.length - 1] = { ...lastMsg, content: structuredPrompt };
|
|
3958
|
+
}
|
|
3959
|
+
const groqReq = GroqChatRequestBuilder.for(model).withMessages(messages).withConfig({
|
|
3960
|
+
temperature: req.temperature ?? STRUCTURED_OUTPUT_TEMPERATURE,
|
|
3961
|
+
maxTokens: req.maxTokens
|
|
3962
|
+
}).build();
|
|
3963
|
+
return this.httpClient.postChatCompletion(groqReq).then((response) => {
|
|
3964
|
+
const content = this.extractContent(response.choices[0]?.message?.content);
|
|
3965
|
+
const parsed = this.jsonParser.parse(content);
|
|
3966
|
+
const genResult = {
|
|
3967
|
+
text: content,
|
|
3968
|
+
model: response.model,
|
|
3969
|
+
providerId: this.id,
|
|
3970
|
+
finishReason: response.choices[0]?.finish_reason ?? "stop",
|
|
3971
|
+
usage: response.usage ? {
|
|
3972
|
+
inputTokens: response.usage.prompt_tokens,
|
|
3973
|
+
outputTokens: response.usage.completion_tokens,
|
|
3974
|
+
totalTokens: response.usage.total_tokens
|
|
3975
|
+
} : void 0
|
|
3976
|
+
};
|
|
3977
|
+
return { value: parsed, result: genResult };
|
|
3978
|
+
});
|
|
3979
|
+
}
|
|
3980
|
+
streamCompletion(prompt, callbacks, options = {}) {
|
|
3981
|
+
return this.streamChatCompletion([createUserMessage(prompt)], callbacks, options);
|
|
3982
|
+
}
|
|
3983
|
+
async streamChatCompletion(messages, callbacks, options = {}) {
|
|
3984
|
+
const model = options.model ?? this.defaultModel;
|
|
3985
|
+
const request = GroqChatRequestBuilder.for(model).withMessages(messages).withConfig(options.generationConfig).withStream(true).build();
|
|
3986
|
+
const parser = new StreamingResponseParser(
|
|
3987
|
+
{
|
|
3988
|
+
onChunk: (chunk) => callbacks.onChunk?.(chunk)
|
|
3989
|
+
},
|
|
3990
|
+
extractContentFromSsePayload
|
|
3991
|
+
);
|
|
3992
|
+
try {
|
|
3993
|
+
const stream = await this.httpClient.streamChatCompletion(request, {
|
|
3994
|
+
signal: options.signal
|
|
3995
|
+
});
|
|
3996
|
+
const fullText = await parser.parse(stream);
|
|
3997
|
+
callbacks.onComplete?.(fullText);
|
|
3998
|
+
return fullText;
|
|
3999
|
+
} catch (error) {
|
|
4000
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
4001
|
+
callbacks.onError?.(err);
|
|
4002
|
+
throw err;
|
|
4003
|
+
}
|
|
4004
|
+
}
|
|
4005
|
+
clearCache() {
|
|
4006
|
+
this.cache.clear();
|
|
4007
|
+
}
|
|
4008
|
+
// --- ITextProvider implementation ---
|
|
4009
|
+
resolveModel(tier) {
|
|
4010
|
+
return GROQ_TIERS[tier] ?? this.defaultModel;
|
|
4011
|
+
}
|
|
4012
|
+
async generate(request) {
|
|
4013
|
+
const messages = [];
|
|
4014
|
+
if (request.systemPrompt) {
|
|
4015
|
+
messages.push({ role: "system", content: request.systemPrompt });
|
|
4016
|
+
}
|
|
4017
|
+
for (const m of request.messages) {
|
|
4018
|
+
messages.push({ role: m.role, content: m.content });
|
|
4019
|
+
}
|
|
4020
|
+
const model = request.model ?? (request.tier ? this.resolveModel(request.tier) : this.defaultModel);
|
|
4021
|
+
const groqReq = GroqChatRequestBuilder.for(model).withMessages(messages).withConfig({
|
|
4022
|
+
temperature: request.temperature,
|
|
4023
|
+
maxTokens: request.maxTokens
|
|
4024
|
+
}).build();
|
|
4025
|
+
const resp = await this.httpClient.postChatCompletion(groqReq);
|
|
4026
|
+
const content = resp.choices[0]?.message?.content ?? "";
|
|
4027
|
+
return {
|
|
4028
|
+
text: content,
|
|
4029
|
+
finishReason: resp.choices[0]?.finish_reason ?? "stop",
|
|
4030
|
+
model: resp.model,
|
|
4031
|
+
providerId: this.id,
|
|
4032
|
+
usage: resp.usage ? {
|
|
4033
|
+
inputTokens: resp.usage.prompt_tokens,
|
|
4034
|
+
outputTokens: resp.usage.completion_tokens,
|
|
4035
|
+
totalTokens: resp.usage.total_tokens
|
|
4036
|
+
} : void 0
|
|
4037
|
+
};
|
|
4038
|
+
}
|
|
4039
|
+
async stream(request, callbacks) {
|
|
4040
|
+
const messages = [];
|
|
4041
|
+
if (request.systemPrompt) {
|
|
4042
|
+
messages.push({ role: "system", content: request.systemPrompt });
|
|
4043
|
+
}
|
|
4044
|
+
for (const m of request.messages) {
|
|
4045
|
+
messages.push({ role: m.role, content: m.content });
|
|
4046
|
+
}
|
|
4047
|
+
const model = request.model ?? (request.tier ? this.resolveModel(request.tier) : this.defaultModel);
|
|
4048
|
+
const text = await this.streamChatCompletion(
|
|
4049
|
+
messages,
|
|
4050
|
+
{
|
|
4051
|
+
onChunk: callbacks.onChunk,
|
|
4052
|
+
onError: callbacks.onError
|
|
4053
|
+
},
|
|
4054
|
+
{
|
|
4055
|
+
model,
|
|
4056
|
+
signal: request.signal,
|
|
4057
|
+
generationConfig: {
|
|
4058
|
+
temperature: request.temperature,
|
|
4059
|
+
maxTokens: request.maxTokens
|
|
4060
|
+
}
|
|
4061
|
+
}
|
|
4062
|
+
);
|
|
4063
|
+
const result = {
|
|
4064
|
+
text,
|
|
4065
|
+
finishReason: "stop",
|
|
4066
|
+
model,
|
|
4067
|
+
providerId: this.id
|
|
4068
|
+
};
|
|
4069
|
+
callbacks.onComplete?.(result);
|
|
4070
|
+
return result;
|
|
4071
|
+
}
|
|
4072
|
+
async countTokens(text) {
|
|
4073
|
+
return Math.max(1, Math.ceil(text.length / 4));
|
|
4074
|
+
}
|
|
4075
|
+
async estimateCost(_request) {
|
|
4076
|
+
return 0;
|
|
4077
|
+
}
|
|
4078
|
+
async healthCheck() {
|
|
4079
|
+
try {
|
|
4080
|
+
await this.generateCompletion("ping", {
|
|
4081
|
+
generationConfig: { maxTokens: 1 }
|
|
4082
|
+
});
|
|
4083
|
+
return "healthy";
|
|
4084
|
+
} catch {
|
|
4085
|
+
return "unavailable";
|
|
4086
|
+
}
|
|
4087
|
+
}
|
|
4088
|
+
async executeWithCache(params, factory) {
|
|
4089
|
+
const key = this.cache.generateKey(params);
|
|
4090
|
+
const cached = this.cache.get(key);
|
|
4091
|
+
if (cached !== null) {
|
|
4092
|
+
return cached;
|
|
4093
|
+
}
|
|
4094
|
+
const result = await factory();
|
|
4095
|
+
this.cache.set(key, result);
|
|
4096
|
+
return result;
|
|
4097
|
+
}
|
|
4098
|
+
extractContent(content) {
|
|
4099
|
+
if (!content) {
|
|
4100
|
+
throw new GroqError(
|
|
4101
|
+
"UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
|
|
4102
|
+
"No content generated from Groq API"
|
|
4103
|
+
);
|
|
4104
|
+
}
|
|
4105
|
+
return content;
|
|
4106
|
+
}
|
|
4107
|
+
};
|
|
4108
|
+
|
|
2970
4109
|
// src/core/calculations/random-base36-suffix.ts
|
|
2971
4110
|
var BASE_36_RADIX = 36;
|
|
2972
4111
|
var DECIMAL_OFFSET = 2;
|
|
@@ -3194,23 +4333,53 @@ function readNumber(source, keys) {
|
|
|
3194
4333
|
return null;
|
|
3195
4334
|
}
|
|
3196
4335
|
|
|
4336
|
+
// src/providers/groq/groq-container.ts
|
|
4337
|
+
var container = null;
|
|
4338
|
+
function configureProvider(config) {
|
|
4339
|
+
const configStore = new GroqConfigStore();
|
|
4340
|
+
const transport = new GroqHttpTransport(configStore);
|
|
4341
|
+
const httpClient = new GroqHttpClient({ configStore, transport });
|
|
4342
|
+
httpClient.initialize(config);
|
|
4343
|
+
const textGeneration = new TextGenerationService(
|
|
4344
|
+
httpClient,
|
|
4345
|
+
new CacheManager(),
|
|
4346
|
+
config.textModel
|
|
4347
|
+
);
|
|
4348
|
+
const chat = new ChatService(textGeneration);
|
|
4349
|
+
container = { groqHttpClient: httpClient, textGeneration, chat };
|
|
4350
|
+
return container;
|
|
4351
|
+
}
|
|
4352
|
+
function getProvider() {
|
|
4353
|
+
return container;
|
|
4354
|
+
}
|
|
4355
|
+
function resetProvider() {
|
|
4356
|
+
container?.groqHttpClient.reset();
|
|
4357
|
+
container = null;
|
|
4358
|
+
}
|
|
4359
|
+
|
|
3197
4360
|
exports.AIError = AIError;
|
|
3198
4361
|
exports.AIErrorType = AIErrorType;
|
|
3199
4362
|
exports.ANTHROPIC_API_VERSION = ANTHROPIC_API_VERSION;
|
|
3200
4363
|
exports.ANTHROPIC_DEFAULT_MAX_TOKENS = ANTHROPIC_DEFAULT_MAX_TOKENS;
|
|
3201
4364
|
exports.ANTHROPIC_MODELS = ANTHROPIC_MODELS;
|
|
3202
4365
|
exports.ANTHROPIC_TIERS = ANTHROPIC_TIERS;
|
|
4366
|
+
exports.API_ENDPOINTS = API_ENDPOINTS;
|
|
3203
4367
|
exports.AnthropicHttpTransport = AnthropicHttpTransport;
|
|
3204
4368
|
exports.AnthropicProvider = AnthropicProvider;
|
|
3205
4369
|
exports.AnthropicRequestBuilder = AnthropicRequestBuilder;
|
|
4370
|
+
exports.AuthenticationError = AuthenticationError;
|
|
3206
4371
|
exports.BaseProvider = BaseProvider;
|
|
3207
4372
|
exports.CacheManager = CacheManager;
|
|
4373
|
+
exports.CancellationError = CancellationError;
|
|
3208
4374
|
exports.ChatIdGenerator = ChatIdGenerator;
|
|
3209
4375
|
exports.ChatService = ChatService;
|
|
3210
4376
|
exports.CompositeExecutor = CompositeExecutor;
|
|
3211
4377
|
exports.DEFAULT_API_KEY_RULE = DEFAULT_API_KEY_RULE;
|
|
4378
|
+
exports.DEFAULT_BASE_URL = DEFAULT_BASE_URL;
|
|
3212
4379
|
exports.DEFAULT_ERROR_CLASSIFICATION_RULE = DEFAULT_ERROR_CLASSIFICATION_RULE;
|
|
4380
|
+
exports.DEFAULT_GENERATION_CONFIG = DEFAULT_GENERATION_CONFIG;
|
|
3213
4381
|
exports.DEFAULT_HTTP_STATUS_RULE = DEFAULT_HTTP_STATUS_RULE;
|
|
4382
|
+
exports.DEFAULT_MODELS = DEFAULT_MODELS;
|
|
3214
4383
|
exports.DEFAULT_PROMPT_STRATEGY = DEFAULT_PROMPT_STRATEGY;
|
|
3215
4384
|
exports.DEFAULT_RETRY_BACKOFF_MULTIPLIER = DEFAULT_RETRY_BACKOFF_MULTIPLIER;
|
|
3216
4385
|
exports.DEFAULT_RETRY_INITIAL_DELAY_MS = DEFAULT_RETRY_INITIAL_DELAY_MS;
|
|
@@ -3224,7 +4393,11 @@ exports.GEMINI_TIERS = GEMINI_TIERS;
|
|
|
3224
4393
|
exports.GROQ_MODELS = GROQ_MODELS;
|
|
3225
4394
|
exports.GROQ_TIERS = GROQ_TIERS;
|
|
3226
4395
|
exports.GeminiProvider = GeminiProvider;
|
|
4396
|
+
exports.GenerationFailedError = GenerationFailedError;
|
|
3227
4397
|
exports.GroqChatRequestBuilder = GroqChatRequestBuilder;
|
|
4398
|
+
exports.GroqConfigStore = GroqConfigStore;
|
|
4399
|
+
exports.GroqError = GroqError;
|
|
4400
|
+
exports.GroqErrorType = AIErrorType;
|
|
3228
4401
|
exports.GroqHttpClient = GroqHttpClient;
|
|
3229
4402
|
exports.GroqHttpTransport = GroqHttpTransport;
|
|
3230
4403
|
exports.GroqProvider = GroqProvider;
|
|
@@ -3245,8 +4418,12 @@ exports.SSE_PREFIX = SSE_PREFIX;
|
|
|
3245
4418
|
exports.StreamingResponseParser = StreamingResponseParser;
|
|
3246
4419
|
exports.StructuredPromptBuilder = StructuredPromptBuilder;
|
|
3247
4420
|
exports.TEXT_TIERS = TEXT_TIERS;
|
|
4421
|
+
exports.TIMEOUTS = TIMEOUTS;
|
|
4422
|
+
exports.TextGenerationService = TextGenerationService;
|
|
3248
4423
|
exports.ThrottleManager = ThrottleManager;
|
|
4424
|
+
exports.TimeoutError = TimeoutError;
|
|
3249
4425
|
exports.USER_FRIENDLY_ERROR_MESSAGES = USER_FRIENDLY_ERROR_MESSAGES;
|
|
4426
|
+
exports.ValidationError = ValidationError;
|
|
3250
4427
|
exports.asObjectArray = asObjectArray;
|
|
3251
4428
|
exports.asRecord = asRecord;
|
|
3252
4429
|
exports.asStringArray = asStringArray;
|
|
@@ -3257,13 +4434,18 @@ exports.cacheManager = cacheManager;
|
|
|
3257
4434
|
exports.chatIdGenerator = chatIdGenerator;
|
|
3258
4435
|
exports.clampScore = clampScore;
|
|
3259
4436
|
exports.classifyHttpStatus = classifyHttpStatus;
|
|
4437
|
+
exports.configureProvider = configureProvider;
|
|
3260
4438
|
exports.consumeAnthropicStream = consumeAnthropicStream;
|
|
3261
4439
|
exports.consumeGroqStream = consumeGroqStream;
|
|
3262
4440
|
exports.createAI = createAI;
|
|
4441
|
+
exports.createAssistantMessage = createAssistantMessage;
|
|
3263
4442
|
exports.createFalProvider = createFalProvider;
|
|
3264
4443
|
exports.createGeminiProvider = createGeminiProvider;
|
|
4444
|
+
exports.createMessage = createMessage;
|
|
3265
4445
|
exports.createPrunaProvider = createPrunaProvider;
|
|
3266
4446
|
exports.createSseResponse = createSseResponse;
|
|
4447
|
+
exports.createSystemMessage = createSystemMessage;
|
|
4448
|
+
exports.createUserMessage = createUserMessage;
|
|
3267
4449
|
exports.debounce = debounce;
|
|
3268
4450
|
exports.debounceManager = debounceManager;
|
|
3269
4451
|
exports.defaultEffortForModel = defaultEffortForModel;
|
|
@@ -3276,9 +4458,14 @@ exports.extractJsonCandidate = extractJsonCandidate;
|
|
|
3276
4458
|
exports.extractJsonFromCodeFence = extractJsonFromCodeFence;
|
|
3277
4459
|
exports.extractJsonObject = extractJsonObject;
|
|
3278
4460
|
exports.extractSseDataPayload = extractSseDataPayload;
|
|
4461
|
+
exports.extractTextFromMessages = extractTextFromMessages;
|
|
3279
4462
|
exports.extractTokenUsage = extractTokenUsage;
|
|
3280
4463
|
exports.formatErrorForLogging = formatErrorForLogging;
|
|
4464
|
+
exports.formatMessagesForDisplay = formatMessagesForDisplay;
|
|
4465
|
+
exports.generateImageThenVideo = generateImageThenVideo;
|
|
4466
|
+
exports.generatePruna = generate;
|
|
3281
4467
|
exports.generateWithPruna = generateWithPruna;
|
|
4468
|
+
exports.getProvider = getProvider;
|
|
3282
4469
|
exports.isAuthErrorType = isAuthErrorType;
|
|
3283
4470
|
exports.isMediaProvider = isMediaProvider;
|
|
3284
4471
|
exports.isRetryableErrorType = isRetryableErrorType;
|
|
@@ -3292,17 +4479,23 @@ exports.mergeTierOverrides = mergeTierOverrides;
|
|
|
3292
4479
|
exports.messageFormatter = messageFormatter;
|
|
3293
4480
|
exports.normalizeSseChunk = normalizeSseChunk;
|
|
3294
4481
|
exports.oneOfCatalog = oneOfCatalog;
|
|
4482
|
+
exports.pollForResult = pollForResult;
|
|
4483
|
+
exports.promptToMessages = promptToMessages;
|
|
3295
4484
|
exports.repairCommonJsonDefects = repairCommonJsonDefects;
|
|
3296
4485
|
exports.requestDeduplicator = requestDeduplicator;
|
|
3297
4486
|
exports.requestQueue = requestQueue;
|
|
4487
|
+
exports.requestStructuredJson = requestStructuredJson;
|
|
4488
|
+
exports.resetProvider = resetProvider;
|
|
3298
4489
|
exports.resolveModel = resolveModel;
|
|
3299
4490
|
exports.resolveUserFriendlyErrorMessage = resolveUserFriendlyErrorMessage;
|
|
3300
4491
|
exports.resolveUserFriendlyMessage = resolveUserFriendlyMessage;
|
|
3301
4492
|
exports.retryManager = retryManager;
|
|
3302
4493
|
exports.splitSseLines = splitSseLines;
|
|
4494
|
+
exports.submitPrediction = submitPrediction;
|
|
3303
4495
|
exports.throttle = throttle;
|
|
3304
4496
|
exports.throttleManager = throttleManager;
|
|
3305
4497
|
exports.toGroqMessages = toGroqMessages;
|
|
4498
|
+
exports.uploadImage = uploadImage;
|
|
3306
4499
|
exports.validateApiKey = validateApiKey;
|
|
3307
4500
|
exports.withRetry = withRetry;
|
|
3308
4501
|
//# sourceMappingURL=index.cjs.map
|