nuabase 1.2.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +100 -58
- package/dist/cjs/index.cjs +298 -405
- package/dist/esm/index.mjs +295 -405
- package/dist/types/__tests__/helpers/client-factory.d.ts +17 -0
- package/dist/types/__tests__/helpers/index.d.ts +1 -0
- package/dist/types/backend/direct.d.ts +17 -0
- package/dist/types/backend/gateway.d.ts +23 -0
- package/dist/types/backend/types.d.ts +61 -0
- package/dist/types/index.d.cts +5 -2
- package/dist/types/index.d.mts +5 -2
- package/dist/types/lib/auth.d.ts +12 -0
- package/dist/types/nua.d.ts +34 -3
- package/package.json +19 -17
- package/dist/types/__tests__/utils/sse.d.ts +0 -12
- package/dist/types/lib/api-client.d.ts +0 -19
- package/dist/types/nua/cast_array/factory.d.ts +0 -4
- package/dist/types/nua/cast_array/request-validation.d.ts +0 -1
- package/dist/types/nua/cast_array/response-schema.d.ts +0 -19
- package/dist/types/nua/cast_array/types.d.ts +0 -33
- package/dist/types/nua/cast_value/factory.d.ts +0 -4
- package/dist/types/nua/cast_value/response-schema.d.ts +0 -15
- package/dist/types/nua/cast_value/types.d.ts +0 -18
- package/dist/types/nua/common/normalized-usage.d.ts +0 -7
- package/dist/types/nua/common/queued-response.d.ts +0 -6
- package/dist/types/nua/core.d.ts +0 -12
- package/dist/types/nua/get-request/index.d.ts +0 -6
- package/dist/types/nua/get-request/types.d.ts +0 -34
- package/dist/types/nua/index.d.ts +0 -5
package/dist/cjs/index.cjs
CHANGED
|
@@ -1,7 +1,80 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var nuaLlmCore = require('nua-llm-core');
|
|
3
4
|
var zod = require('zod');
|
|
4
5
|
|
|
6
|
+
const normalizedUsageZero = {
|
|
7
|
+
promptTokens: 0,
|
|
8
|
+
completionTokens: 0,
|
|
9
|
+
totalTokens: 0
|
|
10
|
+
};
|
|
11
|
+
class DirectBackend {
|
|
12
|
+
constructor(config) {
|
|
13
|
+
if (!config.model) {
|
|
14
|
+
throw new Error("model is required for direct mode");
|
|
15
|
+
}
|
|
16
|
+
this.model = config.model;
|
|
17
|
+
this.client = new nuaLlmCore.NuaLlmClient({ providers: config.providers });
|
|
18
|
+
}
|
|
19
|
+
async castValue(params) {
|
|
20
|
+
const startTime = Date.now();
|
|
21
|
+
const result = await this.client.castValue({
|
|
22
|
+
model: this.model,
|
|
23
|
+
input: { prompt: params.prompt, data: params.data },
|
|
24
|
+
output: { name: params.outputName, effectiveSchema: params.outputSchema }
|
|
25
|
+
});
|
|
26
|
+
const latencyMs = Date.now() - startTime;
|
|
27
|
+
if (!result.success) {
|
|
28
|
+
return {
|
|
29
|
+
success: false,
|
|
30
|
+
error: result.error || "Unknown error",
|
|
31
|
+
source: "direct",
|
|
32
|
+
latencyMs
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
success: true,
|
|
37
|
+
data: result.data,
|
|
38
|
+
usage: result.usage || normalizedUsageZero,
|
|
39
|
+
model: this.model,
|
|
40
|
+
latencyMs,
|
|
41
|
+
source: "direct",
|
|
42
|
+
meta: {}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
async castArray(params) {
|
|
46
|
+
const startTime = Date.now();
|
|
47
|
+
const wrappedSchema = nuaLlmCore.wrapArraySchema(params.outputSchema, {
|
|
48
|
+
primaryKey: params.primaryKey,
|
|
49
|
+
outputName: params.outputName
|
|
50
|
+
});
|
|
51
|
+
const result = await this.client.castArray({
|
|
52
|
+
model: this.model,
|
|
53
|
+
data: params.data,
|
|
54
|
+
input: { prompt: params.prompt, primaryKey: params.primaryKey },
|
|
55
|
+
output: { name: params.outputName, effectiveSchema: wrappedSchema }
|
|
56
|
+
});
|
|
57
|
+
const latencyMs = Date.now() - startTime;
|
|
58
|
+
if (!result.success) {
|
|
59
|
+
return {
|
|
60
|
+
success: false,
|
|
61
|
+
error: result.error || "Unknown error",
|
|
62
|
+
source: "direct",
|
|
63
|
+
latencyMs
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
success: true,
|
|
68
|
+
data: result.data,
|
|
69
|
+
usage: result.usage || normalizedUsageZero,
|
|
70
|
+
model: this.model,
|
|
71
|
+
latencyMs,
|
|
72
|
+
source: "direct",
|
|
73
|
+
meta: {}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
5
78
|
function resolveConfigValue(config, key, envVar) {
|
|
6
79
|
if (config[key]) return config[key];
|
|
7
80
|
const pe = globalThis.process;
|
|
@@ -12,6 +85,50 @@ function resolveConfigValue(config, key, envVar) {
|
|
|
12
85
|
return void 0;
|
|
13
86
|
}
|
|
14
87
|
|
|
88
|
+
const isBrowserEnvironment = () => typeof window !== "undefined" && !!window.document;
|
|
89
|
+
class AuthTokenManager {
|
|
90
|
+
constructor(config) {
|
|
91
|
+
this.apiKey = resolveConfigValue(config, "apiKey", "NUABASE_API_KEY") || void 0;
|
|
92
|
+
this.fetchToken = config.fetchToken;
|
|
93
|
+
if (this.apiKey && this.fetchToken) {
|
|
94
|
+
throw new Error("Provide either an apiKey or a fetchToken function, but not both.");
|
|
95
|
+
}
|
|
96
|
+
if (!this.apiKey && !this.fetchToken) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
"Authentication is required. Provide config.apiKey/NUABASE_API_KEY or a fetchToken function."
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
if (this.apiKey && isBrowserEnvironment()) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
"found config.apiKey. It is a private secret that must only be used in server environments."
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async getToken() {
|
|
108
|
+
if (this.apiKey) {
|
|
109
|
+
return this.apiKey;
|
|
110
|
+
}
|
|
111
|
+
if (!this.fetchToken) {
|
|
112
|
+
throw new Error("Unable to resolve API token because no fetchToken function is configured.");
|
|
113
|
+
}
|
|
114
|
+
if (this.apiToken) {
|
|
115
|
+
return this.apiToken;
|
|
116
|
+
}
|
|
117
|
+
if (!this.pendingTokenPromise) {
|
|
118
|
+
this.pendingTokenPromise = this.fetchToken().then((token) => {
|
|
119
|
+
if (!token) {
|
|
120
|
+
throw new Error("fetchToken must resolve a non-empty API token.");
|
|
121
|
+
}
|
|
122
|
+
this.apiToken = token;
|
|
123
|
+
return token;
|
|
124
|
+
}).finally(() => {
|
|
125
|
+
this.pendingTokenPromise = void 0;
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
return this.pendingTokenPromise;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
15
132
|
async function parseErrorResponse(response, urlPath) {
|
|
16
133
|
const fallbackMessage = `Request to ${urlPath} failed with status ${response.status}${response.statusText ? ` ${response.statusText}` : ""}`;
|
|
17
134
|
let responseBody = "";
|
|
@@ -40,9 +157,17 @@ function safeParseJson(payload) {
|
|
|
40
157
|
}
|
|
41
158
|
}
|
|
42
159
|
function extractErrorMessage(body) {
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
160
|
+
const parts = [];
|
|
161
|
+
const error = body["error"];
|
|
162
|
+
if (typeof error === "string" && error.trim()) {
|
|
163
|
+
parts.push(error.trim());
|
|
164
|
+
}
|
|
165
|
+
const message = body["message"];
|
|
166
|
+
if (typeof message === "string" && message.trim()) {
|
|
167
|
+
parts.push(message.trim());
|
|
168
|
+
}
|
|
169
|
+
if (parts.length === 0) return void 0;
|
|
170
|
+
return parts.join(". ");
|
|
46
171
|
}
|
|
47
172
|
|
|
48
173
|
function isErrorWithMessage(error) {
|
|
@@ -55,447 +180,215 @@ function getErrorMessageFromException(error) {
|
|
|
55
180
|
return String(error);
|
|
56
181
|
}
|
|
57
182
|
|
|
58
|
-
|
|
59
|
-
|
|
183
|
+
function isErrorResponse(response) {
|
|
184
|
+
return Boolean(response && typeof response === "object" && "error" in response);
|
|
185
|
+
}
|
|
186
|
+
class GatewayBackend {
|
|
60
187
|
constructor(config) {
|
|
61
|
-
this.
|
|
62
|
-
this.
|
|
63
|
-
if (this.apiKey && this.fetchToken) {
|
|
64
|
-
throw new Error("Provide either an apiKey or a fetchToken function, but not both.");
|
|
65
|
-
}
|
|
66
|
-
if (!this.apiKey && !this.fetchToken) {
|
|
67
|
-
throw new Error(
|
|
68
|
-
"Authentication is required. Provide config.apiKey/NUABASE_API_KEY or a fetchToken function."
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
if (this.apiKey && isBrowserEnvironment()) {
|
|
72
|
-
throw new Error(
|
|
73
|
-
"found config.apiKey. It is a private secret that must only be used in server environments."
|
|
74
|
-
);
|
|
75
|
-
}
|
|
188
|
+
this.baseUrl = resolveConfigValue(config, "baseUrl", "NUABASE_API_URL") || "https://api.nuabase.com";
|
|
189
|
+
this.authManager = new AuthTokenManager(config);
|
|
76
190
|
}
|
|
77
|
-
async
|
|
78
|
-
|
|
79
|
-
|
|
191
|
+
async castValue(params) {
|
|
192
|
+
const startTime = Date.now();
|
|
193
|
+
const response = await this.post("cast/value/now", {
|
|
194
|
+
input: { prompt: params.prompt, data: params.data },
|
|
195
|
+
output: { name: params.outputName, schema: params.outputSchema }
|
|
196
|
+
});
|
|
197
|
+
const latencyMs = Date.now() - startTime;
|
|
198
|
+
if (isErrorResponse(response)) {
|
|
199
|
+
return { success: false, error: response.error, source: "gateway", latencyMs };
|
|
80
200
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
201
|
+
const apiResponse = response;
|
|
202
|
+
const usage = {
|
|
203
|
+
promptTokens: apiResponse.llmUsage.promptTokens + apiResponse.cacheUsage.promptTokens,
|
|
204
|
+
completionTokens: apiResponse.llmUsage.completionTokens + apiResponse.cacheUsage.completionTokens,
|
|
205
|
+
totalTokens: apiResponse.llmUsage.totalTokens + apiResponse.cacheUsage.totalTokens
|
|
206
|
+
};
|
|
207
|
+
return {
|
|
208
|
+
success: true,
|
|
209
|
+
data: apiResponse.data,
|
|
210
|
+
usage,
|
|
211
|
+
model: apiResponse.model ?? "unknown",
|
|
212
|
+
latencyMs,
|
|
213
|
+
source: "gateway",
|
|
214
|
+
meta: {
|
|
215
|
+
requestId: apiResponse.llmRequestId,
|
|
216
|
+
cached: apiResponse.isCacheHit,
|
|
217
|
+
llmUsage: apiResponse.llmUsage,
|
|
218
|
+
cacheUsage: apiResponse.cacheUsage
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
async castArray(params) {
|
|
223
|
+
const startTime = Date.now();
|
|
224
|
+
const response = await this.post("cast/array/now", {
|
|
225
|
+
input: { prompt: params.prompt, data: params.data, primaryKey: params.primaryKey },
|
|
226
|
+
output: { name: params.outputName, schema: params.outputSchema }
|
|
227
|
+
});
|
|
228
|
+
const latencyMs = Date.now() - startTime;
|
|
229
|
+
if (isErrorResponse(response)) {
|
|
230
|
+
return { success: false, error: response.error, source: "gateway", latencyMs };
|
|
86
231
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
232
|
+
const apiResponse = response;
|
|
233
|
+
const usage = {
|
|
234
|
+
promptTokens: apiResponse.llmUsage.promptTokens + apiResponse.cacheUsage.promptTokens,
|
|
235
|
+
completionTokens: apiResponse.llmUsage.completionTokens + apiResponse.cacheUsage.completionTokens,
|
|
236
|
+
totalTokens: apiResponse.llmUsage.totalTokens + apiResponse.cacheUsage.totalTokens
|
|
237
|
+
};
|
|
238
|
+
return {
|
|
239
|
+
success: true,
|
|
240
|
+
data: apiResponse.data,
|
|
241
|
+
usage,
|
|
242
|
+
model: apiResponse.model ?? "unknown",
|
|
243
|
+
latencyMs,
|
|
244
|
+
source: "gateway",
|
|
245
|
+
meta: {
|
|
246
|
+
requestId: apiResponse.llmRequestId,
|
|
247
|
+
cached: apiResponse.cacheHits === apiResponse.data.length,
|
|
248
|
+
llmUsage: apiResponse.llmUsage,
|
|
249
|
+
cacheUsage: apiResponse.cacheUsage
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
async queueCastValue(params) {
|
|
254
|
+
const response = await this.post("cast/value", {
|
|
255
|
+
input: { prompt: params.prompt, data: params.data },
|
|
256
|
+
output: { name: params.outputName, schema: params.outputSchema }
|
|
257
|
+
});
|
|
258
|
+
if (isErrorResponse(response)) {
|
|
259
|
+
return { success: false, error: response.error, source: "gateway" };
|
|
97
260
|
}
|
|
98
|
-
|
|
261
|
+
const apiResponse = response;
|
|
262
|
+
return { success: true, jobId: apiResponse.jobId, source: "gateway" };
|
|
99
263
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
264
|
+
async queueCastArray(params) {
|
|
265
|
+
const response = await this.post("cast/array", {
|
|
266
|
+
input: { prompt: params.prompt, data: params.data, primaryKey: params.primaryKey },
|
|
267
|
+
output: { name: params.outputName, schema: params.outputSchema }
|
|
268
|
+
});
|
|
269
|
+
if (isErrorResponse(response)) {
|
|
270
|
+
return { success: false, error: response.error, source: "gateway" };
|
|
271
|
+
}
|
|
272
|
+
const apiResponse = response;
|
|
273
|
+
return { success: true, jobId: apiResponse.jobId, source: "gateway" };
|
|
105
274
|
}
|
|
106
|
-
async
|
|
107
|
-
const url = `${this.baseUrl}/${
|
|
275
|
+
async post(path, body) {
|
|
276
|
+
const url = `${this.baseUrl}/${path}`;
|
|
108
277
|
let response;
|
|
109
|
-
const hasBody = method !== "GET" && params !== void 0;
|
|
110
278
|
const headers = {
|
|
111
|
-
Authorization: `Bearer ${await this.
|
|
279
|
+
Authorization: `Bearer ${await this.authManager.getToken()}`,
|
|
280
|
+
"Content-Type": "application/json"
|
|
112
281
|
};
|
|
113
|
-
if (hasBody) {
|
|
114
|
-
headers["Content-Type"] = "application/json";
|
|
115
|
-
}
|
|
116
282
|
try {
|
|
117
283
|
response = await fetch(url, {
|
|
118
|
-
method,
|
|
284
|
+
method: "POST",
|
|
119
285
|
headers,
|
|
120
|
-
body:
|
|
286
|
+
body: JSON.stringify(body)
|
|
121
287
|
});
|
|
122
288
|
} catch (e) {
|
|
123
289
|
return { error: `Error calling Nuabase API: ${getErrorMessageFromException(e)}` };
|
|
124
290
|
}
|
|
125
|
-
let result;
|
|
126
291
|
if (!response.ok) {
|
|
127
|
-
return await parseErrorResponse(response,
|
|
292
|
+
return await parseErrorResponse(response, path);
|
|
128
293
|
}
|
|
129
294
|
try {
|
|
130
|
-
|
|
295
|
+
return await response.json();
|
|
131
296
|
} catch {
|
|
132
297
|
return { error: "Invalid response received from Nuabase API call, unable to parse" };
|
|
133
298
|
}
|
|
134
|
-
return result;
|
|
135
299
|
}
|
|
136
300
|
}
|
|
137
301
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}).strict();
|
|
142
|
-
|
|
143
|
-
const validateArrayRequestParams = (data, primaryKeyName) => {
|
|
144
|
-
if (!Array.isArray(data)) {
|
|
145
|
-
throw new Error("`data` must be an array of objects.");
|
|
302
|
+
class Nua {
|
|
303
|
+
constructor(backend) {
|
|
304
|
+
this.backend = backend;
|
|
146
305
|
}
|
|
147
|
-
|
|
148
|
-
|
|
306
|
+
static gateway(config) {
|
|
307
|
+
return new Nua(new GatewayBackend(config));
|
|
149
308
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
throw new Error(`data[${index}] must be a non-null object.`);
|
|
153
|
-
}
|
|
154
|
-
if (!(primaryKeyName in item)) {
|
|
155
|
-
throw new Error(`data[${index}] must contain property '${primaryKeyName}'.`);
|
|
156
|
-
}
|
|
157
|
-
});
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
const zs_NormalizedUsage = zod.z.object({
|
|
161
|
-
promptTokens: zod.z.number(),
|
|
162
|
-
completionTokens: zod.z.number(),
|
|
163
|
-
totalTokens: zod.z.number()
|
|
164
|
-
}).strict();
|
|
165
|
-
|
|
166
|
-
const zs_ResponseRecord = (primaryKeyName, outputKey, outputSchema) => {
|
|
167
|
-
const primaryKeySchema = zod.z.any();
|
|
168
|
-
const shape = {
|
|
169
|
-
[primaryKeyName]: primaryKeySchema,
|
|
170
|
-
[outputKey]: outputSchema
|
|
171
|
-
};
|
|
172
|
-
return zod.z.object(shape).strict();
|
|
173
|
-
};
|
|
174
|
-
const createNuaApiResponseCastArray = (primaryKeyName, outputKey, outputSchema) => {
|
|
175
|
-
const recordSchema = zs_ResponseRecord(
|
|
176
|
-
primaryKeyName,
|
|
177
|
-
outputKey,
|
|
178
|
-
outputSchema
|
|
179
|
-
);
|
|
180
|
-
const dataSchema = zod.z.array(recordSchema);
|
|
181
|
-
return zod.z.object({
|
|
182
|
-
llmRequestId: zod.z.string(),
|
|
183
|
-
kind: zod.z.literal("cast/array"),
|
|
184
|
-
data: dataSchema,
|
|
185
|
-
cacheHits: zod.z.number(),
|
|
186
|
-
rowsWithNoResults: zod.z.array(zod.z.string()),
|
|
187
|
-
isSuccess: zod.z.literal(true),
|
|
188
|
-
isError: zod.z.optional(zod.z.literal(false)),
|
|
189
|
-
usage: zs_NormalizedUsage
|
|
190
|
-
}).strict();
|
|
191
|
-
};
|
|
192
|
-
const zs_NuaApiResponse_CastArray = createNuaApiResponseCastArray;
|
|
193
|
-
|
|
194
|
-
const zs_ArrayFnDef = zod.z.object({
|
|
195
|
-
prompt: zod.z.string(),
|
|
196
|
-
output: zod.z.object({
|
|
197
|
-
name: zod.z.string(),
|
|
198
|
-
schema: zod.z.custom(
|
|
199
|
-
(value) => value instanceof zod.z.ZodType,
|
|
200
|
-
"Expected a Zod schema"
|
|
201
|
-
)
|
|
202
|
-
})
|
|
203
|
-
});
|
|
204
|
-
const validateArrayFnDef = (fnDef) => {
|
|
205
|
-
const parsed = zs_ArrayFnDef.safeParse(fnDef);
|
|
206
|
-
if (!parsed.success) {
|
|
207
|
-
const message = zod.z.prettifyError(parsed.error);
|
|
208
|
-
throw new TypeError(`Invalid array function definition: ${message}`);
|
|
309
|
+
static direct(config) {
|
|
310
|
+
return new Nua(new DirectBackend(config));
|
|
209
311
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
const lookup = /* @__PURE__ */ new Map();
|
|
215
|
-
for (const row of inputRows) {
|
|
216
|
-
lookup.set(row[primaryKeyName], row);
|
|
217
|
-
}
|
|
218
|
-
return outputRows.map((row) => {
|
|
219
|
-
const primaryKeyValue = row[primaryKeyName];
|
|
220
|
-
const sourceRow = lookup.get(primaryKeyValue);
|
|
221
|
-
if (!sourceRow) {
|
|
222
|
-
throw new Error(
|
|
223
|
-
`Unable to locate source row for primary key "${String(primaryKeyValue)}" in cast array response.`
|
|
224
|
-
);
|
|
312
|
+
// Implementation
|
|
313
|
+
async get(prompt, options) {
|
|
314
|
+
if (typeof prompt !== "string" || prompt.trim() === "") {
|
|
315
|
+
throw new TypeError("Prompt must be a non-empty string");
|
|
225
316
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
data,
|
|
236
|
-
primaryKey: primaryKeyName
|
|
237
|
-
},
|
|
238
|
-
output: {
|
|
239
|
-
schema: outputSchema,
|
|
240
|
-
name: outputName
|
|
317
|
+
const outputSchema = options?.output?.schema ?? zod.z.string();
|
|
318
|
+
const outputName = options?.output?.name ?? "result";
|
|
319
|
+
const jsonSchema = zod.z.toJSONSchema(outputSchema);
|
|
320
|
+
return this.backend.castValue({
|
|
321
|
+
prompt,
|
|
322
|
+
data: options?.input ?? null,
|
|
323
|
+
outputName,
|
|
324
|
+
outputSchema: jsonSchema
|
|
325
|
+
});
|
|
241
326
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
const toNuabaseError = (response) => {
|
|
247
|
-
if (response && typeof response === "object" && "error" in response)
|
|
248
|
-
return { error: String(response.error), isError: true };
|
|
249
|
-
return void 0;
|
|
250
|
-
};
|
|
251
|
-
const queueFn = async (data, primaryKeyName) => {
|
|
252
|
-
validateArrayRequestParams(data, primaryKeyName);
|
|
253
|
-
const response = await client.request(
|
|
254
|
-
"POST",
|
|
255
|
-
"cast/array",
|
|
256
|
-
toCastArrayApiRequest(
|
|
257
|
-
fnDef.prompt,
|
|
258
|
-
data,
|
|
259
|
-
primaryKeyName,
|
|
260
|
-
fnDef.output.name,
|
|
261
|
-
outputJsonSchema
|
|
262
|
-
)
|
|
263
|
-
);
|
|
264
|
-
const error = toNuabaseError(response);
|
|
265
|
-
if (error) return error;
|
|
266
|
-
const parsedResponse = zs_NuaQueuedResponse.safeParse(response);
|
|
267
|
-
if (!parsedResponse.success) {
|
|
268
|
-
const emsg = zod.z.prettifyError(parsedResponse.error);
|
|
269
|
-
return { error: `Invalid queued response received from Nuabase API: ${emsg}`, isError: true };
|
|
327
|
+
// ========== list() - Array Operations ==========
|
|
328
|
+
async list(prompt, options) {
|
|
329
|
+
if (typeof prompt !== "string" || prompt.trim() === "") {
|
|
330
|
+
throw new TypeError("Prompt must be a non-empty string");
|
|
270
331
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
const baseNowFn = async (data, primaryKeyName) => {
|
|
274
|
-
validateArrayRequestParams(data, primaryKeyName);
|
|
275
|
-
const response = await client.request(
|
|
276
|
-
"POST",
|
|
277
|
-
"cast/array/now",
|
|
278
|
-
toCastArrayApiRequest(
|
|
279
|
-
fnDef.prompt,
|
|
280
|
-
data,
|
|
281
|
-
primaryKeyName,
|
|
282
|
-
fnDef.output.name,
|
|
283
|
-
outputJsonSchema
|
|
284
|
-
)
|
|
285
|
-
);
|
|
286
|
-
const error = toNuabaseError(response);
|
|
287
|
-
if (error) return error;
|
|
288
|
-
const outputKey = fnDef.output.name;
|
|
289
|
-
const successSchema = zs_NuaApiResponse_CastArray(primaryKeyName, outputKey, fnDef.output.schema);
|
|
290
|
-
const parsedResponse = successSchema.safeParse(response);
|
|
291
|
-
if (!parsedResponse.success) {
|
|
292
|
-
const emsg = zod.z.prettifyError(parsedResponse.error);
|
|
293
|
-
return { error: `Invalid data received from Nuabase API: ${emsg}`, isError: true };
|
|
332
|
+
if (!Array.isArray(options.input)) {
|
|
333
|
+
throw new TypeError("Input must be an array for list()");
|
|
294
334
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
usage: parsedResponse.data.usage
|
|
307
|
-
};
|
|
308
|
-
};
|
|
309
|
-
return Object.assign(baseNowFn, {
|
|
310
|
-
queue: queueFn
|
|
311
|
-
});
|
|
312
|
-
};
|
|
313
|
-
|
|
314
|
-
const createNuaApiResponseCastValue = (outputKey, outputSchema) => {
|
|
315
|
-
return zod.z.object({
|
|
316
|
-
llmRequestId: zod.z.string(),
|
|
317
|
-
kind: zod.z.literal("cast/value"),
|
|
318
|
-
data: outputSchema,
|
|
319
|
-
isCacheHit: zod.z.boolean(),
|
|
320
|
-
isSuccess: zod.z.literal(true),
|
|
321
|
-
isError: zod.z.optional(zod.z.literal(false)),
|
|
322
|
-
usage: zs_NormalizedUsage
|
|
323
|
-
}).strict();
|
|
324
|
-
};
|
|
325
|
-
const zs_NuaApiResponse_CastValue = createNuaApiResponseCastValue;
|
|
326
|
-
|
|
327
|
-
const zs_ValueFnDef = zod.z.object({
|
|
328
|
-
prompt: zod.z.string(),
|
|
329
|
-
output: zod.z.object({
|
|
330
|
-
name: zod.z.string(),
|
|
331
|
-
schema: zod.z.custom(
|
|
332
|
-
(value) => value instanceof zod.z.ZodType,
|
|
333
|
-
"Expected a Zod schema for `output.schema`"
|
|
334
|
-
)
|
|
335
|
-
})
|
|
336
|
-
});
|
|
337
|
-
const validateValueFnDef = (fnDef) => {
|
|
338
|
-
const parsed = zs_ValueFnDef.safeParse(fnDef);
|
|
339
|
-
if (!parsed.success) {
|
|
340
|
-
const message = zod.z.prettifyError(parsed.error);
|
|
341
|
-
throw new TypeError(`Invalid value function definition: ${message}`);
|
|
335
|
+
if (typeof options.primaryKey !== "string" || options.primaryKey.trim() === "") {
|
|
336
|
+
throw new TypeError("primaryKey must be a non-empty string");
|
|
337
|
+
}
|
|
338
|
+
const jsonSchema = zod.z.toJSONSchema(options.output.schema);
|
|
339
|
+
return this.backend.castArray({
|
|
340
|
+
prompt,
|
|
341
|
+
data: options.input,
|
|
342
|
+
primaryKey: options.primaryKey,
|
|
343
|
+
outputName: options.output.name,
|
|
344
|
+
outputSchema: jsonSchema
|
|
345
|
+
});
|
|
342
346
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
347
|
+
// Implementation
|
|
348
|
+
async queueGet(prompt, options) {
|
|
349
|
+
if (typeof prompt !== "string" || prompt.trim() === "") {
|
|
350
|
+
throw new TypeError("Prompt must be a non-empty string");
|
|
351
|
+
}
|
|
352
|
+
if (!this.backend.queueCastValue) {
|
|
353
|
+
throw new Error("Queue operations are only supported in gateway mode");
|
|
354
|
+
}
|
|
355
|
+
const outputSchema = options?.output?.schema ?? zod.z.string();
|
|
356
|
+
const outputName = options?.output?.name ?? "result";
|
|
357
|
+
const jsonSchema = zod.z.toJSONSchema(outputSchema);
|
|
358
|
+
return this.backend.queueCastValue({
|
|
359
|
+
prompt,
|
|
360
|
+
data: options?.input ?? null,
|
|
361
|
+
outputName,
|
|
362
|
+
outputSchema: jsonSchema
|
|
363
|
+
});
|
|
354
364
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
const toNuabaseError = (response) => {
|
|
360
|
-
if (response && typeof response === "object" && "error" in response)
|
|
361
|
-
return { error: String(response.error), isError: true };
|
|
362
|
-
return void 0;
|
|
363
|
-
};
|
|
364
|
-
const sendRequest = async (path, data) => client.request(
|
|
365
|
-
"POST",
|
|
366
|
-
path,
|
|
367
|
-
toCastValueApiRequest(fnDef.prompt, data, fnDef.output.name, outputJsonSchema)
|
|
368
|
-
);
|
|
369
|
-
const queueFn = async (data) => {
|
|
370
|
-
const response = await sendRequest("cast/value", data);
|
|
371
|
-
const error = toNuabaseError(response);
|
|
372
|
-
if (error) return error;
|
|
373
|
-
const parsedResponse = zs_NuaQueuedResponse.safeParse(response);
|
|
374
|
-
if (!parsedResponse.success) {
|
|
375
|
-
const emsg = zod.z.prettifyError(parsedResponse.error);
|
|
376
|
-
return { error: `Invalid queued response received from Nuabase API: ${emsg}`, isError: true };
|
|
365
|
+
// ========== queueList() - Async Array Operations ==========
|
|
366
|
+
async queueList(prompt, options) {
|
|
367
|
+
if (typeof prompt !== "string" || prompt.trim() === "") {
|
|
368
|
+
throw new TypeError("Prompt must be a non-empty string");
|
|
377
369
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
const baseNowFn = async (data) => {
|
|
381
|
-
const response = await sendRequest("cast/value/now", data);
|
|
382
|
-
const error = toNuabaseError(response);
|
|
383
|
-
if (error) return error;
|
|
384
|
-
const outputKey = fnDef.output.name;
|
|
385
|
-
const successSchema = zs_NuaApiResponse_CastValue(
|
|
386
|
-
outputKey,
|
|
387
|
-
fnDef.output.schema
|
|
388
|
-
);
|
|
389
|
-
const parsedResponse = successSchema.safeParse(response);
|
|
390
|
-
if (!parsedResponse.success) {
|
|
391
|
-
const emsg = zod.z.prettifyError(parsedResponse.error);
|
|
392
|
-
return { error: `Invalid data received from Nuabase API: ${emsg}`, isError: true };
|
|
370
|
+
if (!this.backend.queueCastArray) {
|
|
371
|
+
throw new Error("Queue operations are only supported in gateway mode");
|
|
393
372
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
llmRequestId: parsedResponse.data.llmRequestId,
|
|
397
|
-
kind: parsedResponse.data.kind,
|
|
398
|
-
data: parsedResponse.data.data,
|
|
399
|
-
isCacheHit: parsedResponse.data.isCacheHit,
|
|
400
|
-
usage: parsedResponse.data.usage
|
|
401
|
-
};
|
|
402
|
-
};
|
|
403
|
-
return Object.assign(baseNowFn, {
|
|
404
|
-
queue: queueFn
|
|
405
|
-
});
|
|
406
|
-
};
|
|
407
|
-
|
|
408
|
-
const errorParseFailed = {
|
|
409
|
-
kind: "internal-error",
|
|
410
|
-
message: "Unable to parse stored LLM result"
|
|
411
|
-
};
|
|
412
|
-
const dateFromIso = zod.z.preprocess(
|
|
413
|
-
(value) => {
|
|
414
|
-
if (value == null) return null;
|
|
415
|
-
if (value instanceof Date) return value;
|
|
416
|
-
if (typeof value === "string") {
|
|
417
|
-
const parsed = new Date(value);
|
|
418
|
-
if (!Number.isNaN(parsed.getTime())) return parsed;
|
|
373
|
+
if (!Array.isArray(options.input)) {
|
|
374
|
+
throw new TypeError("Input must be an array for queueList()");
|
|
419
375
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
);
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
}, zod.z.date());
|
|
432
|
-
const apiResponseSchema = zod.z.object({
|
|
433
|
-
id: zod.z.string(),
|
|
434
|
-
requestType: zod.z.string(),
|
|
435
|
-
llmStatus: zod.z.union([
|
|
436
|
-
zod.z.literal("pending"),
|
|
437
|
-
zod.z.literal("processing"),
|
|
438
|
-
zod.z.literal("success"),
|
|
439
|
-
zod.z.literal("failed")
|
|
440
|
-
]),
|
|
441
|
-
sseStatus: zod.z.union([
|
|
442
|
-
zod.z.literal("n/a"),
|
|
443
|
-
zod.z.literal("pending"),
|
|
444
|
-
zod.z.literal("sent"),
|
|
445
|
-
zod.z.literal("failed")
|
|
446
|
-
]),
|
|
447
|
-
webhookStatus: zod.z.union([
|
|
448
|
-
zod.z.literal("n/a"),
|
|
449
|
-
zod.z.literal("pending"),
|
|
450
|
-
zod.z.literal("sent"),
|
|
451
|
-
zod.z.literal("failed")
|
|
452
|
-
]),
|
|
453
|
-
input: zod.z.object({
|
|
454
|
-
prompt: zod.z.union([zod.z.string(), zod.z.null()]),
|
|
455
|
-
data: zod.z.union([zod.z.string(), zod.z.null()]),
|
|
456
|
-
primaryKey: zod.z.union([zod.z.string(), zod.z.null()])
|
|
457
|
-
}),
|
|
458
|
-
output: zod.z.object({
|
|
459
|
-
name: zod.z.string(),
|
|
460
|
-
schema: zod.z.string(),
|
|
461
|
-
effectiveSchema: zod.z.string()
|
|
462
|
-
}),
|
|
463
|
-
result: zod.z.union([zod.z.record(zod.z.string(), zod.z.unknown()), zod.z.null()]).optional(),
|
|
464
|
-
error: zod.z.union([zod.z.string(), zod.z.null()]),
|
|
465
|
-
fullPrompt: zod.z.string(),
|
|
466
|
-
model: zod.z.string(),
|
|
467
|
-
provider: zod.z.string(),
|
|
468
|
-
startedAt: dateFromIso,
|
|
469
|
-
finishedAt: dateFromIso,
|
|
470
|
-
createdAt: mandatoryDateFromIso,
|
|
471
|
-
updatedAt: mandatoryDateFromIso
|
|
472
|
-
});
|
|
473
|
-
const isNuabaseError = (value) => Boolean(value && typeof value === "object" && "isError" in value && value.isError);
|
|
474
|
-
async function getRequest(client, llmRequestId) {
|
|
475
|
-
const response = await client.request("GET", `requests/${encodeURIComponent(llmRequestId)}`);
|
|
476
|
-
if (isNuabaseError(response)) {
|
|
477
|
-
return response;
|
|
478
|
-
}
|
|
479
|
-
const parsedResponse = apiResponseSchema.safeParse(response);
|
|
480
|
-
if (!parsedResponse.success) {
|
|
481
|
-
return errorParseFailed;
|
|
482
|
-
}
|
|
483
|
-
return parsedResponse.data;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
class Nua {
|
|
487
|
-
constructor(config) {
|
|
488
|
-
this.apiClient = new NuabaseAPIClient(config);
|
|
489
|
-
}
|
|
490
|
-
createArrayFn(fnDef) {
|
|
491
|
-
return createArrayFn(this.apiClient, fnDef);
|
|
492
|
-
}
|
|
493
|
-
createFn(fnDef) {
|
|
494
|
-
return createValueFn(this.apiClient, fnDef);
|
|
495
|
-
}
|
|
496
|
-
async getRequest(llmRequestId) {
|
|
497
|
-
return getRequest(this.apiClient, llmRequestId);
|
|
376
|
+
if (typeof options.primaryKey !== "string" || options.primaryKey.trim() === "") {
|
|
377
|
+
throw new TypeError("primaryKey must be a non-empty string");
|
|
378
|
+
}
|
|
379
|
+
const jsonSchema = zod.z.toJSONSchema(options.output.schema);
|
|
380
|
+
return this.backend.queueCastArray({
|
|
381
|
+
prompt,
|
|
382
|
+
data: options.input,
|
|
383
|
+
primaryKey: options.primaryKey,
|
|
384
|
+
outputName: options.output.name,
|
|
385
|
+
outputSchema: jsonSchema
|
|
386
|
+
});
|
|
498
387
|
}
|
|
499
388
|
}
|
|
500
389
|
|
|
390
|
+
Object.defineProperty(exports, "z", {
|
|
391
|
+
enumerable: true,
|
|
392
|
+
get: function () { return zod.z; }
|
|
393
|
+
});
|
|
501
394
|
exports.Nua = Nua;
|