modelfusion 0.34.0 → 0.35.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 +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 +327 -0
- package/model-function/generate-structure/fixJson.d.ts +1 -0
- package/model-function/generate-structure/fixJson.js +323 -0
- package/model-function/generate-structure/fixJson.test.cjs +186 -0
- package/model-function/generate-structure/fixJson.test.d.ts +1 -0
- package/model-function/generate-structure/fixJson.test.js +184 -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 +16 -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
@@ -0,0 +1,184 @@
|
|
1
|
+
import { expect, test, describe } from "vitest";
|
2
|
+
import { fixJson } from "./fixJson";
|
3
|
+
describe("fixJson", () => {
|
4
|
+
test("should handle empty input", () => {
|
5
|
+
expect(fixJson("")).toBe("");
|
6
|
+
});
|
7
|
+
describe("literals", () => {
|
8
|
+
test("should handle incomplete null", () => {
|
9
|
+
expect(fixJson("nul")).toBe("null");
|
10
|
+
});
|
11
|
+
test("should handle incomplete true", () => {
|
12
|
+
expect(fixJson("t")).toBe("true");
|
13
|
+
});
|
14
|
+
test("should handle incomplete false", () => {
|
15
|
+
expect(fixJson("fals")).toBe("false");
|
16
|
+
});
|
17
|
+
});
|
18
|
+
describe("number", () => {
|
19
|
+
test("should handle incomplete numbers", () => {
|
20
|
+
expect(fixJson("12.")).toBe("12");
|
21
|
+
});
|
22
|
+
test("should handle numbers with dot", () => {
|
23
|
+
expect(fixJson("12.2")).toBe("12.2");
|
24
|
+
});
|
25
|
+
test("should handle negative numbers", () => {
|
26
|
+
expect(fixJson("-12")).toBe("-12");
|
27
|
+
});
|
28
|
+
test("should handle incomplete negative numbers", () => {
|
29
|
+
expect(fixJson("-")).toBe("");
|
30
|
+
});
|
31
|
+
test("should handle e-notation numbers", () => {
|
32
|
+
expect(fixJson("2.5e")).toBe("2.5");
|
33
|
+
expect(fixJson("2.5e-")).toBe("2.5");
|
34
|
+
expect(fixJson("2.5e3")).toBe("2.5e3");
|
35
|
+
expect(fixJson("-2.5e3")).toBe("-2.5e3");
|
36
|
+
});
|
37
|
+
test("should handle uppercase e-notation numbers", () => {
|
38
|
+
expect(fixJson("2.5E")).toBe("2.5");
|
39
|
+
expect(fixJson("2.5E-")).toBe("2.5");
|
40
|
+
expect(fixJson("2.5E3")).toBe("2.5E3");
|
41
|
+
expect(fixJson("-2.5E3")).toBe("-2.5E3");
|
42
|
+
});
|
43
|
+
test("should handle incomplete numbers", () => {
|
44
|
+
expect(fixJson("12.e")).toBe("12");
|
45
|
+
expect(fixJson("12.34e")).toBe("12.34");
|
46
|
+
expect(fixJson("5e")).toBe("5");
|
47
|
+
});
|
48
|
+
});
|
49
|
+
describe("string", () => {
|
50
|
+
test("should handle incomplete strings", () => {
|
51
|
+
expect(fixJson('"abc')).toBe('"abc"');
|
52
|
+
});
|
53
|
+
test("should handle escape sequences", () => {
|
54
|
+
expect(fixJson('"value with \\"quoted\\" text and \\\\ escape')).toBe('"value with \\"quoted\\" text and \\\\ escape"');
|
55
|
+
});
|
56
|
+
test("should handle incomplete escape sequences", () => {
|
57
|
+
expect(fixJson('"value with \\')).toBe('"value with "');
|
58
|
+
});
|
59
|
+
test("should handle unicode characters", () => {
|
60
|
+
expect(fixJson('"value with unicode \u003C"')).toBe('"value with unicode \u003C"');
|
61
|
+
});
|
62
|
+
});
|
63
|
+
describe("array", () => {
|
64
|
+
test("should handle incomplete array", () => {
|
65
|
+
expect(fixJson("[")).toBe("[]");
|
66
|
+
});
|
67
|
+
test("should handle closing bracket after number in array", () => {
|
68
|
+
expect(fixJson("[[1], [2")).toBe("[[1], [2]]");
|
69
|
+
});
|
70
|
+
test("should handle closing bracket after string in array", () => {
|
71
|
+
expect(fixJson(`[["1"], ["2`)).toBe(`[["1"], ["2"]]`);
|
72
|
+
});
|
73
|
+
test("should handle closing bracket after literal in array", () => {
|
74
|
+
expect(fixJson("[[false], [nu")).toBe("[[false], [null]]");
|
75
|
+
});
|
76
|
+
test("should handle closing bracket after array in array", () => {
|
77
|
+
expect(fixJson("[[[]], [[]")).toBe("[[[]], [[]]]");
|
78
|
+
});
|
79
|
+
test("should handle closing bracket after object in array", () => {
|
80
|
+
expect(fixJson("[[{}], [{")).toBe("[[{}], [{}]]");
|
81
|
+
});
|
82
|
+
test("should handle trailing comma", () => {
|
83
|
+
expect(fixJson("[1, ")).toBe("[1]");
|
84
|
+
});
|
85
|
+
test("should handle closing array", () => {
|
86
|
+
expect(fixJson("[[], 123")).toBe("[[], 123]");
|
87
|
+
});
|
88
|
+
});
|
89
|
+
describe("object", () => {
|
90
|
+
test("should handle keys without values", () => {
|
91
|
+
expect(fixJson('{"key":')).toBe("{}");
|
92
|
+
});
|
93
|
+
test("should handle closing brace after number in object", () => {
|
94
|
+
expect(fixJson('{"a": {"b": 1}, "c": {"d": 2')).toBe('{"a": {"b": 1}, "c": {"d": 2}}');
|
95
|
+
});
|
96
|
+
test("should handle closing brace after string in object", () => {
|
97
|
+
expect(fixJson('{"a": {"b": "1"}, "c": {"d": 2')).toBe('{"a": {"b": "1"}, "c": {"d": 2}}');
|
98
|
+
});
|
99
|
+
test("should handle closing brace after literal in object", () => {
|
100
|
+
expect(fixJson('{"a": {"b": false}, "c": {"d": 2')).toBe('{"a": {"b": false}, "c": {"d": 2}}');
|
101
|
+
});
|
102
|
+
test("should handle closing brace after array in object", () => {
|
103
|
+
expect(fixJson('{"a": {"b": []}, "c": {"d": 2')).toBe('{"a": {"b": []}, "c": {"d": 2}}');
|
104
|
+
});
|
105
|
+
test("should handle closing brace after object in object", () => {
|
106
|
+
expect(fixJson('{"a": {"b": {}}, "c": {"d": 2')).toBe('{"a": {"b": {}}, "c": {"d": 2}}');
|
107
|
+
});
|
108
|
+
test("should handle partial keys (first key)", () => {
|
109
|
+
expect(fixJson('{"ke')).toBe("{}");
|
110
|
+
});
|
111
|
+
test("should handle partial keys (second key)", () => {
|
112
|
+
expect(fixJson('{"k1": 1, "k2')).toBe('{"k1": 1}');
|
113
|
+
});
|
114
|
+
test("should handle partial keys with colon (second key)", () => {
|
115
|
+
expect(fixJson('{"k1": 1, "k2":')).toBe('{"k1": 1}');
|
116
|
+
});
|
117
|
+
test("should handle trailing whitespaces", () => {
|
118
|
+
expect(fixJson('{"key": "value" ')).toBe('{"key": "value"}');
|
119
|
+
});
|
120
|
+
});
|
121
|
+
describe("nesting", () => {
|
122
|
+
test("should handle nested arrays with numbers", () => {
|
123
|
+
expect(fixJson("[1, [2, 3, [")).toBe("[1, [2, 3, []]]");
|
124
|
+
});
|
125
|
+
test("should handle nested arrays with literals", () => {
|
126
|
+
expect(fixJson("[false, [true, [")).toBe("[false, [true, []]]");
|
127
|
+
});
|
128
|
+
test("should handle nested objects", () => {
|
129
|
+
expect(fixJson('{"key": {"subKey":')).toBe('{"key": {}}');
|
130
|
+
});
|
131
|
+
test("should handle nested objects with numbers", () => {
|
132
|
+
expect(fixJson('{"key": 123, "key2": {"subKey":')).toBe('{"key": 123, "key2": {}}');
|
133
|
+
});
|
134
|
+
test("should handle nested objects with literals", () => {
|
135
|
+
expect(fixJson('{"key": null, "key2": {"subKey":')).toBe('{"key": null, "key2": {}}');
|
136
|
+
});
|
137
|
+
test("should handle arrays within objects", () => {
|
138
|
+
expect(fixJson('{"key": [1, 2, {')).toBe('{"key": [1, 2, {}]}');
|
139
|
+
});
|
140
|
+
test("should handle objects within arrays", () => {
|
141
|
+
expect(fixJson('[1, 2, {"key": "value",')).toBe('[1, 2, {"key": "value"}]');
|
142
|
+
});
|
143
|
+
test("should handle nested arrays and objects", () => {
|
144
|
+
expect(fixJson('{"a": {"b": ["c", {"d": "e",')).toBe('{"a": {"b": ["c", {"d": "e"}]}}');
|
145
|
+
});
|
146
|
+
test("should handle deeply nested structures", () => {
|
147
|
+
expect(fixJson('{"a": {"b": {"c": {"d":')).toBe('{"a": {"b": {"c": {}}}}');
|
148
|
+
});
|
149
|
+
test("should handle potential nested arrays or objects", () => {
|
150
|
+
expect(fixJson('{"a": 1, "b": [')).toBe('{"a": 1, "b": []}');
|
151
|
+
expect(fixJson('{"a": 1, "b": {')).toBe('{"a": 1, "b": {}}');
|
152
|
+
expect(fixJson('{"a": 1, "b": "')).toBe('{"a": 1, "b": ""}');
|
153
|
+
});
|
154
|
+
});
|
155
|
+
describe("regression", () => {
|
156
|
+
test("should handle complex nesting 1", () => {
|
157
|
+
expect(fixJson([
|
158
|
+
"{",
|
159
|
+
' "a": [',
|
160
|
+
" {",
|
161
|
+
' "a1": "v1",',
|
162
|
+
' "a2": "v2",',
|
163
|
+
` "a3": "v3"`,
|
164
|
+
" }",
|
165
|
+
" ],",
|
166
|
+
' "b": [',
|
167
|
+
" {",
|
168
|
+
' "b1": "n',
|
169
|
+
].join("\n"))).toBe([
|
170
|
+
"{",
|
171
|
+
' "a": [',
|
172
|
+
" {",
|
173
|
+
' "a1": "v1",',
|
174
|
+
' "a2": "v2",',
|
175
|
+
` "a3": "v3"`,
|
176
|
+
" }",
|
177
|
+
" ],",
|
178
|
+
' "b": [',
|
179
|
+
" {",
|
180
|
+
' "b1": "n"}]}',
|
181
|
+
].join("\n"));
|
182
|
+
});
|
183
|
+
});
|
184
|
+
});
|
@@ -3,7 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.generateStructure = void 0;
|
4
4
|
const executeCall_js_1 = require("../executeCall.cjs");
|
5
5
|
const StructureValidationError_js_1 = require("./StructureValidationError.cjs");
|
6
|
-
function generateStructure(
|
6
|
+
function generateStructure(
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
8
|
+
model, structureDefinition, prompt, options) {
|
7
9
|
// Note: PROMPT must not be a function.
|
8
10
|
const expandedPrompt = typeof prompt === "function"
|
9
11
|
? prompt(structureDefinition)
|
@@ -2,4 +2,4 @@ import { StructureDefinition } from "../../core/structure/StructureDefinition.js
|
|
2
2
|
import { ModelFunctionOptions } from "../ModelFunctionOptions.js";
|
3
3
|
import { ModelFunctionPromise } from "../executeCall.js";
|
4
4
|
import { StructureGenerationModel, StructureGenerationModelSettings } from "./StructureGenerationModel.js";
|
5
|
-
export declare function generateStructure<STRUCTURE, PROMPT, RESPONSE, NAME extends string, SETTINGS extends StructureGenerationModelSettings>(model: StructureGenerationModel<PROMPT, RESPONSE, SETTINGS>, structureDefinition: StructureDefinition<NAME, STRUCTURE>, prompt: PROMPT | ((structureDefinition: StructureDefinition<NAME, STRUCTURE>) => PROMPT), options?: ModelFunctionOptions<SETTINGS>): ModelFunctionPromise<STRUCTURE, RESPONSE>;
|
5
|
+
export declare function generateStructure<STRUCTURE, PROMPT, RESPONSE, NAME extends string, SETTINGS extends StructureGenerationModelSettings>(model: StructureGenerationModel<PROMPT, RESPONSE, any, SETTINGS>, structureDefinition: StructureDefinition<NAME, STRUCTURE>, prompt: PROMPT | ((structureDefinition: StructureDefinition<NAME, STRUCTURE>) => PROMPT), options?: ModelFunctionOptions<SETTINGS>): ModelFunctionPromise<STRUCTURE, RESPONSE>;
|
@@ -1,6 +1,8 @@
|
|
1
1
|
import { executeCall } from "../executeCall.js";
|
2
2
|
import { StructureValidationError } from "./StructureValidationError.js";
|
3
|
-
export function generateStructure(
|
3
|
+
export function generateStructure(
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
5
|
+
model, structureDefinition, prompt, options) {
|
4
6
|
// Note: PROMPT must not be a function.
|
5
7
|
const expandedPrompt = typeof prompt === "function"
|
6
8
|
? prompt(structureDefinition)
|
@@ -0,0 +1,29 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.parsePartialJson = void 0;
|
7
|
+
const secure_json_parse_1 = __importDefault(require("secure-json-parse"));
|
8
|
+
const fixJson_js_1 = require("./fixJson.cjs");
|
9
|
+
function parsePartialJson(jsonText) {
|
10
|
+
if (jsonText == null) {
|
11
|
+
return undefined;
|
12
|
+
}
|
13
|
+
try {
|
14
|
+
// first attempt a regular JSON parse:
|
15
|
+
return secure_json_parse_1.default.parse(jsonText);
|
16
|
+
}
|
17
|
+
catch (ignored) {
|
18
|
+
try {
|
19
|
+
// then try to fix the partial JSON and parse it:
|
20
|
+
const fixedJsonText = (0, fixJson_js_1.fixJson)(jsonText);
|
21
|
+
return secure_json_parse_1.default.parse(fixedJsonText);
|
22
|
+
}
|
23
|
+
catch (ignored) {
|
24
|
+
// ignored
|
25
|
+
}
|
26
|
+
}
|
27
|
+
return undefined;
|
28
|
+
}
|
29
|
+
exports.parsePartialJson = parsePartialJson;
|
@@ -0,0 +1 @@
|
|
1
|
+
export declare function parsePartialJson(jsonText: string | undefined): unknown | undefined;
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import SecureJSON from "secure-json-parse";
|
2
|
+
import { fixJson } from "./fixJson.js";
|
3
|
+
export function parsePartialJson(jsonText) {
|
4
|
+
if (jsonText == null) {
|
5
|
+
return undefined;
|
6
|
+
}
|
7
|
+
try {
|
8
|
+
// first attempt a regular JSON parse:
|
9
|
+
return SecureJSON.parse(jsonText);
|
10
|
+
}
|
11
|
+
catch (ignored) {
|
12
|
+
try {
|
13
|
+
// then try to fix the partial JSON and parse it:
|
14
|
+
const fixedJsonText = fixJson(jsonText);
|
15
|
+
return SecureJSON.parse(fixedJsonText);
|
16
|
+
}
|
17
|
+
catch (ignored) {
|
18
|
+
// ignored
|
19
|
+
}
|
20
|
+
}
|
21
|
+
return undefined;
|
22
|
+
}
|
@@ -0,0 +1,167 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.streamStructure = void 0;
|
7
|
+
const deep_equal_1 = __importDefault(require("deep-equal"));
|
8
|
+
const nanoid_1 = require("nanoid");
|
9
|
+
const FunctionEventSource_js_1 = require("../../core/FunctionEventSource.cjs");
|
10
|
+
const GlobalFunctionLogging_js_1 = require("../../core/GlobalFunctionLogging.cjs");
|
11
|
+
const GlobalFunctionObservers_js_1 = require("../../core/GlobalFunctionObservers.cjs");
|
12
|
+
const AbortError_js_1 = require("../../core/api/AbortError.cjs");
|
13
|
+
const getFunctionCallLogger_js_1 = require("../../core/getFunctionCallLogger.cjs");
|
14
|
+
const DurationMeasurement_js_1 = require("../../util/DurationMeasurement.cjs");
|
15
|
+
const runSafe_js_1 = require("../../util/runSafe.cjs");
|
16
|
+
const AsyncIterableResultPromise_js_1 = require("../AsyncIterableResultPromise.cjs");
|
17
|
+
function streamStructure(model, structureDefinition, prompt, options) {
|
18
|
+
return new AsyncIterableResultPromise_js_1.AsyncIterableResultPromise(doStreamStructure(model, structureDefinition, prompt, options));
|
19
|
+
}
|
20
|
+
exports.streamStructure = streamStructure;
|
21
|
+
async function doStreamStructure(model, structureDefinition, prompt, options) {
|
22
|
+
if (options?.settings != null) {
|
23
|
+
model = model.withSettings(options.settings);
|
24
|
+
options = {
|
25
|
+
functionId: options.functionId,
|
26
|
+
observers: options.observers,
|
27
|
+
run: options.run,
|
28
|
+
};
|
29
|
+
}
|
30
|
+
const run = options?.run;
|
31
|
+
const settings = model.settings;
|
32
|
+
const eventSource = new FunctionEventSource_js_1.FunctionEventSource({
|
33
|
+
observers: [
|
34
|
+
...(0, getFunctionCallLogger_js_1.getFunctionCallLogger)(options?.logging ?? (0, GlobalFunctionLogging_js_1.getGlobalFunctionLogging)()),
|
35
|
+
...(0, GlobalFunctionObservers_js_1.getGlobalFunctionObservers)(),
|
36
|
+
...(settings.observers ?? []),
|
37
|
+
...(run?.functionObserver != null ? [run.functionObserver] : []),
|
38
|
+
...(options?.observers ?? []),
|
39
|
+
],
|
40
|
+
errorHandler: run?.errorHandler,
|
41
|
+
});
|
42
|
+
const durationMeasurement = (0, DurationMeasurement_js_1.startDurationMeasurement)();
|
43
|
+
const startMetadata = {
|
44
|
+
functionType: "structure-streaming",
|
45
|
+
callId: `call-${(0, nanoid_1.nanoid)()}`,
|
46
|
+
runId: run?.runId,
|
47
|
+
sessionId: run?.sessionId,
|
48
|
+
userId: run?.userId,
|
49
|
+
functionId: options?.functionId,
|
50
|
+
model: model.modelInformation,
|
51
|
+
settings: model.settingsForEvent,
|
52
|
+
input: prompt,
|
53
|
+
timestamp: durationMeasurement.startDate,
|
54
|
+
startTimestamp: durationMeasurement.startDate,
|
55
|
+
};
|
56
|
+
eventSource.notify({
|
57
|
+
eventType: "started",
|
58
|
+
...startMetadata,
|
59
|
+
});
|
60
|
+
const result = await (0, runSafe_js_1.runSafe)(async () => {
|
61
|
+
const deltaIterable = await model.generateStructureStreamResponse(structureDefinition, prompt, {
|
62
|
+
functionId: options?.functionId,
|
63
|
+
settings,
|
64
|
+
run,
|
65
|
+
});
|
66
|
+
return (async function* () {
|
67
|
+
function reportError(error) {
|
68
|
+
const finishMetadata = {
|
69
|
+
eventType: "finished",
|
70
|
+
...startMetadata,
|
71
|
+
finishTimestamp: new Date(),
|
72
|
+
durationInMs: durationMeasurement.durationInMs,
|
73
|
+
};
|
74
|
+
eventSource.notify(error instanceof AbortError_js_1.AbortError
|
75
|
+
? {
|
76
|
+
...finishMetadata,
|
77
|
+
result: {
|
78
|
+
status: "abort",
|
79
|
+
},
|
80
|
+
}
|
81
|
+
: {
|
82
|
+
...finishMetadata,
|
83
|
+
result: {
|
84
|
+
status: "error",
|
85
|
+
error,
|
86
|
+
},
|
87
|
+
});
|
88
|
+
}
|
89
|
+
let lastStructure;
|
90
|
+
let lastFullDelta;
|
91
|
+
for await (const event of deltaIterable) {
|
92
|
+
if (event?.type === "error") {
|
93
|
+
reportError(event.error);
|
94
|
+
throw event.error;
|
95
|
+
}
|
96
|
+
if (event?.type === "delta") {
|
97
|
+
const latestFullDelta = event.fullDelta;
|
98
|
+
const latestStructure = model.extractPartialStructure(latestFullDelta);
|
99
|
+
// only send a new part into the stream when the partial structure has changed:
|
100
|
+
if (!(0, deep_equal_1.default)(lastStructure, latestStructure, {
|
101
|
+
strict: true,
|
102
|
+
})) {
|
103
|
+
lastFullDelta = latestFullDelta;
|
104
|
+
lastStructure = latestStructure;
|
105
|
+
yield {
|
106
|
+
isComplete: false,
|
107
|
+
value: lastStructure,
|
108
|
+
};
|
109
|
+
}
|
110
|
+
}
|
111
|
+
}
|
112
|
+
// process the final result (full type validation):
|
113
|
+
const parseResult = structureDefinition.schema.validate(lastStructure);
|
114
|
+
if (!parseResult.success) {
|
115
|
+
reportError(parseResult.error);
|
116
|
+
throw parseResult.error;
|
117
|
+
}
|
118
|
+
yield {
|
119
|
+
isComplete: true,
|
120
|
+
value: parseResult.value,
|
121
|
+
};
|
122
|
+
const finishMetadata = {
|
123
|
+
eventType: "finished",
|
124
|
+
...startMetadata,
|
125
|
+
finishTimestamp: new Date(),
|
126
|
+
durationInMs: durationMeasurement.durationInMs,
|
127
|
+
};
|
128
|
+
eventSource.notify({
|
129
|
+
...finishMetadata,
|
130
|
+
result: {
|
131
|
+
status: "success",
|
132
|
+
response: lastFullDelta,
|
133
|
+
output: lastStructure,
|
134
|
+
},
|
135
|
+
});
|
136
|
+
})();
|
137
|
+
});
|
138
|
+
if (!result.ok) {
|
139
|
+
const finishMetadata = {
|
140
|
+
eventType: "finished",
|
141
|
+
...startMetadata,
|
142
|
+
finishTimestamp: new Date(),
|
143
|
+
durationInMs: durationMeasurement.durationInMs,
|
144
|
+
};
|
145
|
+
if (result.isAborted) {
|
146
|
+
eventSource.notify({
|
147
|
+
...finishMetadata,
|
148
|
+
result: {
|
149
|
+
status: "abort",
|
150
|
+
},
|
151
|
+
});
|
152
|
+
throw new AbortError_js_1.AbortError();
|
153
|
+
}
|
154
|
+
eventSource.notify({
|
155
|
+
...finishMetadata,
|
156
|
+
result: {
|
157
|
+
status: "error",
|
158
|
+
error: result.error,
|
159
|
+
},
|
160
|
+
});
|
161
|
+
throw result.error;
|
162
|
+
}
|
163
|
+
return {
|
164
|
+
output: result.output,
|
165
|
+
metadata: startMetadata,
|
166
|
+
};
|
167
|
+
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import { StructureDefinition } from "../../core/structure/StructureDefinition.js";
|
2
|
+
import { AsyncIterableResultPromise } from "../AsyncIterableResultPromise.js";
|
3
|
+
import { DeltaEvent } from "../DeltaEvent.js";
|
4
|
+
import { ModelFunctionOptions } from "../ModelFunctionOptions.js";
|
5
|
+
import { StructureGenerationModel, StructureGenerationModelSettings } from "./StructureGenerationModel.js";
|
6
|
+
export type StructureStreamPart<STRUCTURE> = {
|
7
|
+
isComplete: false;
|
8
|
+
value: unknown;
|
9
|
+
} | {
|
10
|
+
isComplete: true;
|
11
|
+
value: STRUCTURE;
|
12
|
+
};
|
13
|
+
export declare function streamStructure<STRUCTURE, PROMPT, FULL_DELTA, NAME extends string, SETTINGS extends StructureGenerationModelSettings>(model: StructureGenerationModel<PROMPT, unknown, FULL_DELTA, SETTINGS> & {
|
14
|
+
generateStructureStreamResponse: (structureDefinition: StructureDefinition<NAME, STRUCTURE>, prompt: PROMPT, options: ModelFunctionOptions<SETTINGS>) => PromiseLike<AsyncIterable<DeltaEvent<FULL_DELTA>>>;
|
15
|
+
extractPartialStructure: (fullDelta: FULL_DELTA) => unknown | undefined;
|
16
|
+
}, structureDefinition: StructureDefinition<NAME, STRUCTURE>, prompt: PROMPT, options?: ModelFunctionOptions<SETTINGS>): AsyncIterableResultPromise<StructureStreamPart<STRUCTURE>>;
|
@@ -0,0 +1,160 @@
|
|
1
|
+
import deepEqual from "deep-equal";
|
2
|
+
import { nanoid as createId } from "nanoid";
|
3
|
+
import { FunctionEventSource } from "../../core/FunctionEventSource.js";
|
4
|
+
import { getGlobalFunctionLogging } from "../../core/GlobalFunctionLogging.js";
|
5
|
+
import { getGlobalFunctionObservers } from "../../core/GlobalFunctionObservers.js";
|
6
|
+
import { AbortError } from "../../core/api/AbortError.js";
|
7
|
+
import { getFunctionCallLogger } from "../../core/getFunctionCallLogger.js";
|
8
|
+
import { startDurationMeasurement } from "../../util/DurationMeasurement.js";
|
9
|
+
import { runSafe } from "../../util/runSafe.js";
|
10
|
+
import { AsyncIterableResultPromise } from "../AsyncIterableResultPromise.js";
|
11
|
+
export function streamStructure(model, structureDefinition, prompt, options) {
|
12
|
+
return new AsyncIterableResultPromise(doStreamStructure(model, structureDefinition, prompt, options));
|
13
|
+
}
|
14
|
+
async function doStreamStructure(model, structureDefinition, prompt, options) {
|
15
|
+
if (options?.settings != null) {
|
16
|
+
model = model.withSettings(options.settings);
|
17
|
+
options = {
|
18
|
+
functionId: options.functionId,
|
19
|
+
observers: options.observers,
|
20
|
+
run: options.run,
|
21
|
+
};
|
22
|
+
}
|
23
|
+
const run = options?.run;
|
24
|
+
const settings = model.settings;
|
25
|
+
const eventSource = new FunctionEventSource({
|
26
|
+
observers: [
|
27
|
+
...getFunctionCallLogger(options?.logging ?? getGlobalFunctionLogging()),
|
28
|
+
...getGlobalFunctionObservers(),
|
29
|
+
...(settings.observers ?? []),
|
30
|
+
...(run?.functionObserver != null ? [run.functionObserver] : []),
|
31
|
+
...(options?.observers ?? []),
|
32
|
+
],
|
33
|
+
errorHandler: run?.errorHandler,
|
34
|
+
});
|
35
|
+
const durationMeasurement = startDurationMeasurement();
|
36
|
+
const startMetadata = {
|
37
|
+
functionType: "structure-streaming",
|
38
|
+
callId: `call-${createId()}`,
|
39
|
+
runId: run?.runId,
|
40
|
+
sessionId: run?.sessionId,
|
41
|
+
userId: run?.userId,
|
42
|
+
functionId: options?.functionId,
|
43
|
+
model: model.modelInformation,
|
44
|
+
settings: model.settingsForEvent,
|
45
|
+
input: prompt,
|
46
|
+
timestamp: durationMeasurement.startDate,
|
47
|
+
startTimestamp: durationMeasurement.startDate,
|
48
|
+
};
|
49
|
+
eventSource.notify({
|
50
|
+
eventType: "started",
|
51
|
+
...startMetadata,
|
52
|
+
});
|
53
|
+
const result = await runSafe(async () => {
|
54
|
+
const deltaIterable = await model.generateStructureStreamResponse(structureDefinition, prompt, {
|
55
|
+
functionId: options?.functionId,
|
56
|
+
settings,
|
57
|
+
run,
|
58
|
+
});
|
59
|
+
return (async function* () {
|
60
|
+
function reportError(error) {
|
61
|
+
const finishMetadata = {
|
62
|
+
eventType: "finished",
|
63
|
+
...startMetadata,
|
64
|
+
finishTimestamp: new Date(),
|
65
|
+
durationInMs: durationMeasurement.durationInMs,
|
66
|
+
};
|
67
|
+
eventSource.notify(error instanceof AbortError
|
68
|
+
? {
|
69
|
+
...finishMetadata,
|
70
|
+
result: {
|
71
|
+
status: "abort",
|
72
|
+
},
|
73
|
+
}
|
74
|
+
: {
|
75
|
+
...finishMetadata,
|
76
|
+
result: {
|
77
|
+
status: "error",
|
78
|
+
error,
|
79
|
+
},
|
80
|
+
});
|
81
|
+
}
|
82
|
+
let lastStructure;
|
83
|
+
let lastFullDelta;
|
84
|
+
for await (const event of deltaIterable) {
|
85
|
+
if (event?.type === "error") {
|
86
|
+
reportError(event.error);
|
87
|
+
throw event.error;
|
88
|
+
}
|
89
|
+
if (event?.type === "delta") {
|
90
|
+
const latestFullDelta = event.fullDelta;
|
91
|
+
const latestStructure = model.extractPartialStructure(latestFullDelta);
|
92
|
+
// only send a new part into the stream when the partial structure has changed:
|
93
|
+
if (!deepEqual(lastStructure, latestStructure, {
|
94
|
+
strict: true,
|
95
|
+
})) {
|
96
|
+
lastFullDelta = latestFullDelta;
|
97
|
+
lastStructure = latestStructure;
|
98
|
+
yield {
|
99
|
+
isComplete: false,
|
100
|
+
value: lastStructure,
|
101
|
+
};
|
102
|
+
}
|
103
|
+
}
|
104
|
+
}
|
105
|
+
// process the final result (full type validation):
|
106
|
+
const parseResult = structureDefinition.schema.validate(lastStructure);
|
107
|
+
if (!parseResult.success) {
|
108
|
+
reportError(parseResult.error);
|
109
|
+
throw parseResult.error;
|
110
|
+
}
|
111
|
+
yield {
|
112
|
+
isComplete: true,
|
113
|
+
value: parseResult.value,
|
114
|
+
};
|
115
|
+
const finishMetadata = {
|
116
|
+
eventType: "finished",
|
117
|
+
...startMetadata,
|
118
|
+
finishTimestamp: new Date(),
|
119
|
+
durationInMs: durationMeasurement.durationInMs,
|
120
|
+
};
|
121
|
+
eventSource.notify({
|
122
|
+
...finishMetadata,
|
123
|
+
result: {
|
124
|
+
status: "success",
|
125
|
+
response: lastFullDelta,
|
126
|
+
output: lastStructure,
|
127
|
+
},
|
128
|
+
});
|
129
|
+
})();
|
130
|
+
});
|
131
|
+
if (!result.ok) {
|
132
|
+
const finishMetadata = {
|
133
|
+
eventType: "finished",
|
134
|
+
...startMetadata,
|
135
|
+
finishTimestamp: new Date(),
|
136
|
+
durationInMs: durationMeasurement.durationInMs,
|
137
|
+
};
|
138
|
+
if (result.isAborted) {
|
139
|
+
eventSource.notify({
|
140
|
+
...finishMetadata,
|
141
|
+
result: {
|
142
|
+
status: "abort",
|
143
|
+
},
|
144
|
+
});
|
145
|
+
throw new AbortError();
|
146
|
+
}
|
147
|
+
eventSource.notify({
|
148
|
+
...finishMetadata,
|
149
|
+
result: {
|
150
|
+
status: "error",
|
151
|
+
error: result.error,
|
152
|
+
},
|
153
|
+
});
|
154
|
+
throw result.error;
|
155
|
+
}
|
156
|
+
return {
|
157
|
+
output: result.output,
|
158
|
+
metadata: startMetadata,
|
159
|
+
};
|
160
|
+
}
|
@@ -1,9 +1,9 @@
|
|
1
1
|
import { PromptFormat } from "../../prompt/PromptFormat.js";
|
2
2
|
import { PromptFormatTextGenerationModel } from "../../prompt/PromptFormatTextGenerationModel.js";
|
3
|
-
import {
|
3
|
+
import { DeltaEvent } from "../DeltaEvent.js";
|
4
4
|
import { Model, ModelSettings } from "../Model.js";
|
5
|
+
import { ModelFunctionOptions } from "../ModelFunctionOptions.js";
|
5
6
|
import { BasicTokenizer, FullTokenizer } from "../tokenize-text/Tokenizer.js";
|
6
|
-
import { DeltaEvent } from "./DeltaEvent.js";
|
7
7
|
export interface TextGenerationModelSettings extends ModelSettings {
|
8
8
|
/**
|
9
9
|
* Maximum number of tokens to generate.
|
@@ -33,11 +33,11 @@ export interface TextGenerationModel<PROMPT, RESPONSE, FULL_DELTA, SETTINGS exte
|
|
33
33
|
/**
|
34
34
|
* Optional. Implement for streaming support.
|
35
35
|
*/
|
36
|
-
readonly generateDeltaStreamResponse
|
36
|
+
readonly generateDeltaStreamResponse?: (prompt: PROMPT, options: ModelFunctionOptions<SETTINGS>) => PromiseLike<AsyncIterable<DeltaEvent<FULL_DELTA>>>;
|
37
37
|
/**
|
38
38
|
* Optional. Implement for streaming support.
|
39
39
|
*/
|
40
|
-
readonly extractTextDelta
|
40
|
+
readonly extractTextDelta?: (fullDelta: FULL_DELTA) => string | undefined;
|
41
41
|
extractUsage?(response: RESPONSE): {
|
42
42
|
promptTokens: number;
|
43
43
|
completionTokens: number;
|