mdi-llmkit 1.0.1 → 1.0.6
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 +170 -190
- package/dist/src/comparison/compareLists.js +64 -64
- package/dist/src/jsonSurgery/jsonSurgery.js +285 -285
- package/dist/tests/comparison/compareLists.test.js +12 -12
- package/dist/tests/jsonSurgery/jsonSurgery.test.js +1 -1
- package/package.json +57 -53
- package/dist/gpt_api/functions.d.ts +0 -25
- package/dist/gpt_api/functions.js +0 -193
- package/dist/gpt_api/gpt_conversation.d.ts +0 -43
- package/dist/gpt_api/gpt_conversation.js +0 -146
- package/dist/gpt_api/json_schema_format.d.ts +0 -18
- package/dist/gpt_api/json_schema_format.js +0 -195
- package/dist/index.d.ts +0 -3
- package/dist/index.js +0 -3
|
@@ -35,16 +35,16 @@ const assertProcessedCountersAreSequential = (events) => {
|
|
|
35
35
|
expect(finishes[finishes.length - 1].totalLeftToProcess).toBe(0);
|
|
36
36
|
}
|
|
37
37
|
};
|
|
38
|
-
describe
|
|
38
|
+
describe('compareItemLists (live API)', () => {
|
|
39
39
|
// IMPORTANT: These tests intentionally use live OpenAI calls and DO NOT mock GptConversation.
|
|
40
40
|
// We are validating the real prompt+schema behavior end-to-end (including model decisions),
|
|
41
41
|
// not just local control-flow in isolation.
|
|
42
|
-
describe
|
|
42
|
+
describe('input validation', () => {
|
|
43
43
|
it('throws for duplicate item names (case-insensitive) within a list', async () => {
|
|
44
44
|
await expect(compareItemLists(createClient(), ['Widget', 'widget'], ['Other'])).rejects.toThrow('Duplicate item names found in before list');
|
|
45
45
|
});
|
|
46
46
|
});
|
|
47
|
-
describe
|
|
47
|
+
describe('string behavior', () => {
|
|
48
48
|
it('classifies case-insensitive exact string matches as unchanged', async () => {
|
|
49
49
|
const { events, callback } = collectEvents();
|
|
50
50
|
const result = await compareItemLists(createClient(), ['String Item A', 'String Item B'], ['string item a', 'STRING ITEM B'], 'Case-only differences are unchanged.', callback);
|
|
@@ -56,7 +56,7 @@ describe.concurrent('compareItemLists (live API)', () => {
|
|
|
56
56
|
expect(events).toHaveLength(0);
|
|
57
57
|
}, 180000);
|
|
58
58
|
});
|
|
59
|
-
describe
|
|
59
|
+
describe('name/description behavior', () => {
|
|
60
60
|
it('treats same names as unchanged even when descriptions differ', async () => {
|
|
61
61
|
const result = await compareItemLists(createClient(), [
|
|
62
62
|
{
|
|
@@ -94,7 +94,7 @@ describe.concurrent('compareItemLists (live API)', () => {
|
|
|
94
94
|
expect(result.renamed['Plan Bronze Legacy']).toBe('Plan Bronze Modern');
|
|
95
95
|
}, 180000);
|
|
96
96
|
});
|
|
97
|
-
describe
|
|
97
|
+
describe('rename behavior', () => {
|
|
98
98
|
it('detects a single guided rename', async () => {
|
|
99
99
|
const result = await compareItemLists(createClient(), ['ACME Legacy Plan'], ['ACME Modern Plan'], 'There is exactly one rename in this migration. ' +
|
|
100
100
|
'ACME Legacy Plan was renamed to ACME Modern Plan. ' +
|
|
@@ -120,7 +120,7 @@ describe.concurrent('compareItemLists (live API)', () => {
|
|
|
120
120
|
expect(result.unchanged).toEqual([]);
|
|
121
121
|
}, 180000);
|
|
122
122
|
});
|
|
123
|
-
describe
|
|
123
|
+
describe('added/deleted behavior', () => {
|
|
124
124
|
it('classifies explicit deletion', async () => {
|
|
125
125
|
const result = await compareItemLists(createClient(), ['Delete Me Item'], [], 'Delete Me Item was intentionally removed and has no replacement.');
|
|
126
126
|
expect(result.removed).toEqual(['Delete Me Item']);
|
|
@@ -136,7 +136,7 @@ describe.concurrent('compareItemLists (live API)', () => {
|
|
|
136
136
|
expect(result.unchanged).toEqual([]);
|
|
137
137
|
}, 180000);
|
|
138
138
|
});
|
|
139
|
-
describe
|
|
139
|
+
describe('mixed outcomes', () => {
|
|
140
140
|
it('handles unchanged + renamed + removed + added together', async () => {
|
|
141
141
|
const result = await compareItemLists(createClient(), ['Shared Constant Item', 'Legacy Rename Target', 'Delete Candidate'], ['shared constant item', 'Modern Rename Target', 'Add Candidate'], 'Legacy Rename Target was renamed to Modern Rename Target. ' +
|
|
142
142
|
'Delete Candidate was removed. ' +
|
|
@@ -148,7 +148,7 @@ describe.concurrent('compareItemLists (live API)', () => {
|
|
|
148
148
|
expect(result.added).toEqual(['Add Candidate']);
|
|
149
149
|
}, 180000);
|
|
150
150
|
});
|
|
151
|
-
describe
|
|
151
|
+
describe('callback reporting behavior', () => {
|
|
152
152
|
it('emits balanced start/finish events with correct source-list flags', async () => {
|
|
153
153
|
const { events, callback } = collectEvents();
|
|
154
154
|
await compareItemLists(createClient(), ['Before Removed A', 'Before Removed B'], ['After Added A'], 'Before Removed A and Before Removed B were removed. ' +
|
|
@@ -192,7 +192,7 @@ describe.concurrent('compareItemLists (live API)', () => {
|
|
|
192
192
|
expect(finishEventsWithErrors.some((event) => (event.error || '').includes('LLM processing failed'))).toBe(true);
|
|
193
193
|
}, 180000);
|
|
194
194
|
});
|
|
195
|
-
describe
|
|
195
|
+
describe('bulk list scenarios', () => {
|
|
196
196
|
it('handles a larger mixed migration with multiple renames/additions/deletions', async () => {
|
|
197
197
|
const beforeItems = [
|
|
198
198
|
'Shared Stable A',
|
|
@@ -269,7 +269,7 @@ describe.concurrent('compareItemLists (live API)', () => {
|
|
|
269
269
|
assertProcessedCountersAreSequential(events);
|
|
270
270
|
}, 240000);
|
|
271
271
|
});
|
|
272
|
-
describe
|
|
272
|
+
describe('inference without explicit mapping instructions', () => {
|
|
273
273
|
it('infers removed string items when after list omits them', async () => {
|
|
274
274
|
const result = await compareItemLists(createClient(), [
|
|
275
275
|
'Invoice Number',
|
|
@@ -299,10 +299,10 @@ describe.concurrent('compareItemLists (live API)', () => {
|
|
|
299
299
|
'Subtotal',
|
|
300
300
|
'Order Date',
|
|
301
301
|
'Shipping Method',
|
|
302
|
-
'
|
|
302
|
+
'Delivery Address',
|
|
303
303
|
]);
|
|
304
304
|
expect(result.removed).toEqual([]);
|
|
305
|
-
expect(result.added).toEqual(['
|
|
305
|
+
expect(result.added?.sort()).toEqual(['Delivery Address', 'Shipping Method']);
|
|
306
306
|
expect(result.renamed).toEqual({});
|
|
307
307
|
expect(result.unchanged).toEqual([
|
|
308
308
|
'Customer Name',
|
|
@@ -8,7 +8,7 @@ if (!OPENAI_API_KEY) {
|
|
|
8
8
|
const createClient = () => new OpenAI({
|
|
9
9
|
apiKey: OPENAI_API_KEY,
|
|
10
10
|
});
|
|
11
|
-
describe
|
|
11
|
+
describe('jsonSurgery (live API)', () => {
|
|
12
12
|
describe('atomic operations', () => {
|
|
13
13
|
it('applies a simple scalar update without mutating original input', async () => {
|
|
14
14
|
const original = {
|
package/package.json
CHANGED
|
@@ -1,53 +1,57 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "mdi-llmkit",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Utilities for managing multi-shot conversations and structured data handling in LLM applications",
|
|
5
|
-
"author": "Mikhail Voloshin",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
"types": "./dist/src/
|
|
18
|
-
"import": "./dist/src/
|
|
19
|
-
},
|
|
20
|
-
"./
|
|
21
|
-
"types": "./dist/src/
|
|
22
|
-
"import": "./dist/src/
|
|
23
|
-
},
|
|
24
|
-
"./
|
|
25
|
-
"types": "./dist/src/
|
|
26
|
-
"import": "./dist/src/
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "mdi-llmkit",
|
|
3
|
+
"version": "1.0.6",
|
|
4
|
+
"description": "Utilities for managing multi-shot conversations and structured data handling in LLM applications",
|
|
5
|
+
"author": "Mikhail Voloshin",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/Mighty-Data-Inc/mdi-llmkit"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"sideEffects": false,
|
|
13
|
+
"main": "dist/src/index.js",
|
|
14
|
+
"types": "dist/src/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/src/index.d.ts",
|
|
18
|
+
"import": "./dist/src/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./gptApi": {
|
|
21
|
+
"types": "./dist/src/gptApi/index.d.ts",
|
|
22
|
+
"import": "./dist/src/gptApi/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./jsonSurgery": {
|
|
25
|
+
"types": "./dist/src/jsonSurgery/jsonSurgery.d.ts",
|
|
26
|
+
"import": "./dist/src/jsonSurgery/jsonSurgery.js"
|
|
27
|
+
},
|
|
28
|
+
"./comparison": {
|
|
29
|
+
"types": "./dist/src/comparison/index.d.ts",
|
|
30
|
+
"import": "./dist/src/comparison/index.js"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist"
|
|
35
|
+
],
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsc -p tsconfig.json",
|
|
38
|
+
"test": "vitest run",
|
|
39
|
+
"prepublishOnly": "npm run test && npm run build"
|
|
40
|
+
},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"llm",
|
|
43
|
+
"openai",
|
|
44
|
+
"responses-api",
|
|
45
|
+
"structured-outputs",
|
|
46
|
+
"chat"
|
|
47
|
+
],
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"dotenv": "^17.3.1",
|
|
50
|
+
"openai": "^6.2.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/node": "^22.18.10",
|
|
54
|
+
"typescript": "^5.9.2",
|
|
55
|
+
"vitest": "^3.2.4"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
export declare const GPT_MODEL_CHEAP = "gpt-4.1-nano";
|
|
2
|
-
export declare const GPT_MODEL_SMART = "gpt-4.1";
|
|
3
|
-
export interface OpenAIClientLike {
|
|
4
|
-
responses: {
|
|
5
|
-
create: (args: {
|
|
6
|
-
model: string;
|
|
7
|
-
input: unknown[];
|
|
8
|
-
text?: Record<string, unknown>;
|
|
9
|
-
}) => Promise<any> | any;
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
|
-
export interface SystemMessage {
|
|
13
|
-
role: "system";
|
|
14
|
-
content: string;
|
|
15
|
-
}
|
|
16
|
-
export interface GptSubmitOptions {
|
|
17
|
-
model?: string;
|
|
18
|
-
jsonResponse?: boolean | Record<string, unknown> | string;
|
|
19
|
-
systemAnnouncementMessage?: string;
|
|
20
|
-
retryLimit?: number;
|
|
21
|
-
retryBackoffTimeSeconds?: number;
|
|
22
|
-
warningCallback?: (message: string) => void;
|
|
23
|
-
}
|
|
24
|
-
export declare function currentDatetimeSystemMessage(): SystemMessage;
|
|
25
|
-
export declare function gptSubmit(messages: unknown[], openaiClient: OpenAIClientLike, options?: GptSubmitOptions): Promise<string | Record<string, unknown> | unknown[] | number | boolean | null>;
|
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
export const GPT_MODEL_CHEAP = "gpt-4.1-nano";
|
|
2
|
-
export const GPT_MODEL_SMART = "gpt-4.1";
|
|
3
|
-
const GPT_RETRY_LIMIT_DEFAULT = 5;
|
|
4
|
-
const GPT_RETRY_BACKOFF_TIME_SECONDS_DEFAULT = 30;
|
|
5
|
-
function isRecord(value) {
|
|
6
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7
|
-
}
|
|
8
|
-
function sleep(ms) {
|
|
9
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
10
|
-
}
|
|
11
|
-
function parseFirstJsonValue(input) {
|
|
12
|
-
const text = input.trimStart();
|
|
13
|
-
if (!text) {
|
|
14
|
-
throw new SyntaxError("Unexpected end of JSON input");
|
|
15
|
-
}
|
|
16
|
-
const first = text[0];
|
|
17
|
-
if (first === "{" || first === "[") {
|
|
18
|
-
const closing = first === "{" ? "}" : "]";
|
|
19
|
-
let depth = 0;
|
|
20
|
-
let inString = false;
|
|
21
|
-
let escaped = false;
|
|
22
|
-
for (let index = 0; index < text.length; index += 1) {
|
|
23
|
-
const char = text[index];
|
|
24
|
-
if (inString) {
|
|
25
|
-
if (escaped) {
|
|
26
|
-
escaped = false;
|
|
27
|
-
continue;
|
|
28
|
-
}
|
|
29
|
-
if (char === "\\") {
|
|
30
|
-
escaped = true;
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
|
-
if (char === '"') {
|
|
34
|
-
inString = false;
|
|
35
|
-
}
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
if (char === '"') {
|
|
39
|
-
inString = true;
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
if (char === first) {
|
|
43
|
-
depth += 1;
|
|
44
|
-
}
|
|
45
|
-
else if (char === closing) {
|
|
46
|
-
depth -= 1;
|
|
47
|
-
if (depth === 0) {
|
|
48
|
-
return JSON.parse(text.slice(0, index + 1));
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
throw new SyntaxError("Unexpected end of JSON input");
|
|
53
|
-
}
|
|
54
|
-
if (first === '"') {
|
|
55
|
-
let escaped = false;
|
|
56
|
-
for (let index = 1; index < text.length; index += 1) {
|
|
57
|
-
const char = text[index];
|
|
58
|
-
if (escaped) {
|
|
59
|
-
escaped = false;
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
if (char === "\\") {
|
|
63
|
-
escaped = true;
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
if (char === '"') {
|
|
67
|
-
return JSON.parse(text.slice(0, index + 1));
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
throw new SyntaxError("Unexpected end of JSON input");
|
|
71
|
-
}
|
|
72
|
-
if (text.startsWith("true")) {
|
|
73
|
-
return true;
|
|
74
|
-
}
|
|
75
|
-
if (text.startsWith("false")) {
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
if (text.startsWith("null")) {
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
const numberMatch = text.match(/^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/);
|
|
82
|
-
if (numberMatch) {
|
|
83
|
-
return JSON.parse(numberMatch[0]);
|
|
84
|
-
}
|
|
85
|
-
throw new SyntaxError("Unexpected token in JSON input");
|
|
86
|
-
}
|
|
87
|
-
function isRetryableOpenAIError(error) {
|
|
88
|
-
if (!(error instanceof Error)) {
|
|
89
|
-
return false;
|
|
90
|
-
}
|
|
91
|
-
const name = error.name || "";
|
|
92
|
-
return name.includes("OpenAI") || name.includes("APIError");
|
|
93
|
-
}
|
|
94
|
-
export function currentDatetimeSystemMessage() {
|
|
95
|
-
const now = new Date();
|
|
96
|
-
const pad = (value) => value.toString().padStart(2, "0");
|
|
97
|
-
const timestamp = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ` +
|
|
98
|
-
`${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
|
|
99
|
-
return {
|
|
100
|
-
role: "system",
|
|
101
|
-
content: `!DATETIME: The current date and time is ${timestamp}`,
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
export async function gptSubmit(messages, openaiClient, options = {}) {
|
|
105
|
-
const model = options.model || GPT_MODEL_SMART;
|
|
106
|
-
const retryLimit = options.retryLimit ?? GPT_RETRY_LIMIT_DEFAULT;
|
|
107
|
-
const retryBackoffTimeSeconds = options.retryBackoffTimeSeconds ?? GPT_RETRY_BACKOFF_TIME_SECONDS_DEFAULT;
|
|
108
|
-
let failedError = null;
|
|
109
|
-
let openaiTextParam;
|
|
110
|
-
if (options.jsonResponse) {
|
|
111
|
-
if (typeof options.jsonResponse === "boolean") {
|
|
112
|
-
openaiTextParam = { format: { type: "json_object" } };
|
|
113
|
-
}
|
|
114
|
-
else if (typeof options.jsonResponse === "string") {
|
|
115
|
-
openaiTextParam = JSON.parse(options.jsonResponse);
|
|
116
|
-
}
|
|
117
|
-
else if (isRecord(options.jsonResponse)) {
|
|
118
|
-
openaiTextParam = JSON.parse(JSON.stringify(options.jsonResponse));
|
|
119
|
-
const format = openaiTextParam.format;
|
|
120
|
-
if (isRecord(format) && typeof format.description === "string") {
|
|
121
|
-
format.description =
|
|
122
|
-
`${format.description}\n\nABSOLUTELY NO UNICODE ALLOWED. ` +
|
|
123
|
-
`Only use typeable keyboard characters. Do not try to circumvent this rule ` +
|
|
124
|
-
`with escape sequences, backslashes, or other tricks. Use double dashes (--), ` +
|
|
125
|
-
`straight quotes (") and single quotes (') instead of em-dashes, en-dashes, ` +
|
|
126
|
-
`and curly versions.`.trim();
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
const filteredMessages = messages.filter((message) => {
|
|
131
|
-
if (!isRecord(message)) {
|
|
132
|
-
return true;
|
|
133
|
-
}
|
|
134
|
-
const role = message.role;
|
|
135
|
-
const content = message.content;
|
|
136
|
-
return !(role === "system" &&
|
|
137
|
-
typeof content === "string" &&
|
|
138
|
-
content.startsWith("!DATETIME:"));
|
|
139
|
-
});
|
|
140
|
-
let preparedMessages = [currentDatetimeSystemMessage(), ...filteredMessages];
|
|
141
|
-
if (options.systemAnnouncementMessage && options.systemAnnouncementMessage.trim()) {
|
|
142
|
-
preparedMessages = [
|
|
143
|
-
{ role: "system", content: options.systemAnnouncementMessage.trim() },
|
|
144
|
-
...preparedMessages,
|
|
145
|
-
];
|
|
146
|
-
}
|
|
147
|
-
for (let index = 0; index < retryLimit; index += 1) {
|
|
148
|
-
let llmReply = "";
|
|
149
|
-
try {
|
|
150
|
-
const payload = {
|
|
151
|
-
model,
|
|
152
|
-
input: preparedMessages,
|
|
153
|
-
};
|
|
154
|
-
if (openaiTextParam) {
|
|
155
|
-
payload.text = openaiTextParam;
|
|
156
|
-
}
|
|
157
|
-
const llmResponse = await openaiClient.responses.create(payload);
|
|
158
|
-
if (llmResponse.error && options.warningCallback) {
|
|
159
|
-
options.warningCallback(`ERROR: OpenAI API returned an error: ${llmResponse.error}`);
|
|
160
|
-
}
|
|
161
|
-
if (llmResponse.incomplete_details && options.warningCallback) {
|
|
162
|
-
options.warningCallback(`ERROR: OpenAI API returned incomplete details: ${llmResponse.incomplete_details}`);
|
|
163
|
-
}
|
|
164
|
-
llmReply = llmResponse.output_text.trim();
|
|
165
|
-
if (!options.jsonResponse) {
|
|
166
|
-
return `${llmReply}`;
|
|
167
|
-
}
|
|
168
|
-
return parseFirstJsonValue(llmReply);
|
|
169
|
-
}
|
|
170
|
-
catch (error) {
|
|
171
|
-
if (error instanceof SyntaxError) {
|
|
172
|
-
failedError = error;
|
|
173
|
-
if (options.warningCallback) {
|
|
174
|
-
options.warningCallback(`JSON decode error:\n\n${error}.\n\nRaw text of LLM Reply:\n${llmReply}\n\nRetrying (attempt ${index + 1} of ${retryLimit}) immediately...`);
|
|
175
|
-
}
|
|
176
|
-
continue;
|
|
177
|
-
}
|
|
178
|
-
if (isRetryableOpenAIError(error)) {
|
|
179
|
-
failedError = error;
|
|
180
|
-
if (options.warningCallback) {
|
|
181
|
-
options.warningCallback(`OpenAI API error:\n\n${error}.\n\nRetrying (attempt ${index + 1} of ${retryLimit}) in ${retryBackoffTimeSeconds} seconds...`);
|
|
182
|
-
}
|
|
183
|
-
await sleep(retryBackoffTimeSeconds * 1000);
|
|
184
|
-
continue;
|
|
185
|
-
}
|
|
186
|
-
throw error;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
if (failedError) {
|
|
190
|
-
throw failedError;
|
|
191
|
-
}
|
|
192
|
-
throw new Error("Unknown error occurred in gptSubmit");
|
|
193
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { type OpenAIClientLike } from "./functions.js";
|
|
2
|
-
export interface ConversationMessage {
|
|
3
|
-
role: string;
|
|
4
|
-
content: string;
|
|
5
|
-
}
|
|
6
|
-
export interface GptConversationOptions {
|
|
7
|
-
openaiClient?: OpenAIClientLike;
|
|
8
|
-
model?: string;
|
|
9
|
-
}
|
|
10
|
-
export interface SubmitOptions {
|
|
11
|
-
model?: string;
|
|
12
|
-
jsonResponse?: boolean | Record<string, unknown> | string;
|
|
13
|
-
}
|
|
14
|
-
export declare class GptConversation extends Array<ConversationMessage> {
|
|
15
|
-
#private;
|
|
16
|
-
static get [Symbol.species](): ArrayConstructor;
|
|
17
|
-
get openaiClient(): OpenAIClientLike | undefined;
|
|
18
|
-
set openaiClient(value: OpenAIClientLike | undefined);
|
|
19
|
-
get model(): string | undefined;
|
|
20
|
-
set model(value: string | undefined);
|
|
21
|
-
get lastReply(): unknown;
|
|
22
|
-
set lastReply(value: unknown);
|
|
23
|
-
constructor(messages?: ConversationMessage[], options?: GptConversationOptions);
|
|
24
|
-
assignMessages(messages?: ConversationMessage[]): this;
|
|
25
|
-
clone(): GptConversation;
|
|
26
|
-
submit(message?: string | Record<string, unknown>, role?: string | null, options?: SubmitOptions): Promise<unknown>;
|
|
27
|
-
addMessage(role: string, content: unknown): this;
|
|
28
|
-
addUserMessage(content: unknown): this;
|
|
29
|
-
addAssistantMessage(content: unknown): this;
|
|
30
|
-
addSystemMessage(content: unknown): this;
|
|
31
|
-
addDeveloperMessage(content: unknown): this;
|
|
32
|
-
submitMessage(role: string, content: unknown): Promise<unknown>;
|
|
33
|
-
submitUserMessage(content: unknown): Promise<unknown>;
|
|
34
|
-
submitAssistantMessage(content: unknown): Promise<unknown>;
|
|
35
|
-
submitSystemMessage(content: unknown): Promise<unknown>;
|
|
36
|
-
submitDeveloperMessage(content: unknown): Promise<unknown>;
|
|
37
|
-
getLastMessage(): ConversationMessage | null;
|
|
38
|
-
getMessagesByRole(role: string): ConversationMessage[];
|
|
39
|
-
getLastReplyStr(): string;
|
|
40
|
-
getLastReplyDict(): Record<string, unknown>;
|
|
41
|
-
getLastReplyDictField(fieldName: string, defaultValue?: unknown): unknown;
|
|
42
|
-
toDictList(): ConversationMessage[];
|
|
43
|
-
}
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { GPT_MODEL_SMART, gptSubmit } from "./functions.js";
|
|
2
|
-
function isRecord(value) {
|
|
3
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4
|
-
}
|
|
5
|
-
export class GptConversation extends Array {
|
|
6
|
-
static get [Symbol.species]() {
|
|
7
|
-
return Array;
|
|
8
|
-
}
|
|
9
|
-
#openaiClient;
|
|
10
|
-
#model;
|
|
11
|
-
#lastReply = null;
|
|
12
|
-
get openaiClient() {
|
|
13
|
-
return this.#openaiClient;
|
|
14
|
-
}
|
|
15
|
-
set openaiClient(value) {
|
|
16
|
-
this.#openaiClient = value;
|
|
17
|
-
}
|
|
18
|
-
get model() {
|
|
19
|
-
return this.#model;
|
|
20
|
-
}
|
|
21
|
-
set model(value) {
|
|
22
|
-
this.#model = value;
|
|
23
|
-
}
|
|
24
|
-
get lastReply() {
|
|
25
|
-
return this.#lastReply;
|
|
26
|
-
}
|
|
27
|
-
set lastReply(value) {
|
|
28
|
-
this.#lastReply = value;
|
|
29
|
-
}
|
|
30
|
-
constructor(messages = [], options = {}) {
|
|
31
|
-
super(...messages);
|
|
32
|
-
this.#openaiClient = options.openaiClient;
|
|
33
|
-
this.#model = options.model;
|
|
34
|
-
}
|
|
35
|
-
assignMessages(messages) {
|
|
36
|
-
this.length = 0;
|
|
37
|
-
if (messages?.length) {
|
|
38
|
-
this.push(...messages);
|
|
39
|
-
}
|
|
40
|
-
return this;
|
|
41
|
-
}
|
|
42
|
-
clone() {
|
|
43
|
-
return new GptConversation(JSON.parse(JSON.stringify([...this])), {
|
|
44
|
-
openaiClient: this.openaiClient,
|
|
45
|
-
model: this.model,
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
async submit(message, role = "user", options = {}) {
|
|
49
|
-
if (!this.openaiClient) {
|
|
50
|
-
throw new Error("OpenAI client is not set. Please provide an OpenAI client.");
|
|
51
|
-
}
|
|
52
|
-
const model = options.model || this.model || GPT_MODEL_SMART;
|
|
53
|
-
let jsonResponse = options.jsonResponse;
|
|
54
|
-
if (message) {
|
|
55
|
-
if (isRecord(message)) {
|
|
56
|
-
if (!jsonResponse && "format" in message) {
|
|
57
|
-
jsonResponse = message;
|
|
58
|
-
}
|
|
59
|
-
if (!role && typeof message.role === "string") {
|
|
60
|
-
role = message.role;
|
|
61
|
-
}
|
|
62
|
-
if ("content" in message) {
|
|
63
|
-
message = String(message.content ?? "");
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
this.addMessage(role || "user", message);
|
|
67
|
-
}
|
|
68
|
-
const llmReply = await gptSubmit(this.toDictList(), this.openaiClient, {
|
|
69
|
-
jsonResponse,
|
|
70
|
-
model,
|
|
71
|
-
});
|
|
72
|
-
this.addAssistantMessage(llmReply);
|
|
73
|
-
this.lastReply = llmReply;
|
|
74
|
-
return llmReply;
|
|
75
|
-
}
|
|
76
|
-
addMessage(role, content) {
|
|
77
|
-
let normalizedContent;
|
|
78
|
-
if (typeof content === "string") {
|
|
79
|
-
normalizedContent = content;
|
|
80
|
-
}
|
|
81
|
-
else if (isRecord(content)) {
|
|
82
|
-
normalizedContent = JSON.stringify(content, null, 2);
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
normalizedContent = String(content);
|
|
86
|
-
}
|
|
87
|
-
this.push({ role, content: normalizedContent });
|
|
88
|
-
return this;
|
|
89
|
-
}
|
|
90
|
-
addUserMessage(content) {
|
|
91
|
-
return this.addMessage("user", content);
|
|
92
|
-
}
|
|
93
|
-
addAssistantMessage(content) {
|
|
94
|
-
return this.addMessage("assistant", content);
|
|
95
|
-
}
|
|
96
|
-
addSystemMessage(content) {
|
|
97
|
-
return this.addMessage("system", content);
|
|
98
|
-
}
|
|
99
|
-
addDeveloperMessage(content) {
|
|
100
|
-
return this.addMessage("developer", content);
|
|
101
|
-
}
|
|
102
|
-
async submitMessage(role, content) {
|
|
103
|
-
this.addMessage(role, content);
|
|
104
|
-
return this.submit();
|
|
105
|
-
}
|
|
106
|
-
async submitUserMessage(content) {
|
|
107
|
-
this.addUserMessage(content);
|
|
108
|
-
return this.submit();
|
|
109
|
-
}
|
|
110
|
-
async submitAssistantMessage(content) {
|
|
111
|
-
this.addAssistantMessage(content);
|
|
112
|
-
return this.submit();
|
|
113
|
-
}
|
|
114
|
-
async submitSystemMessage(content) {
|
|
115
|
-
this.addSystemMessage(content);
|
|
116
|
-
return this.submit();
|
|
117
|
-
}
|
|
118
|
-
async submitDeveloperMessage(content) {
|
|
119
|
-
this.addDeveloperMessage(content);
|
|
120
|
-
return this.submit();
|
|
121
|
-
}
|
|
122
|
-
getLastMessage() {
|
|
123
|
-
return this.length ? this[this.length - 1] : null;
|
|
124
|
-
}
|
|
125
|
-
getMessagesByRole(role) {
|
|
126
|
-
return this.filter((message) => message.role === role);
|
|
127
|
-
}
|
|
128
|
-
getLastReplyStr() {
|
|
129
|
-
return typeof this.lastReply === "string" ? this.lastReply : "";
|
|
130
|
-
}
|
|
131
|
-
getLastReplyDict() {
|
|
132
|
-
if (!isRecord(this.lastReply)) {
|
|
133
|
-
return {};
|
|
134
|
-
}
|
|
135
|
-
return JSON.parse(JSON.stringify(this.lastReply));
|
|
136
|
-
}
|
|
137
|
-
getLastReplyDictField(fieldName, defaultValue = null) {
|
|
138
|
-
if (!isRecord(this.lastReply)) {
|
|
139
|
-
return null;
|
|
140
|
-
}
|
|
141
|
-
return this.lastReply[fieldName] ?? defaultValue;
|
|
142
|
-
}
|
|
143
|
-
toDictList() {
|
|
144
|
-
return [...this];
|
|
145
|
-
}
|
|
146
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
export declare const JSON_INTEGER: unique symbol;
|
|
2
|
-
export declare const JSON_NUMBER: unique symbol;
|
|
3
|
-
export declare const JSON_STRING: StringConstructor;
|
|
4
|
-
export declare const JSON_BOOLEAN: BooleanConstructor;
|
|
5
|
-
export interface JSONSchemaFormatOptions {
|
|
6
|
-
name?: string;
|
|
7
|
-
description?: string;
|
|
8
|
-
}
|
|
9
|
-
export interface JSONSchemaFormatResult {
|
|
10
|
-
format: {
|
|
11
|
-
type: "json_schema";
|
|
12
|
-
strict: true;
|
|
13
|
-
name?: string;
|
|
14
|
-
description?: string;
|
|
15
|
-
schema: Record<string, unknown>;
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
export declare function JSONSchemaFormat(schema: unknown, options?: JSONSchemaFormatOptions): JSONSchemaFormatResult;
|