@hraness/sponge 0.3.3 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/annotations/quote.d.ts +1 -0
- package/dist/src/annotations/quote.js +11 -1
- package/dist/src/digest/endpoint.js +3 -3
- package/dist/src/digest/pipeline.js +7 -4
- package/dist/src/digest/schema.js +6 -2
- package/dist/src/errors.d.ts +1 -0
- package/dist/src/errors.js +3 -1
- package/native/runtime-manifest.json +4 -4
- package/package.json +1 -1
|
@@ -20,6 +20,7 @@ export declare function textQuote(source: string, start: number, end: number): T
|
|
|
20
20
|
export declare function locateQuote(source: string, quote: string): TextQuote | undefined;
|
|
21
21
|
/** A stored selector still names its exact text in `source`. */
|
|
22
22
|
export declare function anchorHolds(source: string, anchor: TextQuote): boolean;
|
|
23
|
+
/** Word count: space-separated tokens, with each run of Han or kana counted as half its characters, rounded up. */
|
|
23
24
|
export declare function words(value: string): number;
|
|
24
25
|
/**
|
|
25
26
|
* Every occurrence of `quote` in `source` ignoring inline Markdown and case, within optional raw bounds.
|
|
@@ -107,9 +107,19 @@ export function anchorHolds(source, anchor) {
|
|
|
107
107
|
anchor.end > anchor.start &&
|
|
108
108
|
source.slice(anchor.start, anchor.end) === anchor.exact);
|
|
109
109
|
}
|
|
110
|
+
/** Han and kana are written without spaces; about two characters make a word. */
|
|
111
|
+
const unspaced = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}]/gu;
|
|
112
|
+
/** Word count: space-separated tokens, with each run of Han or kana counted as half its characters, rounded up. */
|
|
110
113
|
export function words(value) {
|
|
111
114
|
const t = foldText(value);
|
|
112
|
-
|
|
115
|
+
if (!t)
|
|
116
|
+
return 0;
|
|
117
|
+
let n = 0;
|
|
118
|
+
for (const token of t.split(' ')) {
|
|
119
|
+
const cjk = token.match(unspaced)?.length ?? 0;
|
|
120
|
+
n += cjk ? Math.ceil(cjk / 2) + (token.replace(unspaced, '').match(/[\p{L}\p{N}]/u) ? 1 : 0) : 1;
|
|
121
|
+
}
|
|
122
|
+
return n;
|
|
113
123
|
}
|
|
114
124
|
/**
|
|
115
125
|
* Rendered-text projection for snapping a near-verbatim quote: drops inline Markdown syntax (link targets, brackets,
|
|
@@ -116,7 +116,7 @@ async function complete(origin, request, model, messages, schema, o) {
|
|
|
116
116
|
});
|
|
117
117
|
if (!response.ok) {
|
|
118
118
|
await response.body?.cancel().catch(() => undefined);
|
|
119
|
-
throw new SpongeError('unavailable');
|
|
119
|
+
throw new SpongeError('model-unavailable', { detail: { status: response.status } });
|
|
120
120
|
}
|
|
121
121
|
let parsed;
|
|
122
122
|
try {
|
|
@@ -125,14 +125,14 @@ async function complete(origin, request, model, messages, schema, o) {
|
|
|
125
125
|
catch (error) {
|
|
126
126
|
if (error instanceof SpongeError)
|
|
127
127
|
throw error;
|
|
128
|
-
throw new SpongeError('unavailable');
|
|
128
|
+
throw new SpongeError('model-unavailable');
|
|
129
129
|
}
|
|
130
130
|
const choice = parsed?.choices?.[0];
|
|
131
131
|
if (choice?.finish_reason === 'length')
|
|
132
132
|
throw new SpongeError('limit-exceeded');
|
|
133
133
|
const content = choice?.message?.content;
|
|
134
134
|
if (typeof content !== 'string' || !content.trim())
|
|
135
|
-
throw new SpongeError('unavailable');
|
|
135
|
+
throw new SpongeError('model-unavailable');
|
|
136
136
|
if (Buffer.byteLength(content) > o.maxOutputBytes)
|
|
137
137
|
throw new SpongeError('limit-exceeded');
|
|
138
138
|
return content;
|
|
@@ -317,10 +317,13 @@ export function snapQuote(source, text, within) {
|
|
|
317
317
|
export function trimQuote(text, max = L.quoteWords) {
|
|
318
318
|
if (words(text) <= max)
|
|
319
319
|
return text;
|
|
320
|
-
|
|
321
|
-
|
|
320
|
+
// Cut after a space-separated token or, inside unspaced Han and kana, after any character.
|
|
321
|
+
const ends = [
|
|
322
|
+
...new Set([...text.matchAll(/\S+/g), ...text.matchAll(/[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}]/gu)].map((m) => m.index + m[0].length)),
|
|
323
|
+
].sort((a, b) => a - b);
|
|
324
|
+
let n = ends.length, out = text;
|
|
322
325
|
do {
|
|
323
|
-
out = text.slice(0, ends[n - 1]).replace(/[\s,;:(\[\u2014\u2013-]+$/, '');
|
|
326
|
+
out = text.slice(0, ends[n - 1]).replace(/[\s,;:(\[\u2014\u2013\u3001\uff0c-]+$/, '');
|
|
324
327
|
n--;
|
|
325
328
|
} while (n > 0 && words(out) > max);
|
|
326
329
|
return out;
|
|
@@ -486,7 +489,7 @@ export async function draftDigest(input) {
|
|
|
486
489
|
throw new SpongeError('limit-exceeded');
|
|
487
490
|
if (error instanceof SpongeError)
|
|
488
491
|
throw error;
|
|
489
|
-
throw new SpongeError('unavailable');
|
|
492
|
+
throw new SpongeError('model-unavailable', { cause: error });
|
|
490
493
|
}
|
|
491
494
|
if (userSignal.aborted)
|
|
492
495
|
throw new SpongeError('cancelled');
|
|
@@ -49,8 +49,12 @@ const keys = (v, allowed) => Object.keys(v).every((k) => allowed.includes(k));
|
|
|
49
49
|
const str = (v, max = 2000) => typeof v === 'string' && v.trim().length > 0 && v.length <= max && !/[\u0000-\u0008\u000b-\u001f\u007f]/.test(v);
|
|
50
50
|
/** v1: sentence terminators followed by more text; abbreviations are rare enough in a 155-character lead to reject. */
|
|
51
51
|
const sentencesV1 = (v) => foldText(v).split(/(?<=[.!?])\s+(?=\S)/).length;
|
|
52
|
-
/**
|
|
53
|
-
|
|
52
|
+
/**
|
|
53
|
+
* v2: a terminator (and closing quotes) followed by a capital or digit, the split Jungle's first-sentence lead uses, so
|
|
54
|
+
* `e.g. this` stays one sentence. A period after an initial or a common title does not end a sentence: `Lucas C.
|
|
55
|
+
* Cordeiro`, `F. Zakaria`, `U.S. Senate`, `Dr. Lind`.
|
|
56
|
+
*/
|
|
57
|
+
const sentences = (v) => foldText(v).split(/(?<=(?:(?<!\b(?:[A-Z]|Mr|Mrs|Ms|Dr|Prof|St|Jr|Sr|vs|No|Inc|Ltd|Co))\.|[!?])["')]*)\s+(?=["(]?[A-Z0-9])/).length;
|
|
54
58
|
/** Parse an untrusted digest (v1 or v2) into its shape, or report why not. */
|
|
55
59
|
export function parseDigest(value) {
|
|
56
60
|
const issues = [], bad = (path, code = 'shape') => {
|
package/dist/src/errors.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ declare const parents: {
|
|
|
17
17
|
readonly malformed: "invalid-input";
|
|
18
18
|
readonly 'no-capture': "invalid-input";
|
|
19
19
|
readonly 'draft-rejected': "unavailable";
|
|
20
|
+
readonly 'model-unavailable': "unavailable";
|
|
20
21
|
readonly 'parent-missing': "invalid-input";
|
|
21
22
|
readonly 'target-exists': "invalid-input";
|
|
22
23
|
};
|
package/dist/src/errors.js
CHANGED
|
@@ -31,6 +31,7 @@ const parents = {
|
|
|
31
31
|
malformed: 'invalid-input',
|
|
32
32
|
'no-capture': 'invalid-input',
|
|
33
33
|
'draft-rejected': 'unavailable',
|
|
34
|
+
'model-unavailable': 'unavailable',
|
|
34
35
|
'parent-missing': 'invalid-input',
|
|
35
36
|
'target-exists': 'invalid-input',
|
|
36
37
|
};
|
|
@@ -80,7 +81,8 @@ export const hints = {
|
|
|
80
81
|
};
|
|
81
82
|
/** Hints for detail codes whose public code's hint would mislead. Fixed text only, like `hints`. */
|
|
82
83
|
export const detailHints = {
|
|
83
|
-
'draft-rejected': 'The model draft failed the digest checks listed above, even after one repair.
|
|
84
|
+
'draft-rejected': 'The model draft failed the digest checks listed above, even after one repair. Drafting is deterministic, so the same capture and model fail again; refresh the item or use another model.',
|
|
85
|
+
'model-unavailable': 'The model server did not answer or returned an error. Check that it is still running and has free GPU memory (another loaded model can exhaust it), then retry.',
|
|
84
86
|
};
|
|
85
87
|
/** The one error type. `message` is the code, so bare `String(error)` never carries detail, paths or causes. */
|
|
86
88
|
export class SpongeError extends Error {
|
|
@@ -27,14 +27,14 @@
|
|
|
27
27
|
}
|
|
28
28
|
],
|
|
29
29
|
"build": {
|
|
30
|
-
"asrSourceSha256": "
|
|
30
|
+
"asrSourceSha256": "3b32718d4626aff97f2c3e043c9fd020f237adc7f7e5508e7d7b99a555762982",
|
|
31
31
|
"clangVersion": "Apple clang version 21.0.0 (clang-2100.1.1.101)",
|
|
32
|
-
"embeddingSourceSha256": "
|
|
32
|
+
"embeddingSourceSha256": "66f008ea29050d114b0df74337124bac8f6cdba9732e257cc70ad85eb9ebee6f",
|
|
33
33
|
"swiftVersion": "Apple Swift version 6.3.3 (swiftlang-6.3.3.1.3 clang-2100.1.1.101)"
|
|
34
34
|
},
|
|
35
35
|
"minimumMacOS": "26.0",
|
|
36
|
-
"packageVersion": "0.3.
|
|
36
|
+
"packageVersion": "0.3.5",
|
|
37
37
|
"platform": "darwin",
|
|
38
38
|
"schemaVersion": 1,
|
|
39
|
-
"sourceCommit": "
|
|
39
|
+
"sourceCommit": "49b3f7f100cdbcbf5f52418d28c53dc13f198424"
|
|
40
40
|
}
|