modelfusion 0.34.0 → 0.35.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 +47 -1
- package/model-function/AsyncIterableResultPromise.cjs +37 -0
- package/model-function/AsyncIterableResultPromise.d.ts +16 -0
- package/model-function/AsyncIterableResultPromise.js +33 -0
- package/model-function/{generate-text/DeltaEvent.d.ts → DeltaEvent.d.ts} +1 -1
- package/model-function/ModelCallEvent.d.ts +3 -2
- package/model-function/generate-structure/StructureFromTextGenerationModel.d.ts +1 -1
- package/model-function/generate-structure/StructureGenerationModel.d.ts +10 -1
- package/model-function/generate-structure/StructureStreamingEvent.cjs +2 -0
- package/model-function/generate-structure/StructureStreamingEvent.d.ts +7 -0
- package/model-function/generate-structure/StructureStreamingEvent.js +1 -0
- package/model-function/generate-structure/fixJson.cjs +215 -0
- package/model-function/generate-structure/fixJson.d.ts +1 -0
- package/model-function/generate-structure/fixJson.js +211 -0
- package/model-function/generate-structure/fixJson.test.cjs +130 -0
- package/model-function/generate-structure/fixJson.test.d.ts +1 -0
- package/model-function/generate-structure/fixJson.test.js +128 -0
- package/model-function/generate-structure/generateStructure.cjs +3 -1
- package/model-function/generate-structure/generateStructure.d.ts +1 -1
- package/model-function/generate-structure/generateStructure.js +3 -1
- package/model-function/generate-structure/parsePartialJson.cjs +29 -0
- package/model-function/generate-structure/parsePartialJson.d.ts +1 -0
- package/model-function/generate-structure/parsePartialJson.js +22 -0
- package/model-function/generate-structure/streamStructure.cjs +167 -0
- package/model-function/generate-structure/streamStructure.d.ts +17 -0
- package/model-function/generate-structure/streamStructure.js +160 -0
- package/model-function/generate-text/TextGenerationModel.d.ts +4 -4
- package/model-function/generate-text/streamText.cjs +47 -68
- package/model-function/generate-text/streamText.d.ts +3 -18
- package/model-function/generate-text/streamText.js +46 -66
- package/model-function/index.cjs +3 -1
- package/model-function/index.d.ts +3 -1
- package/model-function/index.js +3 -1
- package/model-provider/cohere/CohereTextGenerationModel.cjs +3 -3
- package/model-provider/cohere/CohereTextGenerationModel.d.ts +3 -3
- package/model-provider/cohere/CohereTextGenerationModel.js +3 -3
- package/model-provider/huggingface/HuggingFaceTextGenerationModel.cjs +0 -12
- package/model-provider/huggingface/HuggingFaceTextGenerationModel.d.ts +0 -2
- package/model-provider/huggingface/HuggingFaceTextGenerationModel.js +0 -12
- package/model-provider/llamacpp/LlamaCppTextGenerationModel.cjs +3 -3
- package/model-provider/llamacpp/LlamaCppTextGenerationModel.d.ts +3 -3
- package/model-provider/llamacpp/LlamaCppTextGenerationModel.js +3 -3
- package/model-provider/openai/OpenAITextGenerationModel.cjs +3 -3
- package/model-provider/openai/OpenAITextGenerationModel.d.ts +3 -3
- package/model-provider/openai/OpenAITextGenerationModel.js +3 -3
- package/model-provider/openai/chat/OpenAIChatModel.cjs +23 -2
- package/model-provider/openai/chat/OpenAIChatModel.d.ts +4 -2
- package/model-provider/openai/chat/OpenAIChatModel.js +23 -2
- package/model-provider/openai/chat/OpenAIChatStreamIterable.d.ts +1 -1
- package/package.json +8 -4
- package/prompt/PromptFormatTextGenerationModel.d.ts +1 -1
- package/tool/useTool.cjs +3 -4
- package/tool/useTool.d.ts +1 -1
- package/tool/useTool.js +3 -4
- package/model-function/generate-text/extractTextDeltas.cjs +0 -23
- package/model-function/generate-text/extractTextDeltas.d.ts +0 -7
- package/model-function/generate-text/extractTextDeltas.js +0 -19
- /package/model-function/{generate-text/DeltaEvent.cjs → DeltaEvent.cjs} +0 -0
- /package/model-function/{generate-text/DeltaEvent.js → DeltaEvent.js} +0 -0
package/README.md
CHANGED
@@ -133,7 +133,7 @@ for (const choice of response.choices) {
|
|
133
133
|
console.log(`Duration: ${metadata.durationInMs}ms`);
|
134
134
|
```
|
135
135
|
|
136
|
-
### [Generate Structure](https://modelfusion.dev/guide/function/generate-structure)
|
136
|
+
### [Generate Structure](https://modelfusion.dev/guide/function/generate-structure#generatestructure)
|
137
137
|
|
138
138
|
Generate a structure that matches a schema.
|
139
139
|
|
@@ -168,6 +168,52 @@ const sentiment = await generateStructure(
|
|
168
168
|
|
169
169
|
Providers: [OpenAI](https://modelfusion.dev/integration/model-provider/openai)
|
170
170
|
|
171
|
+
### [Stream Structure](https://modelfusion.dev/guide/function/generate-structure#streamstructure)
|
172
|
+
|
173
|
+
Stream a structure that matches a schema. Partial structures before the final part are untyped JSON.
|
174
|
+
|
175
|
+
```ts
|
176
|
+
const structureStream = await streamStructure(
|
177
|
+
new OpenAIChatModel({
|
178
|
+
model: "gpt-3.5-turbo",
|
179
|
+
temperature: 0,
|
180
|
+
maxCompletionTokens: 2000,
|
181
|
+
}),
|
182
|
+
new ZodStructureDefinition({
|
183
|
+
name: "generateCharacter" as const,
|
184
|
+
description: "Generate character descriptions.",
|
185
|
+
schema: z.object({
|
186
|
+
characters: z.array(
|
187
|
+
z.object({
|
188
|
+
name: z.string(),
|
189
|
+
class: z
|
190
|
+
.string()
|
191
|
+
.describe("Character class, e.g. warrior, mage, or thief."),
|
192
|
+
description: z.string(),
|
193
|
+
})
|
194
|
+
),
|
195
|
+
}),
|
196
|
+
}),
|
197
|
+
[
|
198
|
+
OpenAIChatMessage.user(
|
199
|
+
"Generate 3 character descriptions for a fantasy role playing game."
|
200
|
+
),
|
201
|
+
]
|
202
|
+
);
|
203
|
+
|
204
|
+
for await (const part of structureStream) {
|
205
|
+
if (!part.isComplete) {
|
206
|
+
const unknownPartialStructure = part.value;
|
207
|
+
console.log("partial value", unknownPartialStructure);
|
208
|
+
} else {
|
209
|
+
const fullyTypedStructure = part.value;
|
210
|
+
console.log("final value", fullyTypedStructure);
|
211
|
+
}
|
212
|
+
}
|
213
|
+
```
|
214
|
+
|
215
|
+
Providers: [OpenAI](https://modelfusion.dev/integration/model-provider/openai)
|
216
|
+
|
171
217
|
### [Generate Structure or Text](https://modelfusion.dev/guide/function/generate-structure-or-text)
|
172
218
|
|
173
219
|
Generate a structure (or text as a fallback) using a prompt and multiple schemas.
|
@@ -0,0 +1,37 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.AsyncIterableResultPromise = void 0;
|
4
|
+
class AsyncIterableResultPromise extends Promise {
|
5
|
+
constructor(fullPromise) {
|
6
|
+
super((resolve) => {
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
8
|
+
resolve(null); // we override the resolve function
|
9
|
+
});
|
10
|
+
Object.defineProperty(this, "fullPromise", {
|
11
|
+
enumerable: true,
|
12
|
+
configurable: true,
|
13
|
+
writable: true,
|
14
|
+
value: fullPromise
|
15
|
+
});
|
16
|
+
Object.defineProperty(this, "outputPromise", {
|
17
|
+
enumerable: true,
|
18
|
+
configurable: true,
|
19
|
+
writable: true,
|
20
|
+
value: void 0
|
21
|
+
});
|
22
|
+
this.outputPromise = fullPromise.then((result) => result.output);
|
23
|
+
}
|
24
|
+
asFullResponse() {
|
25
|
+
return this.fullPromise;
|
26
|
+
}
|
27
|
+
then(onfulfilled, onrejected) {
|
28
|
+
return this.outputPromise.then(onfulfilled, onrejected);
|
29
|
+
}
|
30
|
+
catch(onrejected) {
|
31
|
+
return this.outputPromise.catch(onrejected);
|
32
|
+
}
|
33
|
+
finally(onfinally) {
|
34
|
+
return this.outputPromise.finally(onfinally);
|
35
|
+
}
|
36
|
+
}
|
37
|
+
exports.AsyncIterableResultPromise = AsyncIterableResultPromise;
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import { ModelCallMetadata } from "./executeCall.js";
|
2
|
+
export declare class AsyncIterableResultPromise<T> extends Promise<AsyncIterable<T>> {
|
3
|
+
private fullPromise;
|
4
|
+
private outputPromise;
|
5
|
+
constructor(fullPromise: Promise<{
|
6
|
+
output: AsyncIterable<T>;
|
7
|
+
metadata: Omit<ModelCallMetadata, "durationInMs" | "finishTimestamp">;
|
8
|
+
}>);
|
9
|
+
asFullResponse(): Promise<{
|
10
|
+
output: AsyncIterable<T>;
|
11
|
+
metadata: Omit<ModelCallMetadata, "durationInMs" | "finishTimestamp">;
|
12
|
+
}>;
|
13
|
+
then<TResult1 = AsyncIterable<T>, TResult2 = never>(onfulfilled?: ((value: AsyncIterable<T>) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;
|
14
|
+
catch<TResult = never>(onrejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | undefined | null): Promise<AsyncIterable<T> | TResult>;
|
15
|
+
finally(onfinally?: (() => void) | undefined | null): Promise<AsyncIterable<T>>;
|
16
|
+
}
|
@@ -0,0 +1,33 @@
|
|
1
|
+
export class AsyncIterableResultPromise extends Promise {
|
2
|
+
constructor(fullPromise) {
|
3
|
+
super((resolve) => {
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
5
|
+
resolve(null); // we override the resolve function
|
6
|
+
});
|
7
|
+
Object.defineProperty(this, "fullPromise", {
|
8
|
+
enumerable: true,
|
9
|
+
configurable: true,
|
10
|
+
writable: true,
|
11
|
+
value: fullPromise
|
12
|
+
});
|
13
|
+
Object.defineProperty(this, "outputPromise", {
|
14
|
+
enumerable: true,
|
15
|
+
configurable: true,
|
16
|
+
writable: true,
|
17
|
+
value: void 0
|
18
|
+
});
|
19
|
+
this.outputPromise = fullPromise.then((result) => result.output);
|
20
|
+
}
|
21
|
+
asFullResponse() {
|
22
|
+
return this.fullPromise;
|
23
|
+
}
|
24
|
+
then(onfulfilled, onrejected) {
|
25
|
+
return this.outputPromise.then(onfulfilled, onrejected);
|
26
|
+
}
|
27
|
+
catch(onrejected) {
|
28
|
+
return this.outputPromise.catch(onrejected);
|
29
|
+
}
|
30
|
+
finally(onfinally) {
|
31
|
+
return this.outputPromise.finally(onfinally);
|
32
|
+
}
|
33
|
+
}
|
@@ -4,6 +4,7 @@ import { ImageDescriptionFinishedEvent, ImageDescriptionStartedEvent } from "./d
|
|
4
4
|
import { TextEmbeddingFinishedEvent, TextEmbeddingStartedEvent } from "./embed-text/TextEmbeddingEvent.js";
|
5
5
|
import { ImageGenerationFinishedEvent, ImageGenerationStartedEvent } from "./generate-image/ImageGenerationEvent.js";
|
6
6
|
import { StructureGenerationFinishedEvent, StructureGenerationStartedEvent } from "./generate-structure/StructureGenerationEvent.js";
|
7
|
+
import { StructureStreamingFinishedEvent, StructureStreamingStartedEvent } from "./generate-structure/StructureStreamingEvent.js";
|
7
8
|
import { TextGenerationFinishedEvent, TextGenerationStartedEvent } from "./generate-text/TextGenerationEvent.js";
|
8
9
|
import { TextStreamingFinishedEvent, TextStreamingStartedEvent } from "./generate-text/TextStreamingEvent.js";
|
9
10
|
import { SpeechSynthesisFinishedEvent, SpeechSynthesisStartedEvent } from "./synthesize-speech/SpeechSynthesisEvent.js";
|
@@ -48,5 +49,5 @@ export interface BaseModelCallFinishedEvent extends BaseFunctionFinishedEvent {
|
|
48
49
|
*/
|
49
50
|
result: BaseModelCallFinishedEventResult;
|
50
51
|
}
|
51
|
-
export type ModelCallStartedEvent = ImageDescriptionStartedEvent | ImageGenerationStartedEvent | StructureGenerationStartedEvent | SpeechSynthesisStartedEvent | TextEmbeddingStartedEvent | TextGenerationStartedEvent | TextStreamingStartedEvent | TranscriptionStartedEvent;
|
52
|
-
export type ModelCallFinishedEvent = ImageDescriptionFinishedEvent | ImageGenerationFinishedEvent | StructureGenerationFinishedEvent | SpeechSynthesisFinishedEvent | TextEmbeddingFinishedEvent | TextGenerationFinishedEvent | TextStreamingFinishedEvent | TranscriptionFinishedEvent;
|
52
|
+
export type ModelCallStartedEvent = ImageDescriptionStartedEvent | ImageGenerationStartedEvent | StructureGenerationStartedEvent | StructureStreamingStartedEvent | SpeechSynthesisStartedEvent | TextEmbeddingStartedEvent | TextGenerationStartedEvent | TextStreamingStartedEvent | TranscriptionStartedEvent;
|
53
|
+
export type ModelCallFinishedEvent = ImageDescriptionFinishedEvent | ImageGenerationFinishedEvent | StructureGenerationFinishedEvent | StructureStreamingFinishedEvent | SpeechSynthesisFinishedEvent | TextEmbeddingFinishedEvent | TextGenerationFinishedEvent | TextStreamingFinishedEvent | TranscriptionFinishedEvent;
|
@@ -6,7 +6,7 @@ export type StructureFromTextPromptFormat<PROMPT> = {
|
|
6
6
|
createPrompt: (prompt: PROMPT, structure: StructureDefinition<string, unknown>) => string;
|
7
7
|
extractStructure: (response: string) => unknown;
|
8
8
|
};
|
9
|
-
export declare class StructureFromTextGenerationModel<PROMPT, MODEL extends TextGenerationModel<string, any, any, TextGenerationModelSettings>> implements StructureGenerationModel<PROMPT, string, MODEL["settings"]> {
|
9
|
+
export declare class StructureFromTextGenerationModel<PROMPT, MODEL extends TextGenerationModel<string, any, any, TextGenerationModelSettings>> implements StructureGenerationModel<PROMPT, string, undefined, MODEL["settings"]> {
|
10
10
|
private readonly model;
|
11
11
|
private readonly format;
|
12
12
|
constructor({ model, format, }: {
|
@@ -1,9 +1,10 @@
|
|
1
1
|
import { StructureDefinition } from "../../core/structure/StructureDefinition.js";
|
2
|
+
import { DeltaEvent } from "../DeltaEvent.js";
|
2
3
|
import { Model, ModelSettings } from "../Model.js";
|
3
4
|
import { ModelFunctionOptions } from "../ModelFunctionOptions.js";
|
4
5
|
export interface StructureGenerationModelSettings extends ModelSettings {
|
5
6
|
}
|
6
|
-
export interface StructureGenerationModel<PROMPT, RESPONSE, SETTINGS extends StructureGenerationModelSettings> extends Model<SETTINGS> {
|
7
|
+
export interface StructureGenerationModel<PROMPT, RESPONSE, FULL_DELTA, SETTINGS extends StructureGenerationModelSettings> extends Model<SETTINGS> {
|
7
8
|
generateStructureResponse(structure: StructureDefinition<string, unknown>, prompt: PROMPT, options?: ModelFunctionOptions<SETTINGS>): PromiseLike<RESPONSE>;
|
8
9
|
extractStructure(response: RESPONSE): unknown;
|
9
10
|
extractUsage?(response: RESPONSE): {
|
@@ -11,4 +12,12 @@ export interface StructureGenerationModel<PROMPT, RESPONSE, SETTINGS extends Str
|
|
11
12
|
completionTokens: number;
|
12
13
|
totalTokens: number;
|
13
14
|
};
|
15
|
+
/**
|
16
|
+
* Optional. Implement for streaming support.
|
17
|
+
*/
|
18
|
+
readonly generateStructureStreamResponse?: (structureDefinition: StructureDefinition<string, unknown>, prompt: PROMPT, options?: ModelFunctionOptions<SETTINGS>) => PromiseLike<AsyncIterable<DeltaEvent<FULL_DELTA>>>;
|
19
|
+
/**
|
20
|
+
* Optional. Implement for streaming support.
|
21
|
+
*/
|
22
|
+
readonly extractPartialStructure?: (fullDelta: FULL_DELTA) => unknown | undefined;
|
14
23
|
}
|
@@ -0,0 +1,7 @@
|
|
1
|
+
import { BaseModelCallFinishedEvent, BaseModelCallStartedEvent } from "../ModelCallEvent.js";
|
2
|
+
export interface StructureStreamingStartedEvent extends BaseModelCallStartedEvent {
|
3
|
+
functionType: "structure-streaming";
|
4
|
+
}
|
5
|
+
export interface StructureStreamingFinishedEvent extends BaseModelCallFinishedEvent {
|
6
|
+
functionType: "structure-streaming";
|
7
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1,215 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.fixJson = void 0;
|
4
|
+
// Implemented as a scanner with additional fixing
|
5
|
+
// that performs a single linear time scan pass over the partial JSON:
|
6
|
+
function fixJson(input) {
|
7
|
+
const stack = ["BEFORE_VALUE"];
|
8
|
+
let lastValidIndex = -1;
|
9
|
+
let literalStart = null;
|
10
|
+
for (let i = 0; i < input.length; i++) {
|
11
|
+
const char = input[i];
|
12
|
+
const currentState = stack[stack.length - 1];
|
13
|
+
switch (currentState) {
|
14
|
+
case "BEFORE_VALUE": {
|
15
|
+
switch (char) {
|
16
|
+
case '"': {
|
17
|
+
lastValidIndex = i;
|
18
|
+
stack.pop();
|
19
|
+
stack.push("INSIDE_STRING");
|
20
|
+
break;
|
21
|
+
}
|
22
|
+
case "f":
|
23
|
+
case "t":
|
24
|
+
case "n": {
|
25
|
+
lastValidIndex = i;
|
26
|
+
literalStart = i;
|
27
|
+
stack.pop();
|
28
|
+
stack.push("INSIDE_LITERAL");
|
29
|
+
break;
|
30
|
+
}
|
31
|
+
case "-": {
|
32
|
+
stack.pop();
|
33
|
+
stack.push("INSIDE_NUMBER");
|
34
|
+
break;
|
35
|
+
}
|
36
|
+
case "0":
|
37
|
+
case "1":
|
38
|
+
case "2":
|
39
|
+
case "3":
|
40
|
+
case "4":
|
41
|
+
case "5":
|
42
|
+
case "6":
|
43
|
+
case "7":
|
44
|
+
case "8":
|
45
|
+
case "9": {
|
46
|
+
lastValidIndex = i;
|
47
|
+
stack.pop();
|
48
|
+
stack.push("INSIDE_NUMBER");
|
49
|
+
break;
|
50
|
+
}
|
51
|
+
case "{": {
|
52
|
+
lastValidIndex = i;
|
53
|
+
stack.pop();
|
54
|
+
stack.push("INSIDE_OBJECT");
|
55
|
+
break;
|
56
|
+
}
|
57
|
+
case "[": {
|
58
|
+
lastValidIndex = i;
|
59
|
+
stack.pop();
|
60
|
+
stack.push("INSIDE_ARRAY");
|
61
|
+
stack.push("BEFORE_VALUE");
|
62
|
+
break;
|
63
|
+
}
|
64
|
+
}
|
65
|
+
break;
|
66
|
+
}
|
67
|
+
case "INSIDE_OBJECT": {
|
68
|
+
switch (char) {
|
69
|
+
case '"': {
|
70
|
+
stack.push("INSIDE_OBJECT_KEY");
|
71
|
+
break;
|
72
|
+
}
|
73
|
+
}
|
74
|
+
break;
|
75
|
+
}
|
76
|
+
case "INSIDE_OBJECT_KEY": {
|
77
|
+
switch (char) {
|
78
|
+
case '"': {
|
79
|
+
stack.pop();
|
80
|
+
stack.push("AFTER_OBJECT_KEY");
|
81
|
+
break;
|
82
|
+
}
|
83
|
+
}
|
84
|
+
break;
|
85
|
+
}
|
86
|
+
case "AFTER_OBJECT_KEY": {
|
87
|
+
switch (char) {
|
88
|
+
case ":": {
|
89
|
+
stack.pop();
|
90
|
+
stack.push("BEFORE_VALUE");
|
91
|
+
break;
|
92
|
+
}
|
93
|
+
}
|
94
|
+
break;
|
95
|
+
}
|
96
|
+
case "INSIDE_STRING": {
|
97
|
+
switch (char) {
|
98
|
+
case '"': {
|
99
|
+
stack.pop();
|
100
|
+
lastValidIndex = i;
|
101
|
+
break;
|
102
|
+
}
|
103
|
+
case "\\": {
|
104
|
+
stack.push("INSIDE_STRING_ESCAPE");
|
105
|
+
break;
|
106
|
+
}
|
107
|
+
default: {
|
108
|
+
lastValidIndex = i;
|
109
|
+
}
|
110
|
+
}
|
111
|
+
break;
|
112
|
+
}
|
113
|
+
case "INSIDE_ARRAY": {
|
114
|
+
switch (char) {
|
115
|
+
case ",": {
|
116
|
+
stack.push("BEFORE_VALUE");
|
117
|
+
break;
|
118
|
+
}
|
119
|
+
default: {
|
120
|
+
lastValidIndex = i;
|
121
|
+
break;
|
122
|
+
}
|
123
|
+
}
|
124
|
+
break;
|
125
|
+
}
|
126
|
+
case "INSIDE_STRING_ESCAPE": {
|
127
|
+
stack.pop();
|
128
|
+
lastValidIndex = i;
|
129
|
+
break;
|
130
|
+
}
|
131
|
+
case "INSIDE_NUMBER": {
|
132
|
+
switch (char) {
|
133
|
+
case "0":
|
134
|
+
case "1":
|
135
|
+
case "2":
|
136
|
+
case "3":
|
137
|
+
case "4":
|
138
|
+
case "5":
|
139
|
+
case "6":
|
140
|
+
case "7":
|
141
|
+
case "8":
|
142
|
+
case "9": {
|
143
|
+
lastValidIndex = i;
|
144
|
+
break;
|
145
|
+
}
|
146
|
+
case "e":
|
147
|
+
case "E":
|
148
|
+
case "-":
|
149
|
+
case ".": {
|
150
|
+
break;
|
151
|
+
}
|
152
|
+
case ",": {
|
153
|
+
stack.pop();
|
154
|
+
if (stack[stack.length - 1] === "INSIDE_ARRAY") {
|
155
|
+
stack.push("BEFORE_VALUE");
|
156
|
+
}
|
157
|
+
break;
|
158
|
+
}
|
159
|
+
default: {
|
160
|
+
stack.pop();
|
161
|
+
break;
|
162
|
+
}
|
163
|
+
}
|
164
|
+
break;
|
165
|
+
}
|
166
|
+
case "INSIDE_LITERAL": {
|
167
|
+
const partialLiteral = input.substring(literalStart, i);
|
168
|
+
if (!"false".startsWith(partialLiteral) &&
|
169
|
+
!"true".startsWith(partialLiteral) &&
|
170
|
+
!"null".startsWith(partialLiteral)) {
|
171
|
+
stack.pop();
|
172
|
+
if (stack[stack.length - 1] === "INSIDE_ARRAY") {
|
173
|
+
stack.push("BEFORE_VALUE");
|
174
|
+
}
|
175
|
+
}
|
176
|
+
else {
|
177
|
+
lastValidIndex = i;
|
178
|
+
}
|
179
|
+
break;
|
180
|
+
}
|
181
|
+
}
|
182
|
+
}
|
183
|
+
let result = input.slice(0, lastValidIndex + 1);
|
184
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
185
|
+
const state = stack[i];
|
186
|
+
switch (state) {
|
187
|
+
case "INSIDE_STRING": {
|
188
|
+
result += '"';
|
189
|
+
break;
|
190
|
+
}
|
191
|
+
case "INSIDE_OBJECT": {
|
192
|
+
result += "}";
|
193
|
+
break;
|
194
|
+
}
|
195
|
+
case "INSIDE_ARRAY": {
|
196
|
+
result += "]";
|
197
|
+
break;
|
198
|
+
}
|
199
|
+
case "INSIDE_LITERAL": {
|
200
|
+
const partialLiteral = input.substring(literalStart, input.length);
|
201
|
+
if ("true".startsWith(partialLiteral)) {
|
202
|
+
result += "true".slice(partialLiteral.length);
|
203
|
+
}
|
204
|
+
else if ("false".startsWith(partialLiteral)) {
|
205
|
+
result += "false".slice(partialLiteral.length);
|
206
|
+
}
|
207
|
+
else if ("null".startsWith(partialLiteral)) {
|
208
|
+
result += "null".slice(partialLiteral.length);
|
209
|
+
}
|
210
|
+
}
|
211
|
+
}
|
212
|
+
}
|
213
|
+
return result;
|
214
|
+
}
|
215
|
+
exports.fixJson = fixJson;
|
@@ -0,0 +1 @@
|
|
1
|
+
export declare function fixJson(input: string): string;
|
@@ -0,0 +1,211 @@
|
|
1
|
+
// Implemented as a scanner with additional fixing
|
2
|
+
// that performs a single linear time scan pass over the partial JSON:
|
3
|
+
export function fixJson(input) {
|
4
|
+
const stack = ["BEFORE_VALUE"];
|
5
|
+
let lastValidIndex = -1;
|
6
|
+
let literalStart = null;
|
7
|
+
for (let i = 0; i < input.length; i++) {
|
8
|
+
const char = input[i];
|
9
|
+
const currentState = stack[stack.length - 1];
|
10
|
+
switch (currentState) {
|
11
|
+
case "BEFORE_VALUE": {
|
12
|
+
switch (char) {
|
13
|
+
case '"': {
|
14
|
+
lastValidIndex = i;
|
15
|
+
stack.pop();
|
16
|
+
stack.push("INSIDE_STRING");
|
17
|
+
break;
|
18
|
+
}
|
19
|
+
case "f":
|
20
|
+
case "t":
|
21
|
+
case "n": {
|
22
|
+
lastValidIndex = i;
|
23
|
+
literalStart = i;
|
24
|
+
stack.pop();
|
25
|
+
stack.push("INSIDE_LITERAL");
|
26
|
+
break;
|
27
|
+
}
|
28
|
+
case "-": {
|
29
|
+
stack.pop();
|
30
|
+
stack.push("INSIDE_NUMBER");
|
31
|
+
break;
|
32
|
+
}
|
33
|
+
case "0":
|
34
|
+
case "1":
|
35
|
+
case "2":
|
36
|
+
case "3":
|
37
|
+
case "4":
|
38
|
+
case "5":
|
39
|
+
case "6":
|
40
|
+
case "7":
|
41
|
+
case "8":
|
42
|
+
case "9": {
|
43
|
+
lastValidIndex = i;
|
44
|
+
stack.pop();
|
45
|
+
stack.push("INSIDE_NUMBER");
|
46
|
+
break;
|
47
|
+
}
|
48
|
+
case "{": {
|
49
|
+
lastValidIndex = i;
|
50
|
+
stack.pop();
|
51
|
+
stack.push("INSIDE_OBJECT");
|
52
|
+
break;
|
53
|
+
}
|
54
|
+
case "[": {
|
55
|
+
lastValidIndex = i;
|
56
|
+
stack.pop();
|
57
|
+
stack.push("INSIDE_ARRAY");
|
58
|
+
stack.push("BEFORE_VALUE");
|
59
|
+
break;
|
60
|
+
}
|
61
|
+
}
|
62
|
+
break;
|
63
|
+
}
|
64
|
+
case "INSIDE_OBJECT": {
|
65
|
+
switch (char) {
|
66
|
+
case '"': {
|
67
|
+
stack.push("INSIDE_OBJECT_KEY");
|
68
|
+
break;
|
69
|
+
}
|
70
|
+
}
|
71
|
+
break;
|
72
|
+
}
|
73
|
+
case "INSIDE_OBJECT_KEY": {
|
74
|
+
switch (char) {
|
75
|
+
case '"': {
|
76
|
+
stack.pop();
|
77
|
+
stack.push("AFTER_OBJECT_KEY");
|
78
|
+
break;
|
79
|
+
}
|
80
|
+
}
|
81
|
+
break;
|
82
|
+
}
|
83
|
+
case "AFTER_OBJECT_KEY": {
|
84
|
+
switch (char) {
|
85
|
+
case ":": {
|
86
|
+
stack.pop();
|
87
|
+
stack.push("BEFORE_VALUE");
|
88
|
+
break;
|
89
|
+
}
|
90
|
+
}
|
91
|
+
break;
|
92
|
+
}
|
93
|
+
case "INSIDE_STRING": {
|
94
|
+
switch (char) {
|
95
|
+
case '"': {
|
96
|
+
stack.pop();
|
97
|
+
lastValidIndex = i;
|
98
|
+
break;
|
99
|
+
}
|
100
|
+
case "\\": {
|
101
|
+
stack.push("INSIDE_STRING_ESCAPE");
|
102
|
+
break;
|
103
|
+
}
|
104
|
+
default: {
|
105
|
+
lastValidIndex = i;
|
106
|
+
}
|
107
|
+
}
|
108
|
+
break;
|
109
|
+
}
|
110
|
+
case "INSIDE_ARRAY": {
|
111
|
+
switch (char) {
|
112
|
+
case ",": {
|
113
|
+
stack.push("BEFORE_VALUE");
|
114
|
+
break;
|
115
|
+
}
|
116
|
+
default: {
|
117
|
+
lastValidIndex = i;
|
118
|
+
break;
|
119
|
+
}
|
120
|
+
}
|
121
|
+
break;
|
122
|
+
}
|
123
|
+
case "INSIDE_STRING_ESCAPE": {
|
124
|
+
stack.pop();
|
125
|
+
lastValidIndex = i;
|
126
|
+
break;
|
127
|
+
}
|
128
|
+
case "INSIDE_NUMBER": {
|
129
|
+
switch (char) {
|
130
|
+
case "0":
|
131
|
+
case "1":
|
132
|
+
case "2":
|
133
|
+
case "3":
|
134
|
+
case "4":
|
135
|
+
case "5":
|
136
|
+
case "6":
|
137
|
+
case "7":
|
138
|
+
case "8":
|
139
|
+
case "9": {
|
140
|
+
lastValidIndex = i;
|
141
|
+
break;
|
142
|
+
}
|
143
|
+
case "e":
|
144
|
+
case "E":
|
145
|
+
case "-":
|
146
|
+
case ".": {
|
147
|
+
break;
|
148
|
+
}
|
149
|
+
case ",": {
|
150
|
+
stack.pop();
|
151
|
+
if (stack[stack.length - 1] === "INSIDE_ARRAY") {
|
152
|
+
stack.push("BEFORE_VALUE");
|
153
|
+
}
|
154
|
+
break;
|
155
|
+
}
|
156
|
+
default: {
|
157
|
+
stack.pop();
|
158
|
+
break;
|
159
|
+
}
|
160
|
+
}
|
161
|
+
break;
|
162
|
+
}
|
163
|
+
case "INSIDE_LITERAL": {
|
164
|
+
const partialLiteral = input.substring(literalStart, i);
|
165
|
+
if (!"false".startsWith(partialLiteral) &&
|
166
|
+
!"true".startsWith(partialLiteral) &&
|
167
|
+
!"null".startsWith(partialLiteral)) {
|
168
|
+
stack.pop();
|
169
|
+
if (stack[stack.length - 1] === "INSIDE_ARRAY") {
|
170
|
+
stack.push("BEFORE_VALUE");
|
171
|
+
}
|
172
|
+
}
|
173
|
+
else {
|
174
|
+
lastValidIndex = i;
|
175
|
+
}
|
176
|
+
break;
|
177
|
+
}
|
178
|
+
}
|
179
|
+
}
|
180
|
+
let result = input.slice(0, lastValidIndex + 1);
|
181
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
182
|
+
const state = stack[i];
|
183
|
+
switch (state) {
|
184
|
+
case "INSIDE_STRING": {
|
185
|
+
result += '"';
|
186
|
+
break;
|
187
|
+
}
|
188
|
+
case "INSIDE_OBJECT": {
|
189
|
+
result += "}";
|
190
|
+
break;
|
191
|
+
}
|
192
|
+
case "INSIDE_ARRAY": {
|
193
|
+
result += "]";
|
194
|
+
break;
|
195
|
+
}
|
196
|
+
case "INSIDE_LITERAL": {
|
197
|
+
const partialLiteral = input.substring(literalStart, input.length);
|
198
|
+
if ("true".startsWith(partialLiteral)) {
|
199
|
+
result += "true".slice(partialLiteral.length);
|
200
|
+
}
|
201
|
+
else if ("false".startsWith(partialLiteral)) {
|
202
|
+
result += "false".slice(partialLiteral.length);
|
203
|
+
}
|
204
|
+
else if ("null".startsWith(partialLiteral)) {
|
205
|
+
result += "null".slice(partialLiteral.length);
|
206
|
+
}
|
207
|
+
}
|
208
|
+
}
|
209
|
+
}
|
210
|
+
return result;
|
211
|
+
}
|