@happyvertical/ai 0.102.7 → 0.103.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENT.md +3 -2
- package/README.md +123 -4
- package/dist/chunks/{anthropic-CxOrttgj.js → anthropic-CdbK9Jl-.js} +3 -3
- package/dist/chunks/{anthropic-CxOrttgj.js.map → anthropic-CdbK9Jl-.js.map} +1 -1
- package/dist/chunks/{bedrock-B0cNEXFa.js → bedrock-DNkxz3ow.js} +3 -3
- package/dist/chunks/{bedrock-B0cNEXFa.js.map → bedrock-DNkxz3ow.js.map} +1 -1
- package/dist/chunks/{bifrost-BXiXdyb8.js → bifrost-DmbaXObX.js} +4 -4
- package/dist/chunks/{bifrost-BXiXdyb8.js.map → bifrost-DmbaXObX.js.map} +1 -1
- package/dist/chunks/{byteplus-modelark-DZ0YL2vl.js → byteplus-modelark-BAi0y9SK.js} +3 -3
- package/dist/chunks/{byteplus-modelark-DZ0YL2vl.js.map → byteplus-modelark-BAi0y9SK.js.map} +1 -1
- package/dist/chunks/{claude-cli-CtBp-Uk-.js → claude-cli-C40pIzrp.js} +3 -3
- package/dist/chunks/{claude-cli-CtBp-Uk-.js.map → claude-cli-C40pIzrp.js.map} +1 -1
- package/dist/chunks/{continuation-7o7400bp.js → continuation-L5KnnE9R.js} +2 -2
- package/dist/chunks/{continuation-7o7400bp.js.map → continuation-L5KnnE9R.js.map} +1 -1
- package/dist/chunks/{gateway-audio-models-BSZGgjd8.js → gateway-audio-models-B1tGNv1H.js} +2 -2
- package/dist/chunks/{gateway-audio-models-BSZGgjd8.js.map → gateway-audio-models-B1tGNv1H.js.map} +1 -1
- package/dist/chunks/{gemini-DUk4ssth.js → gemini-C-r6c4N9.js} +3 -3
- package/dist/chunks/{gemini-DUk4ssth.js.map → gemini-C-r6c4N9.js.map} +1 -1
- package/dist/chunks/{huggingface-C41BZOoI.js → huggingface-DtuYF1vb.js} +3 -3
- package/dist/chunks/{huggingface-C41BZOoI.js.map → huggingface-DtuYF1vb.js.map} +1 -1
- package/dist/chunks/laya-D-P82D0h.js +471 -0
- package/dist/chunks/laya-D-P82D0h.js.map +1 -0
- package/dist/chunks/{litellm-rMCXClTb.js → litellm-BaGgWzHR.js} +4 -4
- package/dist/chunks/{litellm-rMCXClTb.js.map → litellm-BaGgWzHR.js.map} +1 -1
- package/dist/chunks/{ollama--y88p8Bz.js → ollama-C3T3mn68.js} +4 -4
- package/dist/chunks/{ollama--y88p8Bz.js.map → ollama-C3T3mn68.js.map} +1 -1
- package/dist/chunks/{openai-compat-video-erarPEz-.js → openai-compat-video-CwFsKAxR.js} +3 -3
- package/dist/chunks/{openai-compat-video-erarPEz-.js.map → openai-compat-video-CwFsKAxR.js.map} +1 -1
- package/dist/chunks/{qwen-tts-C9w7teY6.js → qwen-tts-DqMxfA54.js} +2 -2
- package/dist/chunks/{qwen-tts-C9w7teY6.js.map → qwen-tts-DqMxfA54.js.map} +1 -1
- package/dist/chunks/{rate-limit-Bln_w0rH.js → rate-limit-BkK4-hXY.js} +2 -2
- package/dist/chunks/{rate-limit-Bln_w0rH.js.map → rate-limit-BkK4-hXY.js.map} +1 -1
- package/dist/chunks/{seevio-CosohVa0.js → seevio-EzC6eKz-.js} +3 -3
- package/dist/chunks/{seevio-CosohVa0.js.map → seevio-EzC6eKz-.js.map} +1 -1
- package/dist/chunks/{src-nB7u5Xl0.js → src-Cc5yFnnQ.js} +25 -18
- package/dist/chunks/{src-nB7u5Xl0.js.map → src-Cc5yFnnQ.js.map} +1 -1
- package/dist/chunks/{typesafe-BNM-QAJ2.js → typesafe-D7ZmI9-n.js} +3 -3
- package/dist/chunks/{typesafe-BNM-QAJ2.js.map → typesafe-D7ZmI9-n.js.map} +1 -1
- package/dist/chunks/{usage-BaQKo8bc.js → usage-B9gPCWSa.js} +2 -1
- package/dist/chunks/usage-B9gPCWSa.js.map +1 -0
- package/dist/chunks/{webllm-CWgY5nnP.js → webllm-DT31sqUY.js} +2 -2
- package/dist/chunks/{webllm-CWgY5nnP.js.map → webllm-DT31sqUY.js.map} +1 -1
- package/dist/index.js +2 -2
- package/dist/local.js +2 -2
- package/dist/node/factory.d.ts.map +1 -1
- package/dist/node.js +16 -3
- package/dist/node.js.map +1 -1
- package/dist/shared/factory.d.ts.map +1 -1
- package/dist/shared/providers/laya.d.ts +46 -0
- package/dist/shared/providers/laya.d.ts.map +1 -0
- package/dist/shared/types.d.ts +37 -3
- package/dist/shared/types.d.ts.map +1 -1
- package/metadata.json +2 -2
- package/package.json +4 -3
- package/dist/chunks/usage-BaQKo8bc.js.map +0 -1
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
import { f as AuthenticationError, l as prepareRequestControls, o as normalizeBaseAIOptions, t as emitUsage, u as AIError } from "./usage-B9gPCWSa.js";
|
|
2
|
+
import { n as rateLimitErrorFrom } from "./rate-limit-BkK4-hXY.js";
|
|
3
|
+
import { ValidationError } from "@happyvertical/utils";
|
|
4
|
+
//#region src/shared/providers/laya.ts
|
|
5
|
+
/**
|
|
6
|
+
* Laya typed-decision provider.
|
|
7
|
+
*
|
|
8
|
+
* Laya (https://github.com/NandhaKishorM/laya) is a self-hosted,
|
|
9
|
+
* non-autoregressive decision engine. Its `laya-serve` HTTP server answers the
|
|
10
|
+
* Jev `POST /v1/systemone` wire shape, with differences this adapter handles
|
|
11
|
+
* itself rather than inheriting from the TypeSafe adapter: optional auth, a
|
|
12
|
+
* checkpoint chosen by the server's router, a `max_len` token window,
|
|
13
|
+
* probabilities rounded to four decimals one at a time, and truncation facts.
|
|
14
|
+
*
|
|
15
|
+
* The public SDK contract calls Laya's `noul` primitive a `predicate`; the
|
|
16
|
+
* server's vocabulary stays confined to this wire translation.
|
|
17
|
+
*/
|
|
18
|
+
var PROVIDER = "laya";
|
|
19
|
+
/** Distributions this close to unit mass are used exactly as received. */
|
|
20
|
+
var STRICT_DISTRIBUTION_EPSILON = 1e-6;
|
|
21
|
+
/**
|
|
22
|
+
* `laya-serve` rounds every probability to four decimals on its own, so each
|
|
23
|
+
* value is off by at most half of the last place and a distribution of `n`
|
|
24
|
+
* values can be off by `n * 0.00005`.
|
|
25
|
+
*/
|
|
26
|
+
var ROUNDING_ERROR_PER_VALUE = 5e-5;
|
|
27
|
+
/** No rounding story justifies renormalizing a distribution further out. */
|
|
28
|
+
var MAX_NORMALIZED_ERROR = .01;
|
|
29
|
+
function isRecord(value) {
|
|
30
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
31
|
+
}
|
|
32
|
+
function invalid(message) {
|
|
33
|
+
return new AIError(`Laya response ${message}`, "INVALID_RESPONSE", PROVIDER);
|
|
34
|
+
}
|
|
35
|
+
function finite(value, field) {
|
|
36
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw invalid(`${field} must be finite`);
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
function probability(value, field) {
|
|
40
|
+
const result = finite(value, field);
|
|
41
|
+
if (result < 0 || result > 1) throw invalid(`${field} must be between 0 and 1`);
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
function tokenCount(value, field) {
|
|
45
|
+
const result = finite(value, field);
|
|
46
|
+
if (!Number.isSafeInteger(result) || result < 0) throw invalid(`${field} must be a non-negative integer`);
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
function optionalString(source, key, field) {
|
|
50
|
+
const value = source[key];
|
|
51
|
+
if (value === void 0) return void 0;
|
|
52
|
+
if (typeof value !== "string" || !value) throw invalid(`${field} must be a non-empty string`);
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
function sameDecisionValue(left, right) {
|
|
56
|
+
if (left === right) return true;
|
|
57
|
+
if (Array.isArray(left)) return Array.isArray(right) && left.length === right.length && left.every((value, index) => sameDecisionValue(value, right[index]));
|
|
58
|
+
if (isRecord(left)) return isRecord(right) && Object.keys(left).length === Object.keys(right).length && Object.entries(left).every(([key, value]) => key in right && sameDecisionValue(value, right[key]));
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* `laya-serve` returns a rubric level in `legend` as sent when it is a string
|
|
63
|
+
* and as Python's `json.dumps` text when it is anything else.
|
|
64
|
+
*/
|
|
65
|
+
function legendMatches(level, legend) {
|
|
66
|
+
if (sameDecisionValue(level, legend)) return true;
|
|
67
|
+
if (typeof level === "string" || typeof legend !== "string") return false;
|
|
68
|
+
try {
|
|
69
|
+
return sameDecisionValue(level, JSON.parse(legend));
|
|
70
|
+
} catch {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function validateValue(value, field) {
|
|
75
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return;
|
|
76
|
+
if (typeof value === "number") {
|
|
77
|
+
if (!Number.isFinite(value)) throw new ValidationError(`${field} must contain finite numbers`, { provider: PROVIDER });
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (Array.isArray(value)) {
|
|
81
|
+
value.forEach((item, index) => {
|
|
82
|
+
validateValue(item, `${field}[${index}]`);
|
|
83
|
+
});
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (isRecord(value)) {
|
|
87
|
+
Object.entries(value).forEach(([key, item]) => {
|
|
88
|
+
validateValue(item, `${field}.${key}`);
|
|
89
|
+
});
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
throw new ValidationError(`${field} must be JSON-compatible`, { provider: PROVIDER });
|
|
93
|
+
}
|
|
94
|
+
function validateMaxLen(value, field) {
|
|
95
|
+
if (value === void 0) return void 0;
|
|
96
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0) throw new ValidationError(`${field} must be a positive integer`, { provider: PROVIDER });
|
|
97
|
+
return value;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Checks the provider-neutral request shape. Laya's own size limits (questions
|
|
101
|
+
* per request, options per question, state length) are left to the server,
|
|
102
|
+
* which reports them as explicit 413 errors.
|
|
103
|
+
*/
|
|
104
|
+
function validateRequest(request) {
|
|
105
|
+
if (request.state === null || request.state === void 0) throw new ValidationError("state is required", { provider: PROVIDER });
|
|
106
|
+
validateValue(request.state, "state");
|
|
107
|
+
const entries = Object.entries(request.questions || {});
|
|
108
|
+
if (entries.length === 0) throw new ValidationError("questions must not be empty", { provider: PROVIDER });
|
|
109
|
+
for (const [id, question] of entries) {
|
|
110
|
+
if (!id.trim()) throw new ValidationError("question IDs must not be empty", { provider: PROVIDER });
|
|
111
|
+
validateValue(question.instructions, `questions.${id}.instructions`);
|
|
112
|
+
if (question.type === "predicate") {
|
|
113
|
+
if (question.criteria) for (const [key, value] of Object.entries(question.criteria)) validateValue(value, `questions.${id}.criteria.${key}`);
|
|
114
|
+
} else if (question.type === "choice") {
|
|
115
|
+
if (Object.keys(question.criteria || {}).length === 0) throw new ValidationError(`questions.${id}.criteria must contain at least one option`, { provider: PROVIDER });
|
|
116
|
+
for (const [key, value] of Object.entries(question.criteria)) if (value !== null) validateValue(value, `questions.${id}.criteria.${key}`);
|
|
117
|
+
} else if (question.type === "score") {
|
|
118
|
+
if (!Array.isArray(question.criteria) || question.criteria.length < 2) throw new ValidationError(`questions.${id}.criteria must contain at least 2 ordered levels`, { provider: PROVIDER });
|
|
119
|
+
question.criteria.forEach((value, index) => {
|
|
120
|
+
if (value === null) throw new ValidationError(`questions.${id}.criteria[${index}] must describe the level`, { provider: PROVIDER });
|
|
121
|
+
validateValue(value, `questions.${id}.criteria[${index}]`);
|
|
122
|
+
});
|
|
123
|
+
} else throw new ValidationError(`questions.${id}.type is unsupported`, { provider: PROVIDER });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Builds each wire question field by field so unknown properties on a caller's
|
|
128
|
+
* object (for example a generation control) are never forwarded.
|
|
129
|
+
*/
|
|
130
|
+
function wireQuestion(question) {
|
|
131
|
+
if (question.type === "predicate") return {
|
|
132
|
+
type: "noul",
|
|
133
|
+
instructions: question.instructions,
|
|
134
|
+
...question.criteria ? { criteria: question.criteria } : {}
|
|
135
|
+
};
|
|
136
|
+
if (question.type === "choice") return {
|
|
137
|
+
type: "choice",
|
|
138
|
+
instructions: question.instructions,
|
|
139
|
+
criteria: question.criteria
|
|
140
|
+
};
|
|
141
|
+
return {
|
|
142
|
+
type: "score",
|
|
143
|
+
instructions: question.instructions,
|
|
144
|
+
criteria: question.criteria
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
/** Resolves the server root from a base URL that may carry the API path. */
|
|
148
|
+
function serverRoot(value) {
|
|
149
|
+
const text = (value || "").trim();
|
|
150
|
+
if (!text) throw new ValidationError("Laya requires baseUrl, for example http://localhost:8000", { provider: PROVIDER });
|
|
151
|
+
let url;
|
|
152
|
+
try {
|
|
153
|
+
url = new URL(text);
|
|
154
|
+
} catch {
|
|
155
|
+
throw new ValidationError("Laya baseUrl must be an absolute http(s) URL", { provider: PROVIDER });
|
|
156
|
+
}
|
|
157
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") throw new ValidationError("Laya baseUrl must be an absolute http(s) URL", { provider: PROVIDER });
|
|
158
|
+
if (url.search || url.hash || url.username || url.password) throw new ValidationError("Laya baseUrl must not contain credentials, a query, or a fragment", { provider: PROVIDER });
|
|
159
|
+
const path = url.pathname.replace(/\/+$/, "").replace(/\/v1(\/systemone)?$/, "");
|
|
160
|
+
return `${url.origin}${path}`;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Validates a choice or score distribution. Mass within the four-decimal
|
|
164
|
+
* rounding bound is normalized by its actual sum; anything else is rejected.
|
|
165
|
+
*/
|
|
166
|
+
function distribution(value, expected, field) {
|
|
167
|
+
if (!isRecord(value) || Object.keys(value).length !== expected.length || expected.some((key) => !(key in value))) throw invalid(`${field} must cover exactly the requested values`);
|
|
168
|
+
const values = Object.fromEntries(expected.map((key) => [key, probability(value[key], `${field}.${key}`)]));
|
|
169
|
+
const sum = Object.values(values).reduce((total, item) => total + item, 0);
|
|
170
|
+
const drift = Math.abs(sum - 1);
|
|
171
|
+
if (drift <= STRICT_DISTRIBUTION_EPSILON) return { values };
|
|
172
|
+
const allowed = Math.min(expected.length * ROUNDING_ERROR_PER_VALUE, MAX_NORMALIZED_ERROR) + STRICT_DISTRIBUTION_EPSILON;
|
|
173
|
+
if (sum <= 0 || drift > allowed) throw invalid(`${field} must sum to 1`);
|
|
174
|
+
return {
|
|
175
|
+
values: Object.fromEntries(Object.entries(values).map(([key, item]) => [key, item / sum])),
|
|
176
|
+
raw: values
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function answer(question, raw, id, renormalized) {
|
|
180
|
+
if (!isRecord(raw)) throw invalid(`answers.${id} must be an object`);
|
|
181
|
+
if (question.type === "predicate") {
|
|
182
|
+
if (raw.type !== "noul") throw invalid(`answers.${id} type does not match predicate`);
|
|
183
|
+
return {
|
|
184
|
+
type: "predicate",
|
|
185
|
+
probability: probability(raw.noul, `answers.${id}.noul`)
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
if (raw.type !== question.type) throw invalid(`answers.${id} type does not match request`);
|
|
189
|
+
if (question.type === "choice") {
|
|
190
|
+
const choices = Object.keys(question.criteria);
|
|
191
|
+
if (typeof raw.choice !== "string" || !choices.includes(raw.choice)) throw invalid(`answers.${id}.choice is not a requested option`);
|
|
192
|
+
const probabilities = distribution(raw.probabilities, choices, `answers.${id}.probabilities`);
|
|
193
|
+
if (probabilities.raw) renormalized[id] = probabilities.raw;
|
|
194
|
+
return {
|
|
195
|
+
type: "choice",
|
|
196
|
+
choice: raw.choice,
|
|
197
|
+
probabilities: probabilities.values,
|
|
198
|
+
confidence: probability(raw.confidence, `answers.${id}.confidence`)
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
const levels = question.criteria;
|
|
202
|
+
const levelKeys = levels.map((_, index) => String(index));
|
|
203
|
+
const score = finite(raw.score, `answers.${id}.score`);
|
|
204
|
+
if (score < 0 || score > levels.length - 1) throw invalid(`answers.${id}.score is outside the requested rubric`);
|
|
205
|
+
const legend = raw.legend;
|
|
206
|
+
if (!isRecord(legend) || levelKeys.some((key, index) => !legendMatches(levels[index], legend[key]))) throw invalid(`answers.${id}.legend does not match the requested rubric`);
|
|
207
|
+
const probabilities = distribution(raw.probabilities, levelKeys, `answers.${id}.probabilities`);
|
|
208
|
+
if (probabilities.raw) renormalized[id] = probabilities.raw;
|
|
209
|
+
return {
|
|
210
|
+
type: "score",
|
|
211
|
+
score,
|
|
212
|
+
probabilities: probabilities.values,
|
|
213
|
+
confidence: probability(raw.confidence, `answers.${id}.confidence`),
|
|
214
|
+
levels
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
/** A decision-only adapter for a `laya-serve` `/v1/systemone` endpoint. */
|
|
218
|
+
var LayaProvider = class {
|
|
219
|
+
options;
|
|
220
|
+
root;
|
|
221
|
+
maxLen;
|
|
222
|
+
constructor(options) {
|
|
223
|
+
this.root = serverRoot(options.baseUrl);
|
|
224
|
+
this.maxLen = validateMaxLen(options.maxLen, "maxLen");
|
|
225
|
+
this.options = normalizeBaseAIOptions({
|
|
226
|
+
...options,
|
|
227
|
+
baseUrl: this.root
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
async decide(request, options = {}) {
|
|
231
|
+
validateRequest(request);
|
|
232
|
+
const maxLen = validateMaxLen(options.maxLen, "maxLen") ?? this.maxLen;
|
|
233
|
+
const controls = prepareRequestControls(this.options, options);
|
|
234
|
+
const startedAt = Date.now();
|
|
235
|
+
const requestedModel = options.model || this.options.defaultModel || void 0;
|
|
236
|
+
const wire = {
|
|
237
|
+
state: request.state,
|
|
238
|
+
questions: Object.fromEntries(Object.entries(request.questions).map(([id, question]) => [id, wireQuestion(question)])),
|
|
239
|
+
...requestedModel ? { model: requestedModel } : {},
|
|
240
|
+
...maxLen !== void 0 ? { max_len: maxLen } : {}
|
|
241
|
+
};
|
|
242
|
+
try {
|
|
243
|
+
const response = await fetch(`${this.root}/v1/systemone`, {
|
|
244
|
+
method: "POST",
|
|
245
|
+
redirect: "error",
|
|
246
|
+
headers: {
|
|
247
|
+
...this.options.apiKey ? { Authorization: `Bearer ${this.options.apiKey}` } : {},
|
|
248
|
+
"Content-Type": "application/json",
|
|
249
|
+
...this.options.headers
|
|
250
|
+
},
|
|
251
|
+
body: JSON.stringify(wire),
|
|
252
|
+
signal: controls.signal
|
|
253
|
+
});
|
|
254
|
+
if (!response.ok) {
|
|
255
|
+
if (response.status === 401) throw new AuthenticationError(PROVIDER);
|
|
256
|
+
const text = await response.text();
|
|
257
|
+
if (response.status === 429 || response.status === 503) throw rateLimitErrorFrom(PROVIDER, {
|
|
258
|
+
headers: response.headers,
|
|
259
|
+
message: text
|
|
260
|
+
}, { model: requestedModel });
|
|
261
|
+
throw new AIError(`Laya request failed (${response.status}): ${text.slice(0, 500)}`, "API_ERROR", PROVIDER, requestedModel, response.status >= 500);
|
|
262
|
+
}
|
|
263
|
+
let body;
|
|
264
|
+
try {
|
|
265
|
+
body = await response.json();
|
|
266
|
+
} catch (error) {
|
|
267
|
+
if (error instanceof SyntaxError) throw new AIError("Laya response contained invalid JSON", "INVALID_RESPONSE", PROVIDER, requestedModel);
|
|
268
|
+
throw error;
|
|
269
|
+
}
|
|
270
|
+
return this.result(request, body, requestedModel, maxLen, options, startedAt);
|
|
271
|
+
} catch (error) {
|
|
272
|
+
if (controls.didTimeout()) throw new AIError(`Laya request timed out after ${controls.timeout}ms`, "REQUEST_TIMEOUT", PROVIDER, requestedModel, true);
|
|
273
|
+
if (controls.signal.aborted && !(error instanceof AIError)) throw new AIError("Laya request was aborted", "REQUEST_ABORTED", PROVIDER, requestedModel);
|
|
274
|
+
if (!(error instanceof AIError)) throw new AIError(`Network error calling Laya: ${error instanceof Error ? error.message : String(error)}`, "NETWORK_ERROR", PROVIDER, requestedModel, true);
|
|
275
|
+
throw error;
|
|
276
|
+
} finally {
|
|
277
|
+
controls.cleanup();
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
result(request, body, requestedModel, maxLen, options, startedAt) {
|
|
281
|
+
if (!isRecord(body)) throw invalid("must be an object");
|
|
282
|
+
if (typeof body.model !== "string" || !body.model) throw invalid("model is required");
|
|
283
|
+
if (!isRecord(body.answers)) throw invalid("answers is required");
|
|
284
|
+
const responseAnswers = body.answers;
|
|
285
|
+
const ids = Object.keys(request.questions);
|
|
286
|
+
if (Object.keys(responseAnswers).length !== ids.length || ids.some((id) => !(id in responseAnswers))) throw invalid("answers must match requested question IDs");
|
|
287
|
+
const renormalized = {};
|
|
288
|
+
const answers = Object.fromEntries(ids.map((id) => [id, answer(request.questions[id], responseAnswers[id], id, renormalized)]));
|
|
289
|
+
let usage;
|
|
290
|
+
const facts = {};
|
|
291
|
+
if (body.usage !== void 0) {
|
|
292
|
+
const wireUsage = body.usage;
|
|
293
|
+
if (!isRecord(wireUsage) || !("input_tokens" in wireUsage) || !("output_tokens" in wireUsage)) throw invalid("usage must include input_tokens and output_tokens");
|
|
294
|
+
const input = tokenCount(wireUsage.input_tokens, "usage.input_tokens");
|
|
295
|
+
const output = tokenCount(wireUsage.output_tokens, "usage.output_tokens");
|
|
296
|
+
usage = {
|
|
297
|
+
promptTokens: input,
|
|
298
|
+
completionTokens: output,
|
|
299
|
+
totalTokens: input + output
|
|
300
|
+
};
|
|
301
|
+
for (const [key, name] of [["state_tokens", "stateTokens"], ["state_tokens_dropped", "stateTokensDropped"]]) if (wireUsage[key] !== void 0) facts[name] = tokenCount(wireUsage[key], `usage.${key}`);
|
|
302
|
+
if (wireUsage.truncated !== void 0) {
|
|
303
|
+
if (typeof wireUsage.truncated !== "boolean") throw invalid("usage.truncated must be a boolean");
|
|
304
|
+
facts.truncated = wireUsage.truncated;
|
|
305
|
+
}
|
|
306
|
+
if (wireUsage.truncated_questions !== void 0) {
|
|
307
|
+
const truncated = wireUsage.truncated_questions;
|
|
308
|
+
if (!Array.isArray(truncated) || truncated.some((id) => typeof id !== "string")) throw invalid("usage.truncated_questions must list question IDs");
|
|
309
|
+
facts.truncatedQuestions = truncated;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
let checkpoint;
|
|
313
|
+
if (body.routing !== void 0) {
|
|
314
|
+
if (!isRecord(body.routing)) throw invalid("routing must be an object");
|
|
315
|
+
checkpoint = optionalString(body.routing, "model", "routing.model");
|
|
316
|
+
const repo = optionalString(body.routing, "repo", "routing.repo");
|
|
317
|
+
const reason = optionalString(body.routing, "reason", "routing.reason");
|
|
318
|
+
if (checkpoint) facts.checkpoint = checkpoint;
|
|
319
|
+
if (repo) facts.checkpointRepo = repo;
|
|
320
|
+
if (reason) facts.routeReason = reason;
|
|
321
|
+
}
|
|
322
|
+
const model = checkpoint || body.model;
|
|
323
|
+
const renormalizedIds = Object.keys(renormalized);
|
|
324
|
+
const details = {
|
|
325
|
+
serverModel: body.model,
|
|
326
|
+
...requestedModel ? { requestedModel } : {},
|
|
327
|
+
...maxLen !== void 0 ? { maxLen } : {},
|
|
328
|
+
...facts,
|
|
329
|
+
...renormalizedIds.length ? {
|
|
330
|
+
renormalized: renormalizedIds,
|
|
331
|
+
rawProbabilities: renormalized
|
|
332
|
+
} : {}
|
|
333
|
+
};
|
|
334
|
+
emitUsage(this.options, PROVIDER, "decide", model, usage, startedAt, options.usageTags);
|
|
335
|
+
return {
|
|
336
|
+
model,
|
|
337
|
+
...usage ? { usage } : {},
|
|
338
|
+
provenance: {
|
|
339
|
+
provider: PROVIDER,
|
|
340
|
+
model,
|
|
341
|
+
details
|
|
342
|
+
},
|
|
343
|
+
answers
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
async getModels() {
|
|
347
|
+
const checkpoints = [
|
|
348
|
+
[
|
|
349
|
+
"typed-decisions",
|
|
350
|
+
"Laya typed decisions (421M)",
|
|
351
|
+
1024
|
|
352
|
+
],
|
|
353
|
+
[
|
|
354
|
+
"english",
|
|
355
|
+
"Laya English (421M)",
|
|
356
|
+
512
|
|
357
|
+
],
|
|
358
|
+
[
|
|
359
|
+
"multilingual",
|
|
360
|
+
"Laya multilingual (322M)",
|
|
361
|
+
1024
|
|
362
|
+
]
|
|
363
|
+
];
|
|
364
|
+
const configured = this.options.defaultModel;
|
|
365
|
+
const models = checkpoints.map(([id, name, contextLength]) => ({
|
|
366
|
+
id,
|
|
367
|
+
name,
|
|
368
|
+
description: "Laya typed decisions",
|
|
369
|
+
contextLength,
|
|
370
|
+
capabilities: ["decisions"],
|
|
371
|
+
supportsFunctions: false,
|
|
372
|
+
supportsVision: false
|
|
373
|
+
}));
|
|
374
|
+
if (configured && !models.some((model) => model.id === configured)) models.push({
|
|
375
|
+
id: configured,
|
|
376
|
+
name: configured,
|
|
377
|
+
description: "Laya typed decisions (configured checkpoint)",
|
|
378
|
+
contextLength: 0,
|
|
379
|
+
capabilities: ["decisions"],
|
|
380
|
+
supportsFunctions: false,
|
|
381
|
+
supportsVision: false
|
|
382
|
+
});
|
|
383
|
+
return models;
|
|
384
|
+
}
|
|
385
|
+
async getCapabilities() {
|
|
386
|
+
return {
|
|
387
|
+
decisions: true,
|
|
388
|
+
chat: false,
|
|
389
|
+
completion: false,
|
|
390
|
+
embeddings: false,
|
|
391
|
+
streaming: false,
|
|
392
|
+
functions: false,
|
|
393
|
+
vision: false,
|
|
394
|
+
fineTuning: false,
|
|
395
|
+
imageEmbeddings: false,
|
|
396
|
+
imageGeneration: false,
|
|
397
|
+
videoGeneration: false,
|
|
398
|
+
tts: false,
|
|
399
|
+
voiceCloning: false,
|
|
400
|
+
voiceDesign: false,
|
|
401
|
+
maxContextLength: 0,
|
|
402
|
+
supportedOperations: ["decide"]
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
unsupported() {
|
|
406
|
+
throw new AIError("This operation is not supported by the Laya decision-only provider.", "NOT_IMPLEMENTED", PROVIDER);
|
|
407
|
+
}
|
|
408
|
+
async chat(_a, _b) {
|
|
409
|
+
return this.unsupported();
|
|
410
|
+
}
|
|
411
|
+
async complete(_a, _b) {
|
|
412
|
+
return this.unsupported();
|
|
413
|
+
}
|
|
414
|
+
async message(_a, _b) {
|
|
415
|
+
return this.unsupported();
|
|
416
|
+
}
|
|
417
|
+
async embed(_a, _b) {
|
|
418
|
+
return this.unsupported();
|
|
419
|
+
}
|
|
420
|
+
async embedImage(_a, _b) {
|
|
421
|
+
return this.unsupported();
|
|
422
|
+
}
|
|
423
|
+
async describeImage(_a, _b, _c) {
|
|
424
|
+
return this.unsupported();
|
|
425
|
+
}
|
|
426
|
+
async generateImage(_a, _b) {
|
|
427
|
+
return this.unsupported();
|
|
428
|
+
}
|
|
429
|
+
stream(_a, _b) {
|
|
430
|
+
const error = new AIError("Chat streaming is not supported by the Laya decision-only provider.", "NOT_IMPLEMENTED", PROVIDER);
|
|
431
|
+
return { [Symbol.asyncIterator]: () => ({ next: () => Promise.reject(error) }) };
|
|
432
|
+
}
|
|
433
|
+
async countTokens(_a) {
|
|
434
|
+
return this.unsupported();
|
|
435
|
+
}
|
|
436
|
+
async submitVideoGenerationJob(_a) {
|
|
437
|
+
return this.unsupported();
|
|
438
|
+
}
|
|
439
|
+
async getVideoGenerationJob(_a) {
|
|
440
|
+
return this.unsupported();
|
|
441
|
+
}
|
|
442
|
+
async fetchVideoGenerationResult(_a) {
|
|
443
|
+
return this.unsupported();
|
|
444
|
+
}
|
|
445
|
+
async cancelVideoGenerationJob(_a) {
|
|
446
|
+
return this.unsupported();
|
|
447
|
+
}
|
|
448
|
+
async validateVideoGenerationAccess() {
|
|
449
|
+
return this.unsupported();
|
|
450
|
+
}
|
|
451
|
+
async synthesizeSpeech(_a, _b) {
|
|
452
|
+
return this.unsupported();
|
|
453
|
+
}
|
|
454
|
+
streamSpeech(_a, _b) {
|
|
455
|
+
const error = new AIError("TTS streaming is not supported by the Laya decision-only provider.", "NOT_IMPLEMENTED", PROVIDER);
|
|
456
|
+
return { [Symbol.asyncIterator]: () => ({ next: () => Promise.reject(error) }) };
|
|
457
|
+
}
|
|
458
|
+
async cloneVoice(_a) {
|
|
459
|
+
return this.unsupported();
|
|
460
|
+
}
|
|
461
|
+
async designVoice(_a) {
|
|
462
|
+
return this.unsupported();
|
|
463
|
+
}
|
|
464
|
+
async getVoices(_a) {
|
|
465
|
+
return this.unsupported();
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
//#endregion
|
|
469
|
+
export { LayaProvider };
|
|
470
|
+
|
|
471
|
+
//# sourceMappingURL=laya-D-P82D0h.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"laya-D-P82D0h.js","names":[],"sources":["../../src/shared/providers/laya.ts"],"sourcesContent":["/**\n * Laya typed-decision provider.\n *\n * Laya (https://github.com/NandhaKishorM/laya) is a self-hosted,\n * non-autoregressive decision engine. Its `laya-serve` HTTP server answers the\n * Jev `POST /v1/systemone` wire shape, with differences this adapter handles\n * itself rather than inheriting from the TypeSafe adapter: optional auth, a\n * checkpoint chosen by the server's router, a `max_len` token window,\n * probabilities rounded to four decimals one at a time, and truncation facts.\n *\n * The public SDK contract calls Laya's `noul` primitive a `predicate`; the\n * server's vocabulary stays confined to this wire translation.\n */\n\nimport { ValidationError } from '@happyvertical/utils';\n\nimport { rateLimitErrorFrom } from '../rate-limit';\nimport { normalizeBaseAIOptions, prepareRequestControls } from '../safety';\nimport type {\n AICapabilities,\n AIInterface,\n AIMessage,\n AIModel,\n AIResponse,\n ChatOptions,\n CompletionOptions,\n DecisionAnswer,\n DecisionQuestion,\n DecisionRequest,\n DecisionResult,\n DecisionValue,\n EmbeddingOptions,\n EmbeddingResponse,\n ImageDescriptionOptions,\n ImageEmbeddingOptions,\n ImageGenerationOptions,\n ImageGenerationResponse,\n LayaDecisionOptions,\n LayaOptions,\n MessageOptions,\n ScoreDecisionQuestion,\n TTSOptions,\n TTSResponse,\n VideoGenerationJob,\n VideoGenerationOptions,\n VideoGenerationResult,\n VideoGenerationStatusResult,\n Voice,\n VoiceCloneOptions,\n VoiceDesignOptions,\n VoiceListOptions,\n} from '../types';\nimport { AIError, AuthenticationError } from '../types';\nimport { emitUsage } from './usage';\n\nconst PROVIDER = 'laya';\n\n/** Distributions this close to unit mass are used exactly as received. */\nconst STRICT_DISTRIBUTION_EPSILON = 1e-6;\n/**\n * `laya-serve` rounds every probability to four decimals on its own, so each\n * value is off by at most half of the last place and a distribution of `n`\n * values can be off by `n * 0.00005`.\n */\nconst ROUNDING_ERROR_PER_VALUE = 5e-5;\n/** No rounding story justifies renormalizing a distribution further out. */\nconst MAX_NORMALIZED_ERROR = 0.01;\n\ntype WireQuestion =\n | {\n type: 'noul';\n instructions: DecisionValue;\n criteria?: Record<string, DecisionValue>;\n }\n | {\n type: 'choice';\n instructions: DecisionValue;\n criteria: Record<string, DecisionValue | null>;\n }\n | { type: 'score'; instructions: DecisionValue; criteria: DecisionValue[] };\n\ninterface WireRequest {\n state: DecisionValue;\n questions: Record<string, WireQuestion>;\n model?: string;\n max_len?: number;\n}\n\ninterface WireResponse {\n model?: unknown;\n answers?: unknown;\n usage?: unknown;\n routing?: unknown;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction invalid(message: string): AIError {\n return new AIError(`Laya response ${message}`, 'INVALID_RESPONSE', PROVIDER);\n}\n\nfunction finite(value: unknown, field: string): number {\n if (typeof value !== 'number' || !Number.isFinite(value))\n throw invalid(`${field} must be finite`);\n return value;\n}\n\nfunction probability(value: unknown, field: string): number {\n const result = finite(value, field);\n if (result < 0 || result > 1)\n throw invalid(`${field} must be between 0 and 1`);\n return result;\n}\n\nfunction tokenCount(value: unknown, field: string): number {\n const result = finite(value, field);\n if (!Number.isSafeInteger(result) || result < 0)\n throw invalid(`${field} must be a non-negative integer`);\n return result;\n}\n\nfunction optionalString(\n source: Record<string, unknown>,\n key: string,\n field: string,\n): string | undefined {\n const value = source[key];\n if (value === undefined) return undefined;\n if (typeof value !== 'string' || !value)\n throw invalid(`${field} must be a non-empty string`);\n return value;\n}\n\nfunction sameDecisionValue(left: DecisionValue, right: unknown): boolean {\n if (left === right) return true;\n if (Array.isArray(left)) {\n return (\n Array.isArray(right) &&\n left.length === right.length &&\n left.every((value, index) => sameDecisionValue(value, right[index]))\n );\n }\n if (isRecord(left)) {\n return (\n isRecord(right) &&\n Object.keys(left).length === Object.keys(right).length &&\n Object.entries(left).every(\n ([key, value]) => key in right && sameDecisionValue(value, right[key]),\n )\n );\n }\n return false;\n}\n\n/**\n * `laya-serve` returns a rubric level in `legend` as sent when it is a string\n * and as Python's `json.dumps` text when it is anything else.\n */\nfunction legendMatches(level: DecisionValue, legend: unknown): boolean {\n if (sameDecisionValue(level, legend)) return true;\n if (typeof level === 'string' || typeof legend !== 'string') return false;\n try {\n return sameDecisionValue(level, JSON.parse(legend));\n } catch {\n return false;\n }\n}\n\nfunction validateValue(\n value: unknown,\n field: string,\n): asserts value is DecisionValue {\n if (value === null || typeof value === 'string' || typeof value === 'boolean')\n return;\n if (typeof value === 'number') {\n if (!Number.isFinite(value))\n throw new ValidationError(`${field} must contain finite numbers`, {\n provider: PROVIDER,\n });\n return;\n }\n if (Array.isArray(value)) {\n value.forEach((item, index) => {\n validateValue(item, `${field}[${index}]`);\n });\n return;\n }\n if (isRecord(value)) {\n Object.entries(value).forEach(([key, item]) => {\n validateValue(item, `${field}.${key}`);\n });\n return;\n }\n throw new ValidationError(`${field} must be JSON-compatible`, {\n provider: PROVIDER,\n });\n}\n\nfunction validateMaxLen(value: unknown, field: string): number | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== 'number' || !Number.isSafeInteger(value) || value <= 0)\n throw new ValidationError(`${field} must be a positive integer`, {\n provider: PROVIDER,\n });\n return value;\n}\n\n/**\n * Checks the provider-neutral request shape. Laya's own size limits (questions\n * per request, options per question, state length) are left to the server,\n * which reports them as explicit 413 errors.\n */\nfunction validateRequest(request: DecisionRequest): void {\n if (request.state === null || request.state === undefined)\n throw new ValidationError('state is required', { provider: PROVIDER });\n validateValue(request.state, 'state');\n const entries = Object.entries(request.questions || {});\n if (entries.length === 0)\n throw new ValidationError('questions must not be empty', {\n provider: PROVIDER,\n });\n for (const [id, question] of entries) {\n if (!id.trim())\n throw new ValidationError('question IDs must not be empty', {\n provider: PROVIDER,\n });\n validateValue(question.instructions, `questions.${id}.instructions`);\n if (question.type === 'predicate') {\n if (question.criteria) {\n for (const [key, value] of Object.entries(question.criteria))\n validateValue(value, `questions.${id}.criteria.${key}`);\n }\n } else if (question.type === 'choice') {\n const options = Object.keys(question.criteria || {});\n if (options.length === 0)\n throw new ValidationError(\n `questions.${id}.criteria must contain at least one option`,\n { provider: PROVIDER },\n );\n for (const [key, value] of Object.entries(question.criteria))\n if (value !== null)\n validateValue(value, `questions.${id}.criteria.${key}`);\n } else if (question.type === 'score') {\n if (!Array.isArray(question.criteria) || question.criteria.length < 2)\n throw new ValidationError(\n `questions.${id}.criteria must contain at least 2 ordered levels`,\n { provider: PROVIDER },\n );\n question.criteria.forEach((value, index) => {\n // Laya rejects a null level because its legend entry would be null.\n if (value === null)\n throw new ValidationError(\n `questions.${id}.criteria[${index}] must describe the level`,\n { provider: PROVIDER },\n );\n validateValue(value, `questions.${id}.criteria[${index}]`);\n });\n } else {\n throw new ValidationError(`questions.${id}.type is unsupported`, {\n provider: PROVIDER,\n });\n }\n }\n}\n\n/**\n * Builds each wire question field by field so unknown properties on a caller's\n * object (for example a generation control) are never forwarded.\n */\nfunction wireQuestion(question: DecisionQuestion): WireQuestion {\n if (question.type === 'predicate')\n return {\n type: 'noul',\n instructions: question.instructions,\n ...(question.criteria ? { criteria: question.criteria } : {}),\n };\n if (question.type === 'choice')\n return {\n type: 'choice',\n instructions: question.instructions,\n criteria: question.criteria,\n };\n return {\n type: 'score',\n instructions: question.instructions,\n criteria: question.criteria,\n };\n}\n\n/** Resolves the server root from a base URL that may carry the API path. */\nfunction serverRoot(value: string | undefined): string {\n const text = (value || '').trim();\n if (!text)\n throw new ValidationError(\n 'Laya requires baseUrl, for example http://localhost:8000',\n { provider: PROVIDER },\n );\n let url: URL;\n try {\n url = new URL(text);\n } catch {\n throw new ValidationError('Laya baseUrl must be an absolute http(s) URL', {\n provider: PROVIDER,\n });\n }\n if (url.protocol !== 'http:' && url.protocol !== 'https:')\n throw new ValidationError('Laya baseUrl must be an absolute http(s) URL', {\n provider: PROVIDER,\n });\n if (url.search || url.hash || url.username || url.password)\n throw new ValidationError(\n 'Laya baseUrl must not contain credentials, a query, or a fragment',\n { provider: PROVIDER },\n );\n const path = url.pathname\n .replace(/\\/+$/, '')\n .replace(/\\/v1(\\/systemone)?$/, '');\n return `${url.origin}${path}`;\n}\n\ninterface Distribution {\n values: Record<string, number>;\n /** The values as received, set only when they were renormalized. */\n raw?: Record<string, number>;\n}\n\n/**\n * Validates a choice or score distribution. Mass within the four-decimal\n * rounding bound is normalized by its actual sum; anything else is rejected.\n */\nfunction distribution(\n value: unknown,\n expected: string[],\n field: string,\n): Distribution {\n if (\n !isRecord(value) ||\n Object.keys(value).length !== expected.length ||\n expected.some((key) => !(key in value))\n )\n throw invalid(`${field} must cover exactly the requested values`);\n const values = Object.fromEntries(\n expected.map((key) => [key, probability(value[key], `${field}.${key}`)]),\n );\n const sum = Object.values(values).reduce((total, item) => total + item, 0);\n const drift = Math.abs(sum - 1);\n if (drift <= STRICT_DISTRIBUTION_EPSILON) return { values };\n const allowed =\n Math.min(expected.length * ROUNDING_ERROR_PER_VALUE, MAX_NORMALIZED_ERROR) +\n STRICT_DISTRIBUTION_EPSILON;\n if (sum <= 0 || drift > allowed) throw invalid(`${field} must sum to 1`);\n return {\n values: Object.fromEntries(\n Object.entries(values).map(([key, item]) => [key, item / sum]),\n ),\n raw: values,\n };\n}\n\nfunction answer(\n question: DecisionQuestion,\n raw: unknown,\n id: string,\n renormalized: Record<string, Record<string, number>>,\n): DecisionAnswer {\n if (!isRecord(raw)) throw invalid(`answers.${id} must be an object`);\n if (question.type === 'predicate') {\n if (raw.type !== 'noul')\n throw invalid(`answers.${id} type does not match predicate`);\n return {\n type: 'predicate',\n probability: probability(raw.noul, `answers.${id}.noul`),\n };\n }\n if (raw.type !== question.type)\n throw invalid(`answers.${id} type does not match request`);\n if (question.type === 'choice') {\n const choices = Object.keys(question.criteria);\n if (typeof raw.choice !== 'string' || !choices.includes(raw.choice))\n throw invalid(`answers.${id}.choice is not a requested option`);\n const probabilities = distribution(\n raw.probabilities,\n choices,\n `answers.${id}.probabilities`,\n );\n if (probabilities.raw) renormalized[id] = probabilities.raw;\n return {\n type: 'choice',\n choice: raw.choice,\n probabilities: probabilities.values,\n confidence: probability(raw.confidence, `answers.${id}.confidence`),\n };\n }\n const levels = (question as ScoreDecisionQuestion).criteria;\n const levelKeys = levels.map((_, index) => String(index));\n const score = finite(raw.score, `answers.${id}.score`);\n if (score < 0 || score > levels.length - 1)\n throw invalid(`answers.${id}.score is outside the requested rubric`);\n const legend = raw.legend;\n if (\n !isRecord(legend) ||\n levelKeys.some((key, index) => !legendMatches(levels[index], legend[key]))\n )\n throw invalid(`answers.${id}.legend does not match the requested rubric`);\n const probabilities = distribution(\n raw.probabilities,\n levelKeys,\n `answers.${id}.probabilities`,\n );\n if (probabilities.raw) renormalized[id] = probabilities.raw;\n return {\n type: 'score',\n score,\n probabilities: probabilities.values,\n confidence: probability(raw.confidence, `answers.${id}.confidence`),\n levels,\n };\n}\n\n/** A decision-only adapter for a `laya-serve` `/v1/systemone` endpoint. */\nexport class LayaProvider implements AIInterface {\n private readonly options: LayaOptions;\n private readonly root: string;\n private readonly maxLen?: number;\n\n constructor(options: LayaOptions) {\n this.root = serverRoot(options.baseUrl);\n this.maxLen = validateMaxLen(options.maxLen, 'maxLen');\n this.options = normalizeBaseAIOptions({ ...options, baseUrl: this.root });\n }\n\n async decide(\n request: DecisionRequest,\n options: LayaDecisionOptions = {},\n ): Promise<DecisionResult> {\n validateRequest(request);\n const maxLen = validateMaxLen(options.maxLen, 'maxLen') ?? this.maxLen;\n const controls = prepareRequestControls(this.options, options);\n const startedAt = Date.now();\n const requestedModel =\n options.model || this.options.defaultModel || undefined;\n const wire: WireRequest = {\n state: request.state,\n questions: Object.fromEntries(\n Object.entries(request.questions).map(([id, question]) => [\n id,\n wireQuestion(question),\n ]),\n ),\n ...(requestedModel ? { model: requestedModel } : {}),\n ...(maxLen !== undefined ? { max_len: maxLen } : {}),\n };\n try {\n const response = await fetch(`${this.root}/v1/systemone`, {\n method: 'POST',\n redirect: 'error',\n headers: {\n ...(this.options.apiKey\n ? { Authorization: `Bearer ${this.options.apiKey}` }\n : {}),\n 'Content-Type': 'application/json',\n ...this.options.headers,\n },\n body: JSON.stringify(wire),\n signal: controls.signal,\n });\n if (!response.ok) {\n if (response.status === 401) throw new AuthenticationError(PROVIDER);\n const text = await response.text();\n // 503 is laya-serve's admission limit (LAYA_MAX_CONCURRENT), which\n // sends Retry-After; 429 covers a rate-limiting proxy in front of it.\n if (response.status === 429 || response.status === 503)\n throw rateLimitErrorFrom(\n PROVIDER,\n { headers: response.headers, message: text },\n { model: requestedModel },\n );\n throw new AIError(\n `Laya request failed (${response.status}): ${text.slice(0, 500)}`,\n 'API_ERROR',\n PROVIDER,\n requestedModel,\n response.status >= 500,\n );\n }\n let body: WireResponse;\n try {\n body = (await response.json()) as WireResponse;\n } catch (error) {\n if (error instanceof SyntaxError)\n throw new AIError(\n 'Laya response contained invalid JSON',\n 'INVALID_RESPONSE',\n PROVIDER,\n requestedModel,\n );\n throw error;\n }\n return this.result(\n request,\n body,\n requestedModel,\n maxLen,\n options,\n startedAt,\n );\n } catch (error) {\n if (controls.didTimeout())\n throw new AIError(\n `Laya request timed out after ${controls.timeout}ms`,\n 'REQUEST_TIMEOUT',\n PROVIDER,\n requestedModel,\n true,\n );\n if (controls.signal.aborted && !(error instanceof AIError))\n throw new AIError(\n 'Laya request was aborted',\n 'REQUEST_ABORTED',\n PROVIDER,\n requestedModel,\n );\n if (!(error instanceof AIError)) {\n throw new AIError(\n `Network error calling Laya: ${\n error instanceof Error ? error.message : String(error)\n }`,\n 'NETWORK_ERROR',\n PROVIDER,\n requestedModel,\n true,\n );\n }\n throw error;\n } finally {\n controls.cleanup();\n }\n }\n\n private result(\n request: DecisionRequest,\n body: WireResponse,\n requestedModel: string | undefined,\n maxLen: number | undefined,\n options: LayaDecisionOptions,\n startedAt: number,\n ): DecisionResult {\n if (!isRecord(body)) throw invalid('must be an object');\n // The root `model` names Laya's decision head, not the checkpoint.\n if (typeof body.model !== 'string' || !body.model)\n throw invalid('model is required');\n if (!isRecord(body.answers)) throw invalid('answers is required');\n const responseAnswers = body.answers;\n const ids = Object.keys(request.questions);\n if (\n Object.keys(responseAnswers).length !== ids.length ||\n ids.some((id) => !(id in responseAnswers))\n )\n throw invalid('answers must match requested question IDs');\n const renormalized: Record<string, Record<string, number>> = {};\n const answers = Object.fromEntries(\n ids.map((id) => [\n id,\n answer(request.questions[id], responseAnswers[id], id, renormalized),\n ]),\n );\n\n let usage:\n | { promptTokens: number; completionTokens: number; totalTokens: number }\n | undefined;\n const facts: Record<string, DecisionValue> = {};\n if (body.usage !== undefined) {\n const wireUsage = body.usage;\n if (\n !isRecord(wireUsage) ||\n !('input_tokens' in wireUsage) ||\n !('output_tokens' in wireUsage)\n )\n throw invalid('usage must include input_tokens and output_tokens');\n const input = tokenCount(wireUsage.input_tokens, 'usage.input_tokens');\n const output = tokenCount(wireUsage.output_tokens, 'usage.output_tokens');\n usage = {\n promptTokens: input,\n completionTokens: output,\n totalTokens: input + output,\n };\n // Truncation is invisible anywhere else in the response.\n for (const [key, name] of [\n ['state_tokens', 'stateTokens'],\n ['state_tokens_dropped', 'stateTokensDropped'],\n ] as const)\n if (wireUsage[key] !== undefined)\n facts[name] = tokenCount(wireUsage[key], `usage.${key}`);\n if (wireUsage.truncated !== undefined) {\n if (typeof wireUsage.truncated !== 'boolean')\n throw invalid('usage.truncated must be a boolean');\n facts.truncated = wireUsage.truncated;\n }\n if (wireUsage.truncated_questions !== undefined) {\n const truncated = wireUsage.truncated_questions;\n if (\n !Array.isArray(truncated) ||\n truncated.some((id) => typeof id !== 'string')\n )\n throw invalid('usage.truncated_questions must list question IDs');\n facts.truncatedQuestions = truncated as string[];\n }\n }\n\n let checkpoint: string | undefined;\n if (body.routing !== undefined) {\n if (!isRecord(body.routing)) throw invalid('routing must be an object');\n checkpoint = optionalString(body.routing, 'model', 'routing.model');\n const repo = optionalString(body.routing, 'repo', 'routing.repo');\n const reason = optionalString(body.routing, 'reason', 'routing.reason');\n if (checkpoint) facts.checkpoint = checkpoint;\n if (repo) facts.checkpointRepo = repo;\n if (reason) facts.routeReason = reason;\n }\n\n // The checkpoint that answered is reported only in `routing`, which a\n // server running LAYA_JEV_STRICT omits. Without it, report the decision\n // head and leave `details.checkpoint` unset rather than guess.\n const model = checkpoint || body.model;\n const renormalizedIds = Object.keys(renormalized);\n const details: Record<string, DecisionValue> = {\n serverModel: body.model,\n ...(requestedModel ? { requestedModel } : {}),\n ...(maxLen !== undefined ? { maxLen } : {}),\n ...facts,\n ...(renormalizedIds.length\n ? { renormalized: renormalizedIds, rawProbabilities: renormalized }\n : {}),\n };\n emitUsage(\n this.options,\n PROVIDER,\n 'decide',\n model,\n usage,\n startedAt,\n options.usageTags,\n );\n return {\n model,\n ...(usage ? { usage } : {}),\n provenance: { provider: PROVIDER, model, details },\n answers,\n };\n }\n\n async getModels(): Promise<AIModel[]> {\n // laya-serve has no model-listing route; these are the checkpoints of the\n // convaiinnovations/laya bundle. A server may register others.\n const checkpoints: Array<[string, string, number]> = [\n ['typed-decisions', 'Laya typed decisions (421M)', 1024],\n ['english', 'Laya English (421M)', 512],\n ['multilingual', 'Laya multilingual (322M)', 1024],\n ];\n const configured = this.options.defaultModel;\n const models = checkpoints.map(([id, name, contextLength]) => ({\n id,\n name,\n description: 'Laya typed decisions',\n contextLength,\n capabilities: ['decisions'],\n supportsFunctions: false,\n supportsVision: false,\n }));\n if (configured && !models.some((model) => model.id === configured))\n models.push({\n id: configured,\n name: configured,\n description: 'Laya typed decisions (configured checkpoint)',\n contextLength: 0,\n capabilities: ['decisions'],\n supportsFunctions: false,\n supportsVision: false,\n });\n return models;\n }\n async getCapabilities(): Promise<AICapabilities> {\n return {\n decisions: true,\n chat: false,\n completion: false,\n embeddings: false,\n streaming: false,\n functions: false,\n vision: false,\n fineTuning: false,\n imageEmbeddings: false,\n imageGeneration: false,\n videoGeneration: false,\n tts: false,\n voiceCloning: false,\n voiceDesign: false,\n maxContextLength: 0,\n supportedOperations: ['decide'],\n };\n }\n private unsupported(): never {\n throw new AIError(\n 'This operation is not supported by the Laya decision-only provider.',\n 'NOT_IMPLEMENTED',\n PROVIDER,\n );\n }\n async chat(_a: AIMessage[], _b?: ChatOptions): Promise<AIResponse> {\n return this.unsupported();\n }\n async complete(_a: string, _b?: CompletionOptions): Promise<AIResponse> {\n return this.unsupported();\n }\n async message(_a: string, _b?: MessageOptions): Promise<string> {\n return this.unsupported();\n }\n async embed(\n _a: string | string[],\n _b?: EmbeddingOptions,\n ): Promise<EmbeddingResponse> {\n return this.unsupported();\n }\n async embedImage(\n _a: string | Buffer,\n _b?: ImageEmbeddingOptions,\n ): Promise<EmbeddingResponse> {\n return this.unsupported();\n }\n async describeImage(\n _a: string | Buffer,\n _b?: string,\n _c?: ImageDescriptionOptions,\n ): Promise<string> {\n return this.unsupported();\n }\n async generateImage(\n _a: string,\n _b?: ImageGenerationOptions,\n ): Promise<ImageGenerationResponse> {\n return this.unsupported();\n }\n stream(_a: AIMessage[], _b?: ChatOptions): AsyncIterable<string> {\n const error = new AIError(\n 'Chat streaming is not supported by the Laya decision-only provider.',\n 'NOT_IMPLEMENTED',\n PROVIDER,\n );\n return {\n [Symbol.asyncIterator]: () => ({ next: () => Promise.reject(error) }),\n };\n }\n async countTokens(_a: string): Promise<number> {\n return this.unsupported();\n }\n async submitVideoGenerationJob(\n _a: VideoGenerationOptions,\n ): Promise<VideoGenerationJob> {\n return this.unsupported();\n }\n async getVideoGenerationJob(\n _a: VideoGenerationJob,\n ): Promise<VideoGenerationStatusResult> {\n return this.unsupported();\n }\n async fetchVideoGenerationResult(\n _a: VideoGenerationJob,\n ): Promise<VideoGenerationResult> {\n return this.unsupported();\n }\n async cancelVideoGenerationJob(_a: VideoGenerationJob): Promise<void> {\n return this.unsupported();\n }\n async validateVideoGenerationAccess(): Promise<boolean> {\n return this.unsupported();\n }\n async synthesizeSpeech(_a: string, _b?: TTSOptions): Promise<TTSResponse> {\n return this.unsupported();\n }\n streamSpeech(_a: string, _b?: TTSOptions): AsyncIterable<Buffer> {\n const error = new AIError(\n 'TTS streaming is not supported by the Laya decision-only provider.',\n 'NOT_IMPLEMENTED',\n PROVIDER,\n );\n return {\n [Symbol.asyncIterator]: () => ({ next: () => Promise.reject(error) }),\n };\n }\n async cloneVoice(_a: VoiceCloneOptions): Promise<Voice> {\n return this.unsupported();\n }\n async designVoice(_a: VoiceDesignOptions): Promise<Voice> {\n return this.unsupported();\n }\n async getVoices(_a?: VoiceListOptions): Promise<Voice[]> {\n return this.unsupported();\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { c as normalizeImageGenerationOptions, s as normalizeChatOptions } from "./usage-
|
|
2
|
-
import { d as OpenAIProvider } from "./src-
|
|
3
|
-
import { a as LiteLLMAdmin, n as isSpeechModel, r as isTranscriptionModel, s as resolveGatewayAdminBaseUrl, t as isAudioModel } from "./gateway-audio-models-
|
|
1
|
+
import { c as normalizeImageGenerationOptions, s as normalizeChatOptions } from "./usage-B9gPCWSa.js";
|
|
2
|
+
import { d as OpenAIProvider } from "./src-Cc5yFnnQ.js";
|
|
3
|
+
import { a as LiteLLMAdmin, n as isSpeechModel, r as isTranscriptionModel, s as resolveGatewayAdminBaseUrl, t as isAudioModel } from "./gateway-audio-models-B1tGNv1H.js";
|
|
4
4
|
import { ValidationError } from "@happyvertical/utils";
|
|
5
5
|
//#region src/shared/providers/litellm.ts
|
|
6
6
|
/** Chat Completions has effort, not a generic reasoning budget/thoughts envelope. */
|
|
@@ -194,4 +194,4 @@ var LiteLLMProvider = class extends OpenAIProvider {
|
|
|
194
194
|
//#endregion
|
|
195
195
|
export { LiteLLMProvider };
|
|
196
196
|
|
|
197
|
-
//# sourceMappingURL=litellm-
|
|
197
|
+
//# sourceMappingURL=litellm-BaGgWzHR.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"litellm-rMCXClTb.js","names":[],"sources":["../../src/shared/providers/litellm.ts"],"sourcesContent":["import { ValidationError } from '@happyvertical/utils';\n\nimport {\n normalizeChatOptions,\n normalizeImageGenerationOptions,\n} from '../safety';\nimport type {\n AICapabilities,\n AIMessage,\n AIModel,\n AIResponse,\n ChatOptions,\n EmbeddingOptions,\n EmbeddingResponse,\n ImageDescriptionOptions,\n ImageEmbeddingOptions,\n ImageGenerationOptions,\n ImageGenerationResponse,\n LiteLLMOptions,\n} from '../types';\nimport { LiteLLMAdmin, resolveGatewayAdminBaseUrl } from './gateway-admin';\nimport {\n isAudioModel,\n isSpeechModel,\n isTranscriptionModel,\n} from './gateway-audio-models';\nimport { type OpenAICompatibleProfile, OpenAIProvider } from './openai';\n\n/** Chat Completions has effort, not a generic reasoning budget/thoughts envelope. */\nfunction assertChatReasoningControls(\n options: Pick<ChatOptions, 'reasoning' | 'includeThoughts'>,\n): void {\n if (\n options.reasoning?.maxTokens !== undefined ||\n options.reasoning?.includeThoughts !== undefined ||\n options.includeThoughts !== undefined\n ) {\n throw new ValidationError(\n 'LiteLLM Chat Completions does not support generic reasoning.maxTokens or includeThoughts controls; use reasoning.effort and an output token limit',\n { provider: 'litellm' },\n );\n }\n}\n\nconst LITELLM_CAPABILITIES: AICapabilities = {\n chat: true,\n completion: true,\n embeddings: true,\n streaming: true,\n functions: true,\n vision: true,\n fineTuning: false,\n imageEmbeddings: true,\n imageGeneration: true,\n videoGeneration: false,\n tts: false,\n voiceCloning: false,\n voiceDesign: false,\n maxContextLength: 128000,\n supportedOperations: [\n 'chat',\n 'completion',\n 'embedding',\n 'streaming',\n 'functions',\n 'vision',\n 'image_embedding',\n 'image_generation',\n ],\n};\n\nfunction isEmbeddingModel(modelId: string): boolean {\n return /embed/i.test(modelId);\n}\n\nfunction isImageEmbeddingModel(modelId: string): boolean {\n return /titan-embed-image|multimodalembedding|image[-_]?embed|clip/i.test(\n modelId,\n );\n}\n\nfunction isImageGenerationModel(modelId: string): boolean {\n return /gpt-image|dall-e|imagen|stable[-_ ]diffusion|sdxl|flux|titan-image|image-generator/i.test(\n modelId,\n );\n}\n\nfunction isFilteredLiteLLMModel(modelId: string): boolean {\n return /moderation|rerank/i.test(modelId);\n}\n\nfunction inferLiteLLMContextLength(modelId: string): number {\n if (isEmbeddingModel(modelId)) return 8192;\n if (/gemini-1\\.5|gemini-2\\.0|gemini-2\\.5|gemini-3/i.test(modelId)) {\n return 1000000;\n }\n if (/claude/i.test(modelId)) return 200000;\n if (/gpt-4o|gpt-4\\.1|o1|o3|o4|llama|mistral|qwen|deepseek/i.test(modelId)) {\n return 128000;\n }\n return 32768;\n}\n\nfunction inferLiteLLMFunctions(modelId: string): boolean {\n if (isEmbeddingModel(modelId) || isAudioModel(modelId)) return false;\n return /gpt|claude|gemini|command|llama|mistral|qwen|deepseek|o1|o3|o4/i.test(\n modelId,\n );\n}\n\nfunction inferLiteLLMVision(modelId: string): boolean {\n if (isAudioModel(modelId)) return false;\n return /gpt-4o|gpt-4\\.1|vision|claude-3|gemini|pixtral|llava|qwen.*vl|vl-/i.test(\n modelId,\n );\n}\n\nfunction inferLiteLLMCapabilities(modelId: string): string[] {\n if (isTranscriptionModel(modelId)) {\n return ['transcription'];\n }\n\n if (isSpeechModel(modelId)) {\n return ['speech'];\n }\n\n if (isImageGenerationModel(modelId)) {\n return ['image_generation'];\n }\n\n if (isImageEmbeddingModel(modelId)) {\n return ['embeddings', 'image_embedding'];\n }\n\n if (isEmbeddingModel(modelId)) {\n return ['embeddings'];\n }\n\n const capabilities = ['text', 'chat'];\n\n if (inferLiteLLMFunctions(modelId)) {\n capabilities.push('functions');\n }\n\n if (inferLiteLLMVision(modelId)) {\n capabilities.push('vision');\n }\n\n return capabilities;\n}\n\nfunction normalizeBaseUrl(baseUrl: string): string {\n return baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;\n}\n\nfunction ensureLiteLLMOptions(options: LiteLLMOptions): LiteLLMOptions {\n if (!options.baseUrl?.trim()) {\n throw new ValidationError('LiteLLM baseUrl is required', {\n provider: 'litellm',\n hint: 'Pass baseUrl like https://llm.happyvertical.com/v1 or set HAVE_AI_BASE_URL / LITELLM_BASE_URL',\n });\n }\n\n return {\n ...options,\n baseUrl: normalizeBaseUrl(options.baseUrl.trim()),\n };\n}\n\nconst LITELLM_PROFILE: OpenAICompatibleProfile = {\n providerLabel: 'LiteLLM',\n providerName: 'litellm',\n defaultModel: 'gpt-4',\n capabilities: LITELLM_CAPABILITIES,\n describeModel: (modelId) => `LiteLLM model: ${modelId}`,\n getContextLength: inferLiteLLMContextLength,\n getModelCapabilities: inferLiteLLMCapabilities,\n shouldIncludeModel: (modelId) => !isFilteredLiteLLMModel(modelId),\n supportsFunctions: inferLiteLLMFunctions,\n supportsVision: inferLiteLLMVision,\n};\n\ntype LiteLLMResolvedCapability =\n | 'chat'\n | 'vision'\n | 'embeddings'\n | 'image_generation';\n\nfunction supportsCapability(\n model: AIModel,\n capability: LiteLLMResolvedCapability,\n): boolean {\n if (capability === 'vision') {\n return (\n model.capabilities.includes('vision') || model.supportsVision === true\n );\n }\n\n if (capability === 'embeddings') {\n return model.capabilities.includes('embeddings');\n }\n\n if (capability === 'image_generation') {\n return model.capabilities.includes('image_generation');\n }\n\n return model.capabilities.includes('chat');\n}\n\n/**\n * LiteLLM provider implementation.\n *\n * LiteLLM exposes an OpenAI-compatible API surface, so this provider reuses the\n * OpenAI transport while customizing provider identity, model discovery, and\n * capability heuristics for gateway-backed deployments.\n */\nexport class LiteLLMProvider extends OpenAIProvider {\n readonly admin: LiteLLMAdmin;\n private readonly configuredDefaultModel?: string;\n private readonly resolvedModelCache = new Map<\n LiteLLMResolvedCapability,\n Promise<string>\n >();\n\n constructor(options: LiteLLMOptions) {\n const normalized = ensureLiteLLMOptions(options);\n super(normalized, LITELLM_PROFILE);\n this.admin = new LiteLLMAdmin({\n provider: 'litellm',\n baseUrl: resolveGatewayAdminBaseUrl(\n normalized.baseUrl,\n normalized.adminUrl || normalized.adminBaseUrl,\n 'litellm',\n ),\n apiKey: normalized.adminApiKey || normalized.apiKey,\n headers: normalized.adminHeaders,\n timeout: normalized.timeout,\n });\n this.configuredDefaultModel = normalized.defaultModel;\n }\n\n private async resolveModel(\n capability: LiteLLMResolvedCapability,\n explicitModel?: string,\n ): Promise<string> {\n if (explicitModel) {\n return explicitModel;\n }\n\n if (\n this.configuredDefaultModel &&\n (capability === 'chat' || capability === 'vision')\n ) {\n return this.configuredDefaultModel;\n }\n\n const cached = this.resolvedModelCache.get(capability);\n if (cached) {\n return cached;\n }\n\n const pending = this.selectModel(capability).catch((error) => {\n this.resolvedModelCache.delete(capability);\n throw error;\n });\n this.resolvedModelCache.set(capability, pending);\n return pending;\n }\n\n private async selectModel(\n capability: LiteLLMResolvedCapability,\n ): Promise<string> {\n const models = await super.getModels();\n const model = models.find((candidate) =>\n supportsCapability(candidate, capability),\n );\n\n if (!model) {\n throw new ValidationError(\n `No ${capability} model is available from the LiteLLM gateway`,\n {\n provider: 'litellm',\n capability,\n hint: 'Pass defaultModel explicitly or ensure /models returns a compatible model for this key',\n },\n );\n }\n\n return model.id;\n }\n\n async chat(\n messages: AIMessage[],\n options: ChatOptions = {},\n ): Promise<AIResponse> {\n assertChatReasoningControls(options);\n const safe = normalizeChatOptions(\n this.options,\n options,\n 'litellm',\n options.model || this.options.defaultModel,\n );\n return super.chat(messages, {\n ...safe,\n model: await this.resolveModel('chat', safe.model),\n });\n }\n\n async *stream(\n messages: AIMessage[],\n options: ChatOptions = {},\n ): AsyncIterable<string> {\n assertChatReasoningControls(options);\n const safe = normalizeChatOptions(\n this.options,\n options,\n 'litellm',\n options.model || this.options.defaultModel,\n );\n yield* super.stream(messages, {\n ...safe,\n model: await this.resolveModel('chat', safe.model),\n });\n }\n\n async describeImage(\n image: string | Buffer,\n prompt?: string,\n options: ImageDescriptionOptions = {},\n ): Promise<string> {\n assertChatReasoningControls(options);\n const safe = normalizeChatOptions(\n this.options,\n options,\n 'litellm',\n options.model || this.options.defaultModel,\n );\n return super.describeImage(image, prompt, {\n ...safe,\n // The inherited image path calls this.chat(); do not reclassify the\n // normalization-inferred budget as an explicit caller control.\n reasoning: options.reasoning,\n model: await this.resolveModel('vision', safe.model),\n });\n }\n\n async embed(\n text: string | string[],\n options: EmbeddingOptions = {},\n ): Promise<EmbeddingResponse> {\n return super.embed(text, {\n ...options,\n model: await this.resolveModel('embeddings', options.model),\n });\n }\n\n async embedImage(\n image: string | Buffer,\n options: ImageEmbeddingOptions = {},\n ): Promise<EmbeddingResponse> {\n return super.embedImage(image, {\n ...options,\n model: await this.resolveModel('embeddings', options.model),\n });\n }\n\n async generateImage(\n prompt: string,\n options: ImageGenerationOptions = {},\n ): Promise<ImageGenerationResponse> {\n const safe = normalizeImageGenerationOptions(\n this.options,\n options,\n 'litellm',\n options.model,\n );\n return super.generateImage(prompt, {\n ...safe,\n model: await this.resolveModel('image_generation', safe.model),\n });\n }\n}\n"],"mappings":";;;;;;AA6BA,SAAS,4BACP,SACM;CACN,IACE,QAAQ,WAAW,cAAc,KAAA,KACjC,QAAQ,WAAW,oBAAoB,KAAA,KACvC,QAAQ,oBAAoB,KAAA,GAE5B,MAAM,IAAI,gBACR,qJACA,EAAE,UAAU,UAAU,CACxB;AAEJ;AAEA,IAAM,uBAAuC;CAC3C,MAAM;CACN,YAAY;CACZ,YAAY;CACZ,WAAW;CACX,WAAW;CACX,QAAQ;CACR,YAAY;CACZ,iBAAiB;CACjB,iBAAiB;CACjB,iBAAiB;CACjB,KAAK;CACL,cAAc;CACd,aAAa;CACb,kBAAkB;CAClB,qBAAqB;EACnB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF;AAEA,SAAS,iBAAiB,SAA0B;CAClD,OAAO,SAAS,KAAK,OAAO;AAC9B;AAEA,SAAS,sBAAsB,SAA0B;CACvD,OAAO,8DAA8D,KACnE,OACF;AACF;AAEA,SAAS,uBAAuB,SAA0B;CACxD,OAAO,sFAAsF,KAC3F,OACF;AACF;AAEA,SAAS,uBAAuB,SAA0B;CACxD,OAAO,qBAAqB,KAAK,OAAO;AAC1C;AAEA,SAAS,0BAA0B,SAAyB;CAC1D,IAAI,iBAAiB,OAAO,GAAG,OAAO;CACtC,IAAI,gDAAgD,KAAK,OAAO,GAC9D,OAAO;CAET,IAAI,UAAU,KAAK,OAAO,GAAG,OAAO;CACpC,IAAI,wDAAwD,KAAK,OAAO,GACtE,OAAO;CAET,OAAO;AACT;AAEA,SAAS,sBAAsB,SAA0B;CACvD,IAAI,iBAAiB,OAAO,KAAK,aAAa,OAAO,GAAG,OAAO;CAC/D,OAAO,kEAAkE,KACvE,OACF;AACF;AAEA,SAAS,mBAAmB,SAA0B;CACpD,IAAI,aAAa,OAAO,GAAG,OAAO;CAClC,OAAO,qEAAqE,KAC1E,OACF;AACF;AAEA,SAAS,yBAAyB,SAA2B;CAC3D,IAAI,qBAAqB,OAAO,GAC9B,OAAO,CAAC,eAAe;CAGzB,IAAI,cAAc,OAAO,GACvB,OAAO,CAAC,QAAQ;CAGlB,IAAI,uBAAuB,OAAO,GAChC,OAAO,CAAC,kBAAkB;CAG5B,IAAI,sBAAsB,OAAO,GAC/B,OAAO,CAAC,cAAc,iBAAiB;CAGzC,IAAI,iBAAiB,OAAO,GAC1B,OAAO,CAAC,YAAY;CAGtB,MAAM,eAAe,CAAC,QAAQ,MAAM;CAEpC,IAAI,sBAAsB,OAAO,GAC/B,aAAa,KAAK,WAAW;CAG/B,IAAI,mBAAmB,OAAO,GAC5B,aAAa,KAAK,QAAQ;CAG5B,OAAO;AACT;AAEA,SAAS,iBAAiB,SAAyB;CACjD,OAAO,QAAQ,SAAS,GAAG,IAAI,QAAQ,MAAM,GAAG,EAAE,IAAI;AACxD;AAEA,SAAS,qBAAqB,SAAyC;CACrE,IAAI,CAAC,QAAQ,SAAS,KAAK,GACzB,MAAM,IAAI,gBAAgB,+BAA+B;EACvD,UAAU;EACV,MAAM;CACR,CAAC;CAGH,OAAO;EACL,GAAG;EACH,SAAS,iBAAiB,QAAQ,QAAQ,KAAK,CAAC;CAClD;AACF;AAEA,IAAM,kBAA2C;CAC/C,eAAe;CACf,cAAc;CACd,cAAc;CACd,cAAc;CACd,gBAAgB,YAAY,kBAAkB;CAC9C,kBAAkB;CAClB,sBAAsB;CACtB,qBAAqB,YAAY,CAAC,uBAAuB,OAAO;CAChE,mBAAmB;CACnB,gBAAgB;AAClB;AAQA,SAAS,mBACP,OACA,YACS;CACT,IAAI,eAAe,UACjB,OACE,MAAM,aAAa,SAAS,QAAQ,KAAK,MAAM,mBAAmB;CAItE,IAAI,eAAe,cACjB,OAAO,MAAM,aAAa,SAAS,YAAY;CAGjD,IAAI,eAAe,oBACjB,OAAO,MAAM,aAAa,SAAS,kBAAkB;CAGvD,OAAO,MAAM,aAAa,SAAS,MAAM;AAC3C;;;;;;;;AASA,IAAa,kBAAb,cAAqC,eAAe;CAClD;CACA;CACA,qCAAsC,IAAI,IAGxC;CAEF,YAAY,SAAyB;EACnC,MAAM,aAAa,qBAAqB,OAAO;EAC/C,MAAM,YAAY,eAAe;EACjC,KAAK,QAAQ,IAAI,aAAa;GAC5B,UAAU;GACV,SAAS,2BACP,WAAW,SACX,WAAW,YAAY,WAAW,cAClC,SACF;GACA,QAAQ,WAAW,eAAe,WAAW;GAC7C,SAAS,WAAW;GACpB,SAAS,WAAW;EACtB,CAAC;EACD,KAAK,yBAAyB,WAAW;CAC3C;CAEA,MAAc,aACZ,YACA,eACiB;EACjB,IAAI,eACF,OAAO;EAGT,IACE,KAAK,2BACJ,eAAe,UAAU,eAAe,WAEzC,OAAO,KAAK;EAGd,MAAM,SAAS,KAAK,mBAAmB,IAAI,UAAU;EACrD,IAAI,QACF,OAAO;EAGT,MAAM,UAAU,KAAK,YAAY,UAAU,CAAC,CAAC,OAAO,UAAU;GAC5D,KAAK,mBAAmB,OAAO,UAAU;GACzC,MAAM;EACR,CAAC;EACD,KAAK,mBAAmB,IAAI,YAAY,OAAO;EAC/C,OAAO;CACT;CAEA,MAAc,YACZ,YACiB;EAEjB,MAAM,SAAQ,MADO,MAAM,UAAU,EAAA,CAChB,MAAM,cACzB,mBAAmB,WAAW,UAAU,CAC1C;EAEA,IAAI,CAAC,OACH,MAAM,IAAI,gBACR,MAAM,WAAW,+CACjB;GACE,UAAU;GACV;GACA,MAAM;EACR,CACF;EAGF,OAAO,MAAM;CACf;CAEA,MAAM,KACJ,UACA,UAAuB,CAAC,GACH;EACrB,4BAA4B,OAAO;EACnC,MAAM,OAAO,qBACX,KAAK,SACL,SACA,WACA,QAAQ,SAAS,KAAK,QAAQ,YAChC;EACA,OAAO,MAAM,KAAK,UAAU;GAC1B,GAAG;GACH,OAAO,MAAM,KAAK,aAAa,QAAQ,KAAK,KAAK;EACnD,CAAC;CACH;CAEA,OAAO,OACL,UACA,UAAuB,CAAC,GACD;EACvB,4BAA4B,OAAO;EACnC,MAAM,OAAO,qBACX,KAAK,SACL,SACA,WACA,QAAQ,SAAS,KAAK,QAAQ,YAChC;EACA,OAAO,MAAM,OAAO,UAAU;GAC5B,GAAG;GACH,OAAO,MAAM,KAAK,aAAa,QAAQ,KAAK,KAAK;EACnD,CAAC;CACH;CAEA,MAAM,cACJ,OACA,QACA,UAAmC,CAAC,GACnB;EACjB,4BAA4B,OAAO;EACnC,MAAM,OAAO,qBACX,KAAK,SACL,SACA,WACA,QAAQ,SAAS,KAAK,QAAQ,YAChC;EACA,OAAO,MAAM,cAAc,OAAO,QAAQ;GACxC,GAAG;GAGH,WAAW,QAAQ;GACnB,OAAO,MAAM,KAAK,aAAa,UAAU,KAAK,KAAK;EACrD,CAAC;CACH;CAEA,MAAM,MACJ,MACA,UAA4B,CAAC,GACD;EAC5B,OAAO,MAAM,MAAM,MAAM;GACvB,GAAG;GACH,OAAO,MAAM,KAAK,aAAa,cAAc,QAAQ,KAAK;EAC5D,CAAC;CACH;CAEA,MAAM,WACJ,OACA,UAAiC,CAAC,GACN;EAC5B,OAAO,MAAM,WAAW,OAAO;GAC7B,GAAG;GACH,OAAO,MAAM,KAAK,aAAa,cAAc,QAAQ,KAAK;EAC5D,CAAC;CACH;CAEA,MAAM,cACJ,QACA,UAAkC,CAAC,GACD;EAClC,MAAM,OAAO,gCACX,KAAK,SACL,SACA,WACA,QAAQ,KACV;EACA,OAAO,MAAM,cAAc,QAAQ;GACjC,GAAG;GACH,OAAO,MAAM,KAAK,aAAa,oBAAoB,KAAK,KAAK;EAC/D,CAAC;CACH;AACF"}
|
|
1
|
+
{"version":3,"file":"litellm-BaGgWzHR.js","names":[],"sources":["../../src/shared/providers/litellm.ts"],"sourcesContent":["import { ValidationError } from '@happyvertical/utils';\n\nimport {\n normalizeChatOptions,\n normalizeImageGenerationOptions,\n} from '../safety';\nimport type {\n AICapabilities,\n AIMessage,\n AIModel,\n AIResponse,\n ChatOptions,\n EmbeddingOptions,\n EmbeddingResponse,\n ImageDescriptionOptions,\n ImageEmbeddingOptions,\n ImageGenerationOptions,\n ImageGenerationResponse,\n LiteLLMOptions,\n} from '../types';\nimport { LiteLLMAdmin, resolveGatewayAdminBaseUrl } from './gateway-admin';\nimport {\n isAudioModel,\n isSpeechModel,\n isTranscriptionModel,\n} from './gateway-audio-models';\nimport { type OpenAICompatibleProfile, OpenAIProvider } from './openai';\n\n/** Chat Completions has effort, not a generic reasoning budget/thoughts envelope. */\nfunction assertChatReasoningControls(\n options: Pick<ChatOptions, 'reasoning' | 'includeThoughts'>,\n): void {\n if (\n options.reasoning?.maxTokens !== undefined ||\n options.reasoning?.includeThoughts !== undefined ||\n options.includeThoughts !== undefined\n ) {\n throw new ValidationError(\n 'LiteLLM Chat Completions does not support generic reasoning.maxTokens or includeThoughts controls; use reasoning.effort and an output token limit',\n { provider: 'litellm' },\n );\n }\n}\n\nconst LITELLM_CAPABILITIES: AICapabilities = {\n chat: true,\n completion: true,\n embeddings: true,\n streaming: true,\n functions: true,\n vision: true,\n fineTuning: false,\n imageEmbeddings: true,\n imageGeneration: true,\n videoGeneration: false,\n tts: false,\n voiceCloning: false,\n voiceDesign: false,\n maxContextLength: 128000,\n supportedOperations: [\n 'chat',\n 'completion',\n 'embedding',\n 'streaming',\n 'functions',\n 'vision',\n 'image_embedding',\n 'image_generation',\n ],\n};\n\nfunction isEmbeddingModel(modelId: string): boolean {\n return /embed/i.test(modelId);\n}\n\nfunction isImageEmbeddingModel(modelId: string): boolean {\n return /titan-embed-image|multimodalembedding|image[-_]?embed|clip/i.test(\n modelId,\n );\n}\n\nfunction isImageGenerationModel(modelId: string): boolean {\n return /gpt-image|dall-e|imagen|stable[-_ ]diffusion|sdxl|flux|titan-image|image-generator/i.test(\n modelId,\n );\n}\n\nfunction isFilteredLiteLLMModel(modelId: string): boolean {\n return /moderation|rerank/i.test(modelId);\n}\n\nfunction inferLiteLLMContextLength(modelId: string): number {\n if (isEmbeddingModel(modelId)) return 8192;\n if (/gemini-1\\.5|gemini-2\\.0|gemini-2\\.5|gemini-3/i.test(modelId)) {\n return 1000000;\n }\n if (/claude/i.test(modelId)) return 200000;\n if (/gpt-4o|gpt-4\\.1|o1|o3|o4|llama|mistral|qwen|deepseek/i.test(modelId)) {\n return 128000;\n }\n return 32768;\n}\n\nfunction inferLiteLLMFunctions(modelId: string): boolean {\n if (isEmbeddingModel(modelId) || isAudioModel(modelId)) return false;\n return /gpt|claude|gemini|command|llama|mistral|qwen|deepseek|o1|o3|o4/i.test(\n modelId,\n );\n}\n\nfunction inferLiteLLMVision(modelId: string): boolean {\n if (isAudioModel(modelId)) return false;\n return /gpt-4o|gpt-4\\.1|vision|claude-3|gemini|pixtral|llava|qwen.*vl|vl-/i.test(\n modelId,\n );\n}\n\nfunction inferLiteLLMCapabilities(modelId: string): string[] {\n if (isTranscriptionModel(modelId)) {\n return ['transcription'];\n }\n\n if (isSpeechModel(modelId)) {\n return ['speech'];\n }\n\n if (isImageGenerationModel(modelId)) {\n return ['image_generation'];\n }\n\n if (isImageEmbeddingModel(modelId)) {\n return ['embeddings', 'image_embedding'];\n }\n\n if (isEmbeddingModel(modelId)) {\n return ['embeddings'];\n }\n\n const capabilities = ['text', 'chat'];\n\n if (inferLiteLLMFunctions(modelId)) {\n capabilities.push('functions');\n }\n\n if (inferLiteLLMVision(modelId)) {\n capabilities.push('vision');\n }\n\n return capabilities;\n}\n\nfunction normalizeBaseUrl(baseUrl: string): string {\n return baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;\n}\n\nfunction ensureLiteLLMOptions(options: LiteLLMOptions): LiteLLMOptions {\n if (!options.baseUrl?.trim()) {\n throw new ValidationError('LiteLLM baseUrl is required', {\n provider: 'litellm',\n hint: 'Pass baseUrl like https://llm.happyvertical.com/v1 or set HAVE_AI_BASE_URL / LITELLM_BASE_URL',\n });\n }\n\n return {\n ...options,\n baseUrl: normalizeBaseUrl(options.baseUrl.trim()),\n };\n}\n\nconst LITELLM_PROFILE: OpenAICompatibleProfile = {\n providerLabel: 'LiteLLM',\n providerName: 'litellm',\n defaultModel: 'gpt-4',\n capabilities: LITELLM_CAPABILITIES,\n describeModel: (modelId) => `LiteLLM model: ${modelId}`,\n getContextLength: inferLiteLLMContextLength,\n getModelCapabilities: inferLiteLLMCapabilities,\n shouldIncludeModel: (modelId) => !isFilteredLiteLLMModel(modelId),\n supportsFunctions: inferLiteLLMFunctions,\n supportsVision: inferLiteLLMVision,\n};\n\ntype LiteLLMResolvedCapability =\n | 'chat'\n | 'vision'\n | 'embeddings'\n | 'image_generation';\n\nfunction supportsCapability(\n model: AIModel,\n capability: LiteLLMResolvedCapability,\n): boolean {\n if (capability === 'vision') {\n return (\n model.capabilities.includes('vision') || model.supportsVision === true\n );\n }\n\n if (capability === 'embeddings') {\n return model.capabilities.includes('embeddings');\n }\n\n if (capability === 'image_generation') {\n return model.capabilities.includes('image_generation');\n }\n\n return model.capabilities.includes('chat');\n}\n\n/**\n * LiteLLM provider implementation.\n *\n * LiteLLM exposes an OpenAI-compatible API surface, so this provider reuses the\n * OpenAI transport while customizing provider identity, model discovery, and\n * capability heuristics for gateway-backed deployments.\n */\nexport class LiteLLMProvider extends OpenAIProvider {\n readonly admin: LiteLLMAdmin;\n private readonly configuredDefaultModel?: string;\n private readonly resolvedModelCache = new Map<\n LiteLLMResolvedCapability,\n Promise<string>\n >();\n\n constructor(options: LiteLLMOptions) {\n const normalized = ensureLiteLLMOptions(options);\n super(normalized, LITELLM_PROFILE);\n this.admin = new LiteLLMAdmin({\n provider: 'litellm',\n baseUrl: resolveGatewayAdminBaseUrl(\n normalized.baseUrl,\n normalized.adminUrl || normalized.adminBaseUrl,\n 'litellm',\n ),\n apiKey: normalized.adminApiKey || normalized.apiKey,\n headers: normalized.adminHeaders,\n timeout: normalized.timeout,\n });\n this.configuredDefaultModel = normalized.defaultModel;\n }\n\n private async resolveModel(\n capability: LiteLLMResolvedCapability,\n explicitModel?: string,\n ): Promise<string> {\n if (explicitModel) {\n return explicitModel;\n }\n\n if (\n this.configuredDefaultModel &&\n (capability === 'chat' || capability === 'vision')\n ) {\n return this.configuredDefaultModel;\n }\n\n const cached = this.resolvedModelCache.get(capability);\n if (cached) {\n return cached;\n }\n\n const pending = this.selectModel(capability).catch((error) => {\n this.resolvedModelCache.delete(capability);\n throw error;\n });\n this.resolvedModelCache.set(capability, pending);\n return pending;\n }\n\n private async selectModel(\n capability: LiteLLMResolvedCapability,\n ): Promise<string> {\n const models = await super.getModels();\n const model = models.find((candidate) =>\n supportsCapability(candidate, capability),\n );\n\n if (!model) {\n throw new ValidationError(\n `No ${capability} model is available from the LiteLLM gateway`,\n {\n provider: 'litellm',\n capability,\n hint: 'Pass defaultModel explicitly or ensure /models returns a compatible model for this key',\n },\n );\n }\n\n return model.id;\n }\n\n async chat(\n messages: AIMessage[],\n options: ChatOptions = {},\n ): Promise<AIResponse> {\n assertChatReasoningControls(options);\n const safe = normalizeChatOptions(\n this.options,\n options,\n 'litellm',\n options.model || this.options.defaultModel,\n );\n return super.chat(messages, {\n ...safe,\n model: await this.resolveModel('chat', safe.model),\n });\n }\n\n async *stream(\n messages: AIMessage[],\n options: ChatOptions = {},\n ): AsyncIterable<string> {\n assertChatReasoningControls(options);\n const safe = normalizeChatOptions(\n this.options,\n options,\n 'litellm',\n options.model || this.options.defaultModel,\n );\n yield* super.stream(messages, {\n ...safe,\n model: await this.resolveModel('chat', safe.model),\n });\n }\n\n async describeImage(\n image: string | Buffer,\n prompt?: string,\n options: ImageDescriptionOptions = {},\n ): Promise<string> {\n assertChatReasoningControls(options);\n const safe = normalizeChatOptions(\n this.options,\n options,\n 'litellm',\n options.model || this.options.defaultModel,\n );\n return super.describeImage(image, prompt, {\n ...safe,\n // The inherited image path calls this.chat(); do not reclassify the\n // normalization-inferred budget as an explicit caller control.\n reasoning: options.reasoning,\n model: await this.resolveModel('vision', safe.model),\n });\n }\n\n async embed(\n text: string | string[],\n options: EmbeddingOptions = {},\n ): Promise<EmbeddingResponse> {\n return super.embed(text, {\n ...options,\n model: await this.resolveModel('embeddings', options.model),\n });\n }\n\n async embedImage(\n image: string | Buffer,\n options: ImageEmbeddingOptions = {},\n ): Promise<EmbeddingResponse> {\n return super.embedImage(image, {\n ...options,\n model: await this.resolveModel('embeddings', options.model),\n });\n }\n\n async generateImage(\n prompt: string,\n options: ImageGenerationOptions = {},\n ): Promise<ImageGenerationResponse> {\n const safe = normalizeImageGenerationOptions(\n this.options,\n options,\n 'litellm',\n options.model,\n );\n return super.generateImage(prompt, {\n ...safe,\n model: await this.resolveModel('image_generation', safe.model),\n });\n }\n}\n"],"mappings":"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"}
|