@umituz/web-ai 1.2.0 → 1.4.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 +6 -0
- package/dist/core/index.cjs +110 -13
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +51 -2
- package/dist/core/index.d.ts +51 -2
- package/dist/core/index.js +103 -14
- package/dist/core/index.js.map +1 -1
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.d.cts +1 -1
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.cjs +119 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +111 -14
- package/dist/index.js.map +1 -1
- package/dist/providers/index.cjs +13 -0
- package/dist/providers/index.cjs.map +1 -1
- package/dist/providers/index.d.cts +6 -1
- package/dist/providers/index.d.ts +6 -1
- package/dist/providers/index.js +13 -1
- package/dist/providers/index.js.map +1 -1
- package/dist/{remote-chat-client-LrNlew4n.d.cts → remote-chat-client-DnmLTBUA.d.cts} +11 -1
- package/dist/{remote-chat-client-LrNlew4n.d.ts → remote-chat-client-DnmLTBUA.d.ts} +11 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -90,6 +90,12 @@ The pieces a backend-proxy chat stack needs, all persistence-free:
|
|
|
90
90
|
- `buildUsageDailySeries` / `usageWindowTotals` / `usageTrendOf` /
|
|
91
91
|
`errorRatePct` — turn sparse SQL day aggregates into zero-filled usage
|
|
92
92
|
series and dashboard math.
|
|
93
|
+
- `deriveConversationTitle(content, { fallback, maxChars })` — first-line
|
|
94
|
+
title derivation for new conversations (Markdown prefix strip, collapse,
|
|
95
|
+
truncate).
|
|
96
|
+
- `planStuckRecovery(task, now)` / `STUCK_TASK_THRESHOLD_MS` — recovery
|
|
97
|
+
decision for queue rows found stale in `running`; sits next to
|
|
98
|
+
`canStartTask` / `planTaskFailure`.
|
|
93
99
|
|
|
94
100
|
## Client-side chat over your own backend
|
|
95
101
|
|
package/dist/core/index.cjs
CHANGED
|
@@ -322,6 +322,31 @@ function aiErrorStatus(code) {
|
|
|
322
322
|
return AI_ERROR_STATUS[code];
|
|
323
323
|
}
|
|
324
324
|
|
|
325
|
+
// src/core/errors/ai-failure.ts
|
|
326
|
+
var TYPE_TO_CODE = {
|
|
327
|
+
["INVALID_API_KEY" /* INVALID_API_KEY */]: "invalid_key",
|
|
328
|
+
["MISSING_CONFIG" /* MISSING_CONFIG */]: "missing_key",
|
|
329
|
+
["NETWORK_ERROR" /* NETWORK_ERROR */]: "network",
|
|
330
|
+
["ABORT_ERROR" /* ABORT_ERROR */]: "timeout",
|
|
331
|
+
["RATE_LIMIT_ERROR" /* RATE_LIMIT_ERROR */]: "rate_limited",
|
|
332
|
+
["SERVER_ERROR" /* SERVER_ERROR */]: "upstream_error",
|
|
333
|
+
["CLIENT_ERROR" /* CLIENT_ERROR */]: "invalid_request",
|
|
334
|
+
["UNKNOWN_ERROR" /* UNKNOWN_ERROR */]: "upstream_error"
|
|
335
|
+
};
|
|
336
|
+
function aiFailureOf(error) {
|
|
337
|
+
if (error instanceof AiError) {
|
|
338
|
+
return { code: error.code, status: AI_ERROR_STATUS[error.code], message: error.userMessage };
|
|
339
|
+
}
|
|
340
|
+
if (error instanceof AIError) {
|
|
341
|
+
const code = TYPE_TO_CODE[error.type];
|
|
342
|
+
return { code, status: AI_ERROR_STATUS[code], message: CODE_MESSAGES[code] };
|
|
343
|
+
}
|
|
344
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
345
|
+
return { code: "timeout", status: AI_ERROR_STATUS.timeout, message: CODE_MESSAGES.timeout };
|
|
346
|
+
}
|
|
347
|
+
return { code: "upstream_error", status: AI_ERROR_STATUS.upstream_error, message: CODE_MESSAGES.upstream_error };
|
|
348
|
+
}
|
|
349
|
+
|
|
325
350
|
// src/core/executor/retry-executor.strategy.ts
|
|
326
351
|
var RetryExecutor = class {
|
|
327
352
|
constructor(retryManager2, options) {
|
|
@@ -925,6 +950,34 @@ function failoverCandidate(primary, fallback) {
|
|
|
925
950
|
return fallback;
|
|
926
951
|
}
|
|
927
952
|
|
|
953
|
+
// src/core/utils/title.ts
|
|
954
|
+
function deriveConversationTitle(content, options = {}) {
|
|
955
|
+
const fallback = options.fallback ?? "";
|
|
956
|
+
const maxChars = options.maxChars ?? 60;
|
|
957
|
+
const firstLine = content.split("\n").map((line) => line.trim()).find((line) => line.length > 0);
|
|
958
|
+
const cleaned = (firstLine ?? fallback).replace(/^#{1,6}\s+/, "").replace(/^[>*-]\s+/, "").replace(/\s+/g, " ");
|
|
959
|
+
if (cleaned.length === 0) return fallback;
|
|
960
|
+
return cleaned.length > maxChars ? `${cleaned.slice(0, maxChars).trimEnd()}\u2026` : cleaned;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
// src/core/utils/allowance-window.ts
|
|
964
|
+
var DEFAULT_ALLOWANCE_WINDOW_MS = 36e5;
|
|
965
|
+
function createAllowanceWindow(options) {
|
|
966
|
+
const windowMs = options.windowMs ?? DEFAULT_ALLOWANCE_WINDOW_MS;
|
|
967
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
968
|
+
return {
|
|
969
|
+
allow(key, now = Date.now()) {
|
|
970
|
+
const bucket = buckets.get(key);
|
|
971
|
+
if (bucket === void 0 || now - bucket.windowStart >= windowMs) {
|
|
972
|
+
buckets.set(key, { windowStart: now, count: 1 });
|
|
973
|
+
return true;
|
|
974
|
+
}
|
|
975
|
+
bucket.count += 1;
|
|
976
|
+
return bucket.count <= options.limit;
|
|
977
|
+
}
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
|
|
928
981
|
// src/core/constants/protocol.constants.ts
|
|
929
982
|
var SSE_PREFIX = "data: ";
|
|
930
983
|
var SSE_DONE_MARKER = "[DONE]";
|
|
@@ -1304,6 +1357,11 @@ function planTaskFailure(task, errorDetail, backoffBaseMs, now = /* @__PURE__ */
|
|
|
1304
1357
|
}
|
|
1305
1358
|
};
|
|
1306
1359
|
}
|
|
1360
|
+
var STUCK_TASK_THRESHOLD_MS = 15 * 60 * 1e3;
|
|
1361
|
+
function planStuckRecovery(task, now = /* @__PURE__ */ new Date(), thresholdMs = STUCK_TASK_THRESHOLD_MS) {
|
|
1362
|
+
if (now.getTime() - task.updatedAt.getTime() < thresholdMs) return null;
|
|
1363
|
+
return task.attempts >= task.maxAttempts ? { action: "fail" } : { action: "requeue" };
|
|
1364
|
+
}
|
|
1307
1365
|
|
|
1308
1366
|
// src/core/sse/sse-response.service.ts
|
|
1309
1367
|
var SSE_BRIDGE_EVENTS = {
|
|
@@ -1903,21 +1961,52 @@ async function readSseResponse(response, callbacks) {
|
|
|
1903
1961
|
callbacks.onError?.("request_failed", "The server returned an empty stream.");
|
|
1904
1962
|
return;
|
|
1905
1963
|
}
|
|
1906
|
-
const
|
|
1964
|
+
for await (const event of iterateSseJson(response)) {
|
|
1965
|
+
if (event) dispatchSseEvent(event, callbacks);
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
function dataPayloadOf(frame) {
|
|
1969
|
+
let data = null;
|
|
1970
|
+
for (const line of frame.split("\n")) {
|
|
1971
|
+
if (line.startsWith("data:")) {
|
|
1972
|
+
const payload = line.slice(5).trim();
|
|
1973
|
+
data = data === null ? payload : `${data}
|
|
1974
|
+
${payload}`;
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
return data !== null && data.length > 0 ? data : null;
|
|
1978
|
+
}
|
|
1979
|
+
async function* iterateSseData(source) {
|
|
1980
|
+
const body = source instanceof Response ? source.body : source;
|
|
1981
|
+
if (!body) {
|
|
1982
|
+
throw new Error("iterateSseData: the response has no body to read");
|
|
1983
|
+
}
|
|
1984
|
+
const reader = body.getReader();
|
|
1907
1985
|
const decoder = new TextDecoder();
|
|
1908
1986
|
let buffer = "";
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1987
|
+
try {
|
|
1988
|
+
while (true) {
|
|
1989
|
+
const { done, value } = await reader.read();
|
|
1990
|
+
if (done) break;
|
|
1991
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1992
|
+
let boundary = buffer.indexOf("\n\n");
|
|
1993
|
+
while (boundary !== -1) {
|
|
1994
|
+
const frame = buffer.slice(0, boundary);
|
|
1995
|
+
buffer = buffer.slice(boundary + 2);
|
|
1996
|
+
boundary = buffer.indexOf("\n\n");
|
|
1997
|
+
const data = dataPayloadOf(frame);
|
|
1998
|
+
if (data) yield data;
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
} finally {
|
|
2002
|
+
await reader.cancel().catch(() => void 0);
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
async function* iterateSseJson(source) {
|
|
2006
|
+
for await (const data of iterateSseData(source)) {
|
|
2007
|
+
try {
|
|
2008
|
+
yield JSON.parse(data);
|
|
2009
|
+
} catch {
|
|
1921
2010
|
}
|
|
1922
2011
|
}
|
|
1923
2012
|
}
|
|
@@ -2062,6 +2151,7 @@ exports.CODE_MESSAGES = CODE_MESSAGES;
|
|
|
2062
2151
|
exports.CacheManager = CacheManager;
|
|
2063
2152
|
exports.CompositeExecutor = CompositeExecutor;
|
|
2064
2153
|
exports.DEFAULT_AI_IDENTITY = DEFAULT_AI_IDENTITY;
|
|
2154
|
+
exports.DEFAULT_ALLOWANCE_WINDOW_MS = DEFAULT_ALLOWANCE_WINDOW_MS;
|
|
2065
2155
|
exports.DEFAULT_API_KEY_RULE = DEFAULT_API_KEY_RULE;
|
|
2066
2156
|
exports.DEFAULT_EMBED_BATCH_SIZE = DEFAULT_EMBED_BATCH_SIZE;
|
|
2067
2157
|
exports.DEFAULT_ERROR_CLASSIFICATION_RULE = DEFAULT_ERROR_CLASSIFICATION_RULE;
|
|
@@ -2100,6 +2190,7 @@ exports.SSE_BRIDGE_EVENTS = SSE_BRIDGE_EVENTS;
|
|
|
2100
2190
|
exports.SSE_DONE_MARKER = SSE_DONE_MARKER;
|
|
2101
2191
|
exports.SSE_PREFIX = SSE_PREFIX;
|
|
2102
2192
|
exports.STATUS_TO_CODE = STATUS_TO_CODE;
|
|
2193
|
+
exports.STUCK_TASK_THRESHOLD_MS = STUCK_TASK_THRESHOLD_MS;
|
|
2103
2194
|
exports.SYSTEM_PROMPT_VERSION = SYSTEM_PROMPT_VERSION;
|
|
2104
2195
|
exports.StreamingResponseParser = StreamingResponseParser;
|
|
2105
2196
|
exports.StructuredPromptBuilder = StructuredPromptBuilder;
|
|
@@ -2113,6 +2204,7 @@ exports.USER_FRIENDLY_ERROR_MESSAGES = USER_FRIENDLY_ERROR_MESSAGES;
|
|
|
2113
2204
|
exports.accumulateReport = accumulateReport;
|
|
2114
2205
|
exports.aiErrorMessage = aiErrorMessage;
|
|
2115
2206
|
exports.aiErrorStatus = aiErrorStatus;
|
|
2207
|
+
exports.aiFailureOf = aiFailureOf;
|
|
2116
2208
|
exports.asObjectArray = asObjectArray;
|
|
2117
2209
|
exports.asRecord = asRecord;
|
|
2118
2210
|
exports.asStringArray = asStringArray;
|
|
@@ -2138,11 +2230,13 @@ exports.classifyHttpStatus = classifyHttpStatus;
|
|
|
2138
2230
|
exports.computeCostMicros = computeCostMicros;
|
|
2139
2231
|
exports.cosineSimilarity = cosineSimilarity;
|
|
2140
2232
|
exports.countChanges = countChanges;
|
|
2233
|
+
exports.createAllowanceWindow = createAllowanceWindow;
|
|
2141
2234
|
exports.createRemoteChatClient = createRemoteChatClient;
|
|
2142
2235
|
exports.createSseResponse = createSseResponse;
|
|
2143
2236
|
exports.debounce = debounce;
|
|
2144
2237
|
exports.debounceManager = debounceManager;
|
|
2145
2238
|
exports.defaultSseDeltaExtractor = defaultSseDeltaExtractor;
|
|
2239
|
+
exports.deriveConversationTitle = deriveConversationTitle;
|
|
2146
2240
|
exports.deriveSnippetTitle = deriveSnippetTitle;
|
|
2147
2241
|
exports.describeAIErrorType = describeAIErrorType;
|
|
2148
2242
|
exports.diffLines = diffLines;
|
|
@@ -2175,6 +2269,8 @@ exports.isTextTier = isTextTier;
|
|
|
2175
2269
|
exports.isTransientError = isTransientError;
|
|
2176
2270
|
exports.isValidApiKey = isValidApiKey;
|
|
2177
2271
|
exports.isWithinWindow = isWithinWindow;
|
|
2272
|
+
exports.iterateSseData = iterateSseData;
|
|
2273
|
+
exports.iterateSseJson = iterateSseJson;
|
|
2178
2274
|
exports.magnitude = magnitude;
|
|
2179
2275
|
exports.mergeTierOverrides = mergeTierOverrides;
|
|
2180
2276
|
exports.nextAttemptDelayMs = nextAttemptDelayMs;
|
|
@@ -2183,6 +2279,7 @@ exports.normalizeSources = normalizeSources;
|
|
|
2183
2279
|
exports.normalizeSseChunk = normalizeSseChunk;
|
|
2184
2280
|
exports.oneOfCatalog = oneOfCatalog;
|
|
2185
2281
|
exports.parseSseEvent = parseSseEvent;
|
|
2282
|
+
exports.planStuckRecovery = planStuckRecovery;
|
|
2186
2283
|
exports.planTaskFailure = planTaskFailure;
|
|
2187
2284
|
exports.readSseResponse = readSseResponse;
|
|
2188
2285
|
exports.repairCommonJsonDefects = repairCommonJsonDefects;
|