@brownandroot/api 1.2.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -469
- package/dist/businessUnits.remote.d.ts +1 -1
- package/dist/businessUnits.remote.js +16 -1
- package/dist/cache.d.ts +51 -28
- package/dist/cache.js +193 -39
- package/dist/costcodes.remote.d.ts +1 -1
- package/dist/costcodes.remote.js +16 -1
- package/dist/employees.remote.d.ts +1 -1
- package/dist/employees.remote.js +16 -1
- package/dist/index.d.ts +63 -1
- package/dist/index.js +133 -14
- package/dist/jobtypejobsteps.remote.d.ts +1 -1
- package/dist/jobtypejobsteps.remote.js +16 -1
- package/dist/llm.remote.d.ts +1 -0
- package/dist/llm.remote.js +1 -0
- package/dist/paytypes.remote.d.ts +1 -1
- package/dist/paytypes.remote.js +16 -1
- package/dist/workorders.remote.d.ts +1 -1
- package/dist/workorders.remote.js +16 -1
- package/package.json +1 -37
package/dist/index.js
CHANGED
|
@@ -1,12 +1,69 @@
|
|
|
1
|
+
import { getLlmLogs, chat as llmChat } from './llm.remote.js';
|
|
2
|
+
import { listDocuments, uploadDocument, deleteDocument, searchDocuments } from './rag.remote.js';
|
|
3
|
+
export class ApiHubError extends Error {
|
|
4
|
+
status;
|
|
5
|
+
path;
|
|
6
|
+
url;
|
|
7
|
+
responseBody;
|
|
8
|
+
retriable;
|
|
9
|
+
service;
|
|
10
|
+
constructor(options) {
|
|
11
|
+
super(options.message);
|
|
12
|
+
this.name = 'ApiHubError';
|
|
13
|
+
this.status = options.status;
|
|
14
|
+
this.path = options.path;
|
|
15
|
+
this.url = options.url;
|
|
16
|
+
this.responseBody = options.responseBody;
|
|
17
|
+
this.retriable = options.retriable;
|
|
18
|
+
this.service = options.service ?? 'apihub';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
1
21
|
export class ApiHubClient {
|
|
2
22
|
baseUrl;
|
|
3
23
|
apiKey;
|
|
4
24
|
cacheTtl;
|
|
25
|
+
timeoutMs;
|
|
26
|
+
retryCount;
|
|
27
|
+
onError;
|
|
5
28
|
cache = new Map();
|
|
6
29
|
constructor(options) {
|
|
7
30
|
this.baseUrl = options.baseUrl.replace(/\/+$/, '');
|
|
8
31
|
this.apiKey = options.apiKey;
|
|
9
32
|
this.cacheTtl = options.cacheTtl ?? 5 * 60 * 1000;
|
|
33
|
+
this.timeoutMs = options.timeoutMs ?? 10_000;
|
|
34
|
+
this.retryCount = options.retryCount ?? 2;
|
|
35
|
+
this.onError = options.onError;
|
|
36
|
+
}
|
|
37
|
+
isRetriableStatus(status) {
|
|
38
|
+
return status === 429 || status === 502 || status === 503 || status === 504;
|
|
39
|
+
}
|
|
40
|
+
createApiHubError(options) {
|
|
41
|
+
const error = new ApiHubError({ ...options, service: 'apihub' });
|
|
42
|
+
this.onError?.(error);
|
|
43
|
+
if (typeof window === 'undefined') {
|
|
44
|
+
console.error('[APIHubError]', {
|
|
45
|
+
status: error.status,
|
|
46
|
+
path: error.path,
|
|
47
|
+
url: error.url,
|
|
48
|
+
retriable: error.retriable,
|
|
49
|
+
responseBody: error.responseBody,
|
|
50
|
+
message: error.message,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return error;
|
|
54
|
+
}
|
|
55
|
+
async fetchWithTimeout(url, init) {
|
|
56
|
+
const controller = new AbortController();
|
|
57
|
+
const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
58
|
+
try {
|
|
59
|
+
return await fetch(url, { ...init, signal: controller.signal });
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
clearTimeout(timeout);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async delay(ms) {
|
|
66
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
10
67
|
}
|
|
11
68
|
/** Clear all cached responses, or a specific path */
|
|
12
69
|
clearCache(path) {
|
|
@@ -32,22 +89,62 @@ export class ApiHubClient {
|
|
|
32
89
|
return data;
|
|
33
90
|
}
|
|
34
91
|
async request(path) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
res
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
92
|
+
const url = `${this.baseUrl}${path}`;
|
|
93
|
+
for (let attempt = 0; attempt <= this.retryCount; attempt++) {
|
|
94
|
+
let res;
|
|
95
|
+
try {
|
|
96
|
+
res = await this.fetchWithTimeout(url, {
|
|
97
|
+
headers: { 'x-api-key': this.apiKey },
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
const retriable = attempt < this.retryCount;
|
|
102
|
+
if (retriable) {
|
|
103
|
+
const jitter = Math.floor(Math.random() * 100);
|
|
104
|
+
await this.delay(150 * Math.pow(2, attempt) + jitter);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const message = error instanceof Error && error.name === 'AbortError'
|
|
108
|
+
? `APIHub request timed out after ${this.timeoutMs}ms`
|
|
109
|
+
: 'APIHub unavailable';
|
|
110
|
+
throw this.createApiHubError({
|
|
111
|
+
message,
|
|
112
|
+
status: null,
|
|
113
|
+
path,
|
|
114
|
+
url,
|
|
115
|
+
responseBody: null,
|
|
116
|
+
retriable: false,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
if (res.ok) {
|
|
120
|
+
return res.json();
|
|
121
|
+
}
|
|
45
122
|
const body = await res.json().catch(() => null);
|
|
123
|
+
const retriable = this.isRetriableStatus(res.status);
|
|
124
|
+
if (retriable && attempt < this.retryCount) {
|
|
125
|
+
const jitter = Math.floor(Math.random() * 100);
|
|
126
|
+
await this.delay(150 * Math.pow(2, attempt) + jitter);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
46
129
|
const message = (body && typeof body === 'object' && 'error' in body && typeof body.error === 'string' ? body.error : null) ??
|
|
47
130
|
`Request failed: ${res.status} ${res.statusText}`;
|
|
48
|
-
throw
|
|
131
|
+
throw this.createApiHubError({
|
|
132
|
+
message,
|
|
133
|
+
status: res.status,
|
|
134
|
+
path,
|
|
135
|
+
url,
|
|
136
|
+
responseBody: body,
|
|
137
|
+
retriable,
|
|
138
|
+
});
|
|
49
139
|
}
|
|
50
|
-
|
|
140
|
+
throw this.createApiHubError({
|
|
141
|
+
message: 'APIHub request failed after retries',
|
|
142
|
+
status: null,
|
|
143
|
+
path,
|
|
144
|
+
url,
|
|
145
|
+
responseBody: null,
|
|
146
|
+
retriable: false,
|
|
147
|
+
});
|
|
51
148
|
}
|
|
52
149
|
/** Get all employees */
|
|
53
150
|
async getEmployees() {
|
|
@@ -125,6 +222,11 @@ export class ApiHubClient {
|
|
|
125
222
|
}
|
|
126
223
|
/** Send a chat completion request to the LLM */
|
|
127
224
|
async chat(request) {
|
|
225
|
+
const { enableRag, ...rest } = request;
|
|
226
|
+
const payload = { ...rest };
|
|
227
|
+
if (enableRag !== undefined) {
|
|
228
|
+
payload.enableRag = enableRag;
|
|
229
|
+
}
|
|
128
230
|
let res;
|
|
129
231
|
try {
|
|
130
232
|
res = await fetch(`${this.baseUrl}/llm/chat`, {
|
|
@@ -133,7 +235,7 @@ export class ApiHubClient {
|
|
|
133
235
|
'x-api-key': this.apiKey,
|
|
134
236
|
'Content-Type': 'application/json',
|
|
135
237
|
},
|
|
136
|
-
body: JSON.stringify(
|
|
238
|
+
body: JSON.stringify(payload),
|
|
137
239
|
});
|
|
138
240
|
}
|
|
139
241
|
catch {
|
|
@@ -152,6 +254,12 @@ export class ApiHubClient {
|
|
|
152
254
|
// -----------------------------------------------------------------------
|
|
153
255
|
/** Start a streaming chat session — returns the raw SSE Response for proxying */
|
|
154
256
|
async chatStream(request) {
|
|
257
|
+
const { enableRag, ...rest } = request;
|
|
258
|
+
const payload = { ...rest };
|
|
259
|
+
if (enableRag !== undefined) {
|
|
260
|
+
// Streaming endpoint currently expects useRag in payload.
|
|
261
|
+
payload.useRag = enableRag;
|
|
262
|
+
}
|
|
155
263
|
let res;
|
|
156
264
|
try {
|
|
157
265
|
res = await fetch(`${this.baseUrl}/ai/chat/stream`, {
|
|
@@ -160,7 +268,7 @@ export class ApiHubClient {
|
|
|
160
268
|
'x-api-key': this.apiKey,
|
|
161
269
|
'Content-Type': 'application/json',
|
|
162
270
|
},
|
|
163
|
-
body: JSON.stringify(
|
|
271
|
+
body: JSON.stringify(payload),
|
|
164
272
|
});
|
|
165
273
|
}
|
|
166
274
|
catch {
|
|
@@ -333,3 +441,14 @@ export class ApiHubClient {
|
|
|
333
441
|
return this.request(`/jobtypejobsteps/${encodeURIComponent(id)}`);
|
|
334
442
|
}
|
|
335
443
|
}
|
|
444
|
+
export { employees, workorders, costcodes, paytypes, businessUnits, jobtypejobsteps, clearCache } from './cache.js';
|
|
445
|
+
export const llm = {
|
|
446
|
+
getLogs: getLlmLogs,
|
|
447
|
+
chat: llmChat,
|
|
448
|
+
};
|
|
449
|
+
export const rag = {
|
|
450
|
+
list: listDocuments,
|
|
451
|
+
upload: uploadDocument,
|
|
452
|
+
delete: deleteDocument,
|
|
453
|
+
search: searchDocuments,
|
|
454
|
+
};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export declare const getJobtypejobsteps: import("@sveltejs/kit").RemoteQueryFunction<void, import("./index.js").Jobtypejobstep[]>;
|
|
2
2
|
export declare const getJobtypejobstepsDropdown: import("@sveltejs/kit").RemoteQueryFunction<void, import("./index.js").DropdownOption[]>;
|
|
3
|
-
export declare const getJobtypejobstep: import("@sveltejs/kit").RemoteQueryFunction<string, import("./index.js").Jobtypejobstep>;
|
|
3
|
+
export declare const getJobtypejobstep: import("@sveltejs/kit").RemoteQueryFunction<string, import("./index.js").Jobtypejobstep | null>;
|
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
import { query } from '$app/server';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { getClient } from './client.js';
|
|
4
|
+
function isNotFoundError(error) {
|
|
5
|
+
if (!(error instanceof Error))
|
|
6
|
+
return false;
|
|
7
|
+
return /not found/i.test(error.message);
|
|
8
|
+
}
|
|
9
|
+
async function asNullable(fetcher) {
|
|
10
|
+
try {
|
|
11
|
+
return await fetcher();
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
if (isNotFoundError(error))
|
|
15
|
+
return null;
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
4
19
|
export const getJobtypejobsteps = query(async () => getClient().getJobtypejobsteps());
|
|
5
20
|
export const getJobtypejobstepsDropdown = query(async () => getClient().getJobtypejobstepsDropdown());
|
|
6
|
-
export const getJobtypejobstep = query(z.string(), async (id) => getClient().getJobtypejobstep(id));
|
|
21
|
+
export const getJobtypejobstep = query(z.string(), async (id) => asNullable(() => getClient().getJobtypejobstep(id)));
|
package/dist/llm.remote.d.ts
CHANGED
package/dist/llm.remote.js
CHANGED
|
@@ -12,6 +12,7 @@ const chatRequestSchema = z.object({
|
|
|
12
12
|
function: z.string().optional(),
|
|
13
13
|
temperature: z.number().min(0).max(2).optional(),
|
|
14
14
|
maxTokens: z.number().int().positive().optional(),
|
|
15
|
+
enableRag: z.boolean().optional(),
|
|
15
16
|
});
|
|
16
17
|
export const getLlmLogs = query(async () => getClient().getLlmLogs());
|
|
17
18
|
export const chat = command(chatRequestSchema, async (request) => getClient().chat(request));
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export declare const getPaytypes: import("@sveltejs/kit").RemoteQueryFunction<void, import("./index.js").Paytype[]>;
|
|
2
2
|
export declare const getPaytypesDropdown: import("@sveltejs/kit").RemoteQueryFunction<void, import("./index.js").PaytypeDropdownOption[]>;
|
|
3
|
-
export declare const getPaytype: import("@sveltejs/kit").RemoteQueryFunction<string, import("./index.js").Paytype>;
|
|
3
|
+
export declare const getPaytype: import("@sveltejs/kit").RemoteQueryFunction<string, import("./index.js").Paytype | null>;
|
package/dist/paytypes.remote.js
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
import { query } from '$app/server';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { getClient } from './client.js';
|
|
4
|
+
function isNotFoundError(error) {
|
|
5
|
+
if (!(error instanceof Error))
|
|
6
|
+
return false;
|
|
7
|
+
return /not found/i.test(error.message);
|
|
8
|
+
}
|
|
9
|
+
async function asNullable(fetcher) {
|
|
10
|
+
try {
|
|
11
|
+
return await fetcher();
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
if (isNotFoundError(error))
|
|
15
|
+
return null;
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
4
19
|
export const getPaytypes = query(async () => getClient().getPaytypes());
|
|
5
20
|
export const getPaytypesDropdown = query(async () => getClient().getPaytypesDropdown());
|
|
6
|
-
export const getPaytype = query(z.string(), async (id) => getClient().getPaytype(id));
|
|
21
|
+
export const getPaytype = query(z.string(), async (id) => asNullable(() => getClient().getPaytype(id)));
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export declare const getWorkorders: import("@sveltejs/kit").RemoteQueryFunction<void, import("./index.js").Workorder[]>;
|
|
2
2
|
export declare const getWorkordersDropdown: import("@sveltejs/kit").RemoteQueryFunction<void, import("./index.js").DropdownOption[]>;
|
|
3
|
-
export declare const getWorkorder: import("@sveltejs/kit").RemoteQueryFunction<string, import("./index.js").Workorder>;
|
|
3
|
+
export declare const getWorkorder: import("@sveltejs/kit").RemoteQueryFunction<string, import("./index.js").Workorder | null>;
|
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
import { query } from '$app/server';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { getClient } from './client.js';
|
|
4
|
+
function isNotFoundError(error) {
|
|
5
|
+
if (!(error instanceof Error))
|
|
6
|
+
return false;
|
|
7
|
+
return /not found/i.test(error.message);
|
|
8
|
+
}
|
|
9
|
+
async function asNullable(fetcher) {
|
|
10
|
+
try {
|
|
11
|
+
return await fetcher();
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
if (isNotFoundError(error))
|
|
15
|
+
return null;
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
4
19
|
export const getWorkorders = query(async () => getClient().getWorkorders());
|
|
5
20
|
export const getWorkordersDropdown = query(async () => getClient().getWorkordersDropdown());
|
|
6
|
-
export const getWorkorder = query(z.string(), async (id) => getClient().getWorkorder(id));
|
|
21
|
+
export const getWorkorder = query(z.string(), async (id) => asNullable(() => getClient().getWorkorder(id)));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brownandroot/api",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -13,42 +13,6 @@
|
|
|
13
13
|
".": {
|
|
14
14
|
"types": "./dist/index.d.ts",
|
|
15
15
|
"default": "./dist/index.js"
|
|
16
|
-
},
|
|
17
|
-
"./cache": {
|
|
18
|
-
"types": "./dist/cache.d.ts",
|
|
19
|
-
"default": "./dist/cache.js"
|
|
20
|
-
},
|
|
21
|
-
"./employees": {
|
|
22
|
-
"types": "./dist/employees.remote.d.ts",
|
|
23
|
-
"default": "./dist/employees.remote.js"
|
|
24
|
-
},
|
|
25
|
-
"./businessUnits": {
|
|
26
|
-
"types": "./dist/businessUnits.remote.d.ts",
|
|
27
|
-
"default": "./dist/businessUnits.remote.js"
|
|
28
|
-
},
|
|
29
|
-
"./costcodes": {
|
|
30
|
-
"types": "./dist/costcodes.remote.d.ts",
|
|
31
|
-
"default": "./dist/costcodes.remote.js"
|
|
32
|
-
},
|
|
33
|
-
"./paytypes": {
|
|
34
|
-
"types": "./dist/paytypes.remote.d.ts",
|
|
35
|
-
"default": "./dist/paytypes.remote.js"
|
|
36
|
-
},
|
|
37
|
-
"./workorders": {
|
|
38
|
-
"types": "./dist/workorders.remote.d.ts",
|
|
39
|
-
"default": "./dist/workorders.remote.js"
|
|
40
|
-
},
|
|
41
|
-
"./jobtypejobsteps": {
|
|
42
|
-
"types": "./dist/jobtypejobsteps.remote.d.ts",
|
|
43
|
-
"default": "./dist/jobtypejobsteps.remote.js"
|
|
44
|
-
},
|
|
45
|
-
"./llm": {
|
|
46
|
-
"types": "./dist/llm.remote.d.ts",
|
|
47
|
-
"default": "./dist/llm.remote.js"
|
|
48
|
-
},
|
|
49
|
-
"./rag": {
|
|
50
|
-
"types": "./dist/rag.remote.d.ts",
|
|
51
|
-
"default": "./dist/rag.remote.js"
|
|
52
16
|
}
|
|
53
17
|
},
|
|
54
18
|
"files": [
|