@llumiverse/core 0.22.0 → 0.22.2
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/lib/cjs/CompletionStream.js +89 -20
- package/lib/cjs/CompletionStream.js.map +1 -1
- package/lib/cjs/Driver.js +23 -5
- package/lib/cjs/Driver.js.map +1 -1
- package/lib/cjs/async.js.map +1 -1
- package/lib/cjs/json.js +3 -174
- package/lib/cjs/json.js.map +1 -1
- package/lib/cjs/stream.js +16 -10
- package/lib/cjs/stream.js.map +1 -1
- package/lib/cjs/validation.js +31 -7
- package/lib/cjs/validation.js.map +1 -1
- package/lib/esm/CompletionStream.js +89 -20
- package/lib/esm/CompletionStream.js.map +1 -1
- package/lib/esm/Driver.js +23 -5
- package/lib/esm/Driver.js.map +1 -1
- package/lib/esm/async.js.map +1 -1
- package/lib/esm/json.js +3 -172
- package/lib/esm/json.js.map +1 -1
- package/lib/esm/stream.js +16 -10
- package/lib/esm/stream.js.map +1 -1
- package/lib/esm/validation.js +31 -7
- package/lib/esm/validation.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/lib/types/CompletionStream.d.ts +2 -2
- package/lib/types/CompletionStream.d.ts.map +1 -1
- package/lib/types/Driver.d.ts +3 -3
- package/lib/types/Driver.d.ts.map +1 -1
- package/lib/types/async.d.ts +2 -2
- package/lib/types/async.d.ts.map +1 -1
- package/lib/types/json.d.ts +0 -13
- package/lib/types/json.d.ts.map +1 -1
- package/lib/types/stream.d.ts.map +1 -1
- package/lib/types/validation.d.ts +2 -2
- package/lib/types/validation.d.ts.map +1 -1
- package/package.json +6 -5
- package/src/CompletionStream.ts +88 -21
- package/src/Driver.ts +3 -4
- package/src/async.ts +4 -4
- package/src/json.ts +3 -168
- package/src/stream.ts +19 -11
- package/src/validation.ts +33 -10
package/src/json.ts
CHANGED
|
@@ -1,181 +1,16 @@
|
|
|
1
1
|
import { JSONValue } from "@llumiverse/common";
|
|
2
|
+
import { jsonrepair } from 'jsonrepair';
|
|
2
3
|
|
|
3
4
|
function extractJsonFromText(text: string): string {
|
|
4
5
|
const start = text.indexOf("{");
|
|
5
6
|
const end = text.lastIndexOf("}");
|
|
6
|
-
|
|
7
|
-
return text.replace(/\\n/g, "");
|
|
7
|
+
return text.substring(start, end + 1);
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export function extractAndParseJSON(text: string): JSONValue {
|
|
11
11
|
return parseJSON(extractJsonFromText(text));
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
const RX_DQUOTE = /^"([^"\\]|\\.)*"/us;
|
|
15
|
-
const RX_SQUOTE = /^'([^'\\]|\\.)*'/us;
|
|
16
|
-
const RX_NUMBER = /^-?\d+(\.\d+)?/;
|
|
17
|
-
const RX_BOOLEAN = /^true|false/;
|
|
18
|
-
const RX_NULL = /^null/;
|
|
19
|
-
const RX_KEY = /^[$_a-zA-Z][$_a-zA-Z0-9]*/;
|
|
20
|
-
const RX_PUNCTUATION = /^\s*([\[\]{}:,])\s*/;
|
|
21
|
-
|
|
22
|
-
function fixText(value: string) {
|
|
23
|
-
return value.replaceAll('\n', '\\n').replaceAll('\r', '\\r');
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function decodeSingleQuotedString(value: string) {
|
|
27
|
-
return JSON.parse('"' + value.slice(1, -1).replaceAll(/(?<!\\)"/g, '\\"') + '"');
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export class JsonParser {
|
|
31
|
-
pos: number = 0;
|
|
32
|
-
|
|
33
|
-
constructor(public text: string) { }
|
|
34
|
-
|
|
35
|
-
skip(n: number) {
|
|
36
|
-
this.text = this.text.substring(n);
|
|
37
|
-
this.pos += n;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
tryReadPunctuation() {
|
|
41
|
-
const m = RX_PUNCTUATION.exec(this.text);
|
|
42
|
-
if (m) {
|
|
43
|
-
this.skip(m[0].length);
|
|
44
|
-
return m[1];
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
readKey() {
|
|
49
|
-
const first = this.text.charCodeAt(0);
|
|
50
|
-
if (first === 34) { // "
|
|
51
|
-
const m = RX_DQUOTE.exec(this.text);
|
|
52
|
-
if (m) {
|
|
53
|
-
this.skip(m[0].length);
|
|
54
|
-
return JSON.parse(m[0]);
|
|
55
|
-
}
|
|
56
|
-
} else if (first === 39) { // '
|
|
57
|
-
const m = RX_SQUOTE.exec(this.text);
|
|
58
|
-
if (m) {
|
|
59
|
-
this.skip(m[0].length);
|
|
60
|
-
return decodeSingleQuotedString(m[0]);
|
|
61
|
-
}
|
|
62
|
-
} else {
|
|
63
|
-
const m = RX_KEY.exec(this.text);
|
|
64
|
-
if (m) {
|
|
65
|
-
this.skip(m[0].length);
|
|
66
|
-
return m[0];
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
throw new Error('Expected a key at position ' + this.pos + ' but found ' + this.text);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
readScalar() {
|
|
73
|
-
const first = this.text.charCodeAt(0);
|
|
74
|
-
if (first === 34) { // "
|
|
75
|
-
const m = RX_DQUOTE.exec(this.text);
|
|
76
|
-
if (m) {
|
|
77
|
-
this.skip(m[0].length);
|
|
78
|
-
return JSON.parse(fixText(m[0]));
|
|
79
|
-
}
|
|
80
|
-
} else if (first === 39) { // '
|
|
81
|
-
const m = RX_SQUOTE.exec(this.text);
|
|
82
|
-
if (m) {
|
|
83
|
-
this.skip(m[0].length);
|
|
84
|
-
return decodeSingleQuotedString(fixText(m[0]));
|
|
85
|
-
}
|
|
86
|
-
} else {
|
|
87
|
-
let m = RX_NUMBER.exec(this.text);
|
|
88
|
-
if (m) {
|
|
89
|
-
this.skip(m[0].length);
|
|
90
|
-
return parseFloat(m[0]);
|
|
91
|
-
}
|
|
92
|
-
m = RX_BOOLEAN.exec(this.text);
|
|
93
|
-
if (m) {
|
|
94
|
-
this.skip(m[0].length);
|
|
95
|
-
return m[0] === 'true';
|
|
96
|
-
}
|
|
97
|
-
m = RX_NULL.exec(this.text);
|
|
98
|
-
if (m) {
|
|
99
|
-
this.skip(m[0].length);
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
throw new Error('Expected a value at position ' + this.pos + ' but found ' + this.text);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
readObject() {
|
|
107
|
-
let key: string | undefined;
|
|
108
|
-
const obj: any = {};
|
|
109
|
-
while (true) {
|
|
110
|
-
if (!key) { // read key
|
|
111
|
-
const p = this.tryReadPunctuation();
|
|
112
|
-
if (p === '}') {
|
|
113
|
-
return obj;
|
|
114
|
-
} else if (p === ',') {
|
|
115
|
-
continue;
|
|
116
|
-
} else if (p) {
|
|
117
|
-
throw new Error('Expected a key at position ' + this.pos + ' but found ' + this.text);
|
|
118
|
-
}
|
|
119
|
-
key = this.readKey();
|
|
120
|
-
if (!key) {
|
|
121
|
-
throw new Error('Expected a key at position ' + this.pos + ' but found ' + this.text);
|
|
122
|
-
}
|
|
123
|
-
if (this.tryReadPunctuation() !== ':') {
|
|
124
|
-
throw new Error('Expected a colon at position ' + this.pos + ' but found ' + this.text);
|
|
125
|
-
};
|
|
126
|
-
} else { // read value
|
|
127
|
-
const value = this.readValue();
|
|
128
|
-
if (value === undefined) {
|
|
129
|
-
throw new Error('Expected a value at position ' + this.pos + ' but found ' + this.text);
|
|
130
|
-
}
|
|
131
|
-
obj[key] = value;
|
|
132
|
-
key = undefined;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
readArray() {
|
|
138
|
-
const ar: any[] = [];
|
|
139
|
-
while (true) {
|
|
140
|
-
const p = this.tryReadPunctuation();
|
|
141
|
-
if (p === ',') {
|
|
142
|
-
continue;
|
|
143
|
-
} else if (p === ']') {
|
|
144
|
-
return ar;
|
|
145
|
-
} else if (p === '[') {
|
|
146
|
-
ar.push(this.readArray());
|
|
147
|
-
} else if (p === '{') {
|
|
148
|
-
ar.push(this.readObject());
|
|
149
|
-
} else if (!p) {
|
|
150
|
-
ar.push(this.readScalar());
|
|
151
|
-
} else {
|
|
152
|
-
throw new Error('Expected a value at position ' + this.pos + ' but found ' + this.text);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
readValue() {
|
|
158
|
-
const p = this.tryReadPunctuation();
|
|
159
|
-
if (p === '{') {
|
|
160
|
-
return this.readObject();
|
|
161
|
-
} else if (p === '[') {
|
|
162
|
-
return this.readArray();
|
|
163
|
-
} else if (!p) {
|
|
164
|
-
return this.readScalar();
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
static parse(text: string) {
|
|
169
|
-
const parser = new JsonParser(text);
|
|
170
|
-
const r = parser.readValue();
|
|
171
|
-
if (r === undefined) {
|
|
172
|
-
throw new Error('Not a valid JSON');
|
|
173
|
-
}
|
|
174
|
-
return r;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
|
|
179
14
|
export function parseJSON(text: string): JSONValue {
|
|
180
15
|
text = text.trim();
|
|
181
16
|
try {
|
|
@@ -183,7 +18,7 @@ export function parseJSON(text: string): JSONValue {
|
|
|
183
18
|
} catch (err: any) {
|
|
184
19
|
// use a relaxed parser
|
|
185
20
|
try {
|
|
186
|
-
return
|
|
21
|
+
return JSON.parse(jsonrepair(text));
|
|
187
22
|
} catch (err2: any) { // throw the original error
|
|
188
23
|
throw err;
|
|
189
24
|
}
|
package/src/stream.ts
CHANGED
|
@@ -1,22 +1,30 @@
|
|
|
1
1
|
|
|
2
2
|
export async function readStreamAsBase64(stream: ReadableStream): Promise<string> {
|
|
3
|
-
|
|
3
|
+
const uint8Array = await readStreamAsUint8Array(stream);
|
|
4
|
+
return Buffer.from(uint8Array).toString('base64');
|
|
4
5
|
}
|
|
5
6
|
|
|
6
7
|
export async function readStreamAsString(stream: ReadableStream): Promise<string> {
|
|
7
|
-
|
|
8
|
+
const uint8Array = await readStreamAsUint8Array(stream);
|
|
9
|
+
return Buffer.from(uint8Array).toString();
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
export async function readStreamAsUint8Array(stream: ReadableStream): Promise<Uint8Array> {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async function _readStreamAsBuffer(stream: ReadableStream): Promise<Buffer> {
|
|
17
|
-
const out: Buffer[] = [];
|
|
13
|
+
const chunks: Uint8Array[] = [];
|
|
14
|
+
let totalLength = 0;
|
|
15
|
+
|
|
18
16
|
for await (const chunk of stream) {
|
|
19
|
-
|
|
17
|
+
const uint8Chunk = chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk);
|
|
18
|
+
chunks.push(uint8Chunk);
|
|
19
|
+
totalLength += uint8Chunk.length;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const combined = new Uint8Array(totalLength);
|
|
23
|
+
let offset = 0;
|
|
24
|
+
for (const chunk of chunks) {
|
|
25
|
+
combined.set(chunk, offset);
|
|
26
|
+
offset += chunk.length;
|
|
20
27
|
}
|
|
21
|
-
|
|
28
|
+
|
|
29
|
+
return combined;
|
|
22
30
|
}
|
package/src/validation.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { CompletionResult, ResultValidationError } from "@llumiverse/common";
|
|
1
2
|
import { Ajv } from 'ajv';
|
|
2
3
|
import addFormats from 'ajv-formats';
|
|
3
4
|
import { extractAndParseJSON } from "./json.js";
|
|
4
5
|
import { resolveField } from './resolver.js';
|
|
5
|
-
import { ResultValidationError } from "@llumiverse/common";
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
const ajv = new Ajv({
|
|
@@ -28,17 +28,40 @@ export class ValidationError extends Error implements ResultValidationError {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
let
|
|
31
|
+
function parseCompletionAsJson(data: CompletionResult[]) {
|
|
32
|
+
let lastError: ValidationError | undefined;
|
|
33
|
+
for (const part of data) {
|
|
34
|
+
if (part.type === "text") {
|
|
35
|
+
const text = part.value.trim();
|
|
36
|
+
try {
|
|
37
|
+
return extractAndParseJSON(text);
|
|
38
|
+
} catch (error: any) {
|
|
39
|
+
lastError = new ValidationError("json_error", error.message);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (!lastError) {
|
|
44
|
+
lastError = new ValidationError("json_error", "No JSON compatible response found in completion result");
|
|
45
|
+
}
|
|
46
|
+
throw lastError;
|
|
47
|
+
}
|
|
48
|
+
|
|
33
49
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
50
|
+
export function validateResult(data: CompletionResult[], schema: Object): CompletionResult[] {
|
|
51
|
+
let json;
|
|
52
|
+
if (Array.isArray(data)) {
|
|
53
|
+
const jsonResults = data.filter(r => r.type === "json");
|
|
54
|
+
if (jsonResults.length > 0) {
|
|
55
|
+
json = jsonResults[0].value;
|
|
56
|
+
} else {
|
|
57
|
+
try {
|
|
58
|
+
json = parseCompletionAsJson(data);
|
|
59
|
+
} catch (error: any) {
|
|
60
|
+
throw new ValidationError("json_error", error.message)
|
|
61
|
+
}
|
|
39
62
|
}
|
|
40
63
|
} else {
|
|
41
|
-
|
|
64
|
+
throw new Error("Data to validate must be an array")
|
|
42
65
|
}
|
|
43
66
|
|
|
44
67
|
const validate = ajv.compile(schema);
|
|
@@ -71,5 +94,5 @@ export function validateResult(data: any, schema: Object) {
|
|
|
71
94
|
}
|
|
72
95
|
}
|
|
73
96
|
|
|
74
|
-
return json;
|
|
97
|
+
return [{ type: "json", value: json }];
|
|
75
98
|
}
|