mdi-llmkit 0.1.0 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +190 -108
  2. package/dist/src/comparison/compareLists.d.ts +97 -0
  3. package/dist/src/comparison/compareLists.js +375 -0
  4. package/dist/src/comparison/index.d.ts +1 -0
  5. package/dist/src/comparison/index.js +1 -0
  6. package/dist/{gpt_api → src/gptApi}/functions.d.ts +1 -5
  7. package/dist/{gpt_api → src/gptApi}/functions.js +25 -64
  8. package/dist/{gpt_api/gpt_conversation.d.ts → src/gptApi/gptConversation.d.ts} +1 -1
  9. package/dist/{gpt_api/gpt_conversation.js → src/gptApi/gptConversation.js} +15 -15
  10. package/dist/src/gptApi/index.d.ts +3 -0
  11. package/dist/src/gptApi/index.js +3 -0
  12. package/dist/{gpt_api/json_schema_format.d.ts → src/gptApi/jsonSchemaFormat.d.ts} +3 -7
  13. package/dist/{gpt_api/json_schema_format.js → src/gptApi/jsonSchemaFormat.js} +51 -48
  14. package/dist/src/index.d.ts +3 -0
  15. package/dist/src/index.js +3 -0
  16. package/dist/src/jsonSurgery/jsonSurgery.d.ts +81 -0
  17. package/dist/src/jsonSurgery/jsonSurgery.js +776 -0
  18. package/dist/src/jsonSurgery/placemarkedJSON.d.ts +57 -0
  19. package/dist/src/jsonSurgery/placemarkedJSON.js +151 -0
  20. package/dist/tests/comparison/compareLists.test.d.ts +1 -0
  21. package/dist/tests/comparison/compareLists.test.js +434 -0
  22. package/dist/tests/gptApi/gptConversation.test.d.ts +1 -0
  23. package/dist/tests/gptApi/gptConversation.test.js +157 -0
  24. package/dist/tests/gptApi/gptSubmit.test.d.ts +1 -0
  25. package/dist/tests/gptApi/gptSubmit.test.js +161 -0
  26. package/dist/tests/gptApi/jsonSchemaFormat.test.d.ts +1 -0
  27. package/dist/tests/gptApi/jsonSchemaFormat.test.js +372 -0
  28. package/dist/tests/jsonSurgery/jsonSurgery.test.d.ts +1 -0
  29. package/dist/tests/jsonSurgery/jsonSurgery.test.js +729 -0
  30. package/dist/tests/jsonSurgery/placemarkedJSON.test.d.ts +1 -0
  31. package/dist/tests/jsonSurgery/placemarkedJSON.test.js +209 -0
  32. package/dist/tests/setupEnv.d.ts +1 -0
  33. package/dist/tests/setupEnv.js +4 -0
  34. package/dist/tests/subpathExports.test.d.ts +1 -0
  35. package/dist/tests/subpathExports.test.js +47 -0
  36. package/package.json +57 -40
  37. package/dist/index.d.ts +0 -3
  38. package/dist/index.js +0 -3
package/README.md CHANGED
@@ -1,108 +1,190 @@
1
- # mdi-llmkit (TypeScript)
2
-
3
- Utilities for managing LLM chat conversations and structured JSON responses with OpenAI's Responses API.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- npm install mdi-llmkit openai
9
- ```
10
-
11
- ## Quick Start
12
-
13
- ### `gptSubmit`
14
-
15
- ```ts
16
- import OpenAI from "openai";
17
- import { gptSubmit } from "mdi-llmkit";
18
-
19
- const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
20
-
21
- const reply = await gptSubmit(
22
- [{ role: "user", content: "Say hello." }],
23
- client,
24
- );
25
-
26
- console.log(reply);
27
- ```
28
-
29
- ### `GptConversation`
30
-
31
- ```ts
32
- import OpenAI from "openai";
33
- import { GptConversation } from "mdi-llmkit";
34
-
35
- const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
36
- const conversation = new GptConversation([], { openaiClient: client });
37
-
38
- const reply = await conversation.submitUserMessage("Give me three project name ideas.");
39
- console.log(reply);
40
- ```
41
-
42
- ### `JSONSchemaFormat`
43
-
44
- ```ts
45
- import { JSONSchemaFormat, JSON_INTEGER, gptSubmit } from "mdi-llmkit";
46
-
47
- const responseFormat = JSONSchemaFormat(
48
- {
49
- answer: "The final answer",
50
- confidence: ["Confidence score", [0, 100], []],
51
- rank: JSON_INTEGER,
52
- },
53
- {
54
- name: "answer_payload",
55
- description: "Structured answer payload",
56
- },
57
- );
58
-
59
- const result = await gptSubmit(
60
- [{ role: "user", content: "Return answer as structured JSON." }],
61
- client,
62
- { jsonResponse: responseFormat },
63
- );
64
- ```
65
-
66
- ## JSON Response Mode
67
-
68
- ```ts
69
- import OpenAI from "openai";
70
- import { gptSubmit } from "mdi-llmkit";
71
-
72
- const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
73
-
74
- const result = await gptSubmit(
75
- [{ role: "user", content: "Return JSON with keys a and b." }],
76
- client,
77
- { jsonResponse: true },
78
- );
79
-
80
- console.log(result);
81
- ```
82
-
83
- ## Notes
84
-
85
- - Current TypeScript parity slices include `gptSubmit`, `GptConversation`, and `JSONSchemaFormat`.
86
- - Integer schemas can be expressed with `JSON_INTEGER`; numeric (float-capable) schemas can use `JSON_NUMBER`.
87
-
88
- ## Migration from Python
89
-
90
- - Function naming: Python `gpt_submit(...)` maps to TypeScript `gptSubmit(...)`.
91
- - Argument style: Python keyword args map to a TypeScript options object.
92
- - Conversation submit methods: Python `submit_user_message(...)` maps to `submitUserMessage(...)`.
93
- - JSON schema DSL: Python tuple metadata uses TypeScript array metadata.
94
- - Python: `("Age", (0, 120), int)`
95
- - TypeScript: `["Age", [0, 120], JSON_INTEGER]`
96
- - JSON schema type markers in TypeScript:
97
- - `JSON_INTEGER` for integer-only values.
98
- - `JSON_NUMBER` for float-capable numeric values.
99
-
100
- ## CI and Release
101
-
102
- - CI workflow: `.github/workflows/typescript-ci.yml`
103
- - Runs on push to `main` and on pull requests when TypeScript package files change.
104
- - Executes `npm ci`, `npm test`, and `npm run build` in `packages/typescript-mdi-llmkit`.
105
- - Release workflow: `.github/workflows/typescript-release.yml`
106
- - Runs on tags matching `typescript-v*` (for example: `typescript-v0.1.0`).
107
- - Requires repository secret `NPM_TOKEN` with publish permission to npm.
108
- - Executes tests/build before `npm publish --access public --provenance`.
1
+ # mdi-llmkit (TypeScript)
2
+
3
+ Utilities for managing LLM chat conversations and structured JSON responses with OpenAI's Responses API.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install mdi-llmkit openai
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ### `gptSubmit`
14
+
15
+ ```ts
16
+ import OpenAI from 'openai';
17
+ import { gptSubmit } from 'mdi-llmkit';
18
+
19
+ const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
20
+
21
+ const reply = await gptSubmit(
22
+ [{ role: 'user', content: 'Say hello.' }],
23
+ client
24
+ );
25
+
26
+ console.log(reply);
27
+ ```
28
+
29
+ ### `GptConversation`
30
+
31
+ ```ts
32
+ import OpenAI from 'openai';
33
+ import { GptConversation } from 'mdi-llmkit';
34
+
35
+ const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
36
+ const conversation = new GptConversation([], { openaiClient: client });
37
+
38
+ const reply = await conversation.submitUserMessage(
39
+ 'Give me three project name ideas.'
40
+ );
41
+ console.log(reply);
42
+ ```
43
+
44
+ ### `JSONSchemaFormat`
45
+
46
+ ```ts
47
+ import { JSONSchemaFormat, JSON_INTEGER, gptSubmit } from 'mdi-llmkit';
48
+
49
+ const responseFormat = JSONSchemaFormat(
50
+ 'answer_payload',
51
+ {
52
+ answer: 'The final answer',
53
+ confidence: ['Confidence score', [0, 100], []],
54
+ rank: JSON_INTEGER,
55
+ },
56
+ 'Structured answer payload'
57
+ );
58
+
59
+ const result = await gptSubmit(
60
+ [{ role: 'user', content: 'Return answer as structured JSON.' }],
61
+ client,
62
+ { jsonResponse: responseFormat }
63
+ );
64
+ ```
65
+
66
+ ## `jsonSurgery`
67
+
68
+ `jsonSurgery` applies iterative, model-guided edits to a JSON-compatible object using
69
+ structured JSON-path operations (`assign`, `append`, `insert`, `delete`, `rename`).
70
+
71
+ ```ts
72
+ import { jsonSurgery } from 'mdi-llmkit/jsonSurgery';
73
+ ```
74
+
75
+ - It deep-copies the input object and returns the modified copy.
76
+ - It supports optional schema guidance and key-skipping for model-visible context.
77
+ - It supports validation/progress callbacks and soft iteration/time limits.
78
+
79
+ ## `compareItemLists` (comparison)
80
+
81
+ `compareItemLists` performs a semantic diff between a "before" list and an "after" list,
82
+ including LLM-assisted rename/add/remove decisions.
83
+
84
+ Types:
85
+
86
+ - `SemanticallyComparableListItem`
87
+ - `string`
88
+ - `{ name: string; description?: string }`
89
+ - `ItemComparisonResult`
90
+ - `Removed | Added | Renamed | Unchanged`
91
+ - `OnComparingItemCallback`
92
+ - `(item, isFromBeforeList, isStarting, result, newName, error, totalProcessedSoFar, totalLeftToProcess) => void`
93
+
94
+ Behavior notes:
95
+
96
+ - Item matching is name-based and case-insensitive.
97
+ - `description` provides extra model context but is not identity.
98
+ - Names are expected to be unique within each list (case-insensitive).
99
+ - Progress callback is fired at item start (`isStarting=true`) and finish (`isStarting=false`).
100
+
101
+ Example:
102
+
103
+ ```ts
104
+ import OpenAI from 'openai';
105
+ import {
106
+ compareItemLists,
107
+ ItemComparisonResult,
108
+ type OnComparingItemCallback,
109
+ } from 'mdi-llmkit/comparison';
110
+
111
+ const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
112
+
113
+ const onComparingItem: OnComparingItemCallback = (
114
+ item,
115
+ isFromBeforeList,
116
+ isStarting,
117
+ result,
118
+ newName,
119
+ error,
120
+ processed,
121
+ left
122
+ ) => {
123
+ if (error) {
124
+ console.warn('Comparison warning:', error);
125
+ }
126
+ if (!isStarting && result === ItemComparisonResult.Renamed) {
127
+ console.log('Renamed:', item, '->', newName);
128
+ }
129
+ console.log({ isFromBeforeList, isStarting, result, processed, left });
130
+ };
131
+
132
+ const comparison = await compareItemLists(
133
+ client,
134
+ [{ name: 'Widget A', description: 'Legacy widget' }, 'Widget B'],
135
+ [
136
+ { name: 'Widget Alpha', description: 'Migrated name for Widget A' },
137
+ 'Widget B',
138
+ ],
139
+ 'Widgets migrated from legacy catalog to new naming standards.',
140
+ onComparingItem
141
+ );
142
+
143
+ console.log(comparison);
144
+ ```
145
+
146
+ ## JSON Response Mode
147
+
148
+ ```ts
149
+ import OpenAI from 'openai';
150
+ import { gptSubmit } from 'mdi-llmkit';
151
+
152
+ const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
153
+
154
+ const result = await gptSubmit(
155
+ [{ role: 'user', content: 'Return JSON with keys a and b.' }],
156
+ client,
157
+ { jsonResponse: true }
158
+ );
159
+
160
+ console.log(result);
161
+ ```
162
+
163
+ ## Notes
164
+
165
+ - Current TypeScript parity slices include `gptSubmit`, `GptConversation`, and `JSONSchemaFormat`.
166
+ - You can import GPT API symbols via subpath imports, e.g. `import { GptConversation } from "mdi-llmkit/gptApi"`.
167
+ - Comparison symbols are available via `mdi-llmkit/comparison`.
168
+ - Integer schemas can be expressed with `JSON_INTEGER`; numeric (float-capable) schemas can use `JSON_NUMBER`.
169
+
170
+ ## Migration from Python
171
+
172
+ - Function naming: Python `gpt_submit(...)` maps to TypeScript `gptSubmit(...)`.
173
+ - Argument style: Python keyword args map to a TypeScript options object.
174
+ - Conversation submit methods: Python `submit_user_message(...)` maps to `submitUserMessage(...)`.
175
+ - JSON schema DSL: Python tuple metadata uses TypeScript array metadata.
176
+ - Python: `("Age", (0, 120), int)`
177
+ - TypeScript: `["Age", [0, 120], JSON_INTEGER]`
178
+ - JSON schema type markers in TypeScript:
179
+ - `JSON_INTEGER` for integer-only values.
180
+ - `JSON_NUMBER` for float-capable numeric values.
181
+
182
+ ## CI and Release
183
+
184
+ - CI workflow: `.github/workflows/typescript-ci.yml`
185
+ - Runs on push to `main` and on pull requests when TypeScript package files change.
186
+ - Executes `npm ci`, `npm test`, and `npm run build` in `packages/typescript-mdi-llmkit`.
187
+ - Release workflow: `.github/workflows/typescript-release.yml`
188
+ - Runs on tags matching `typescript-v*` (for example: `typescript-v1.0.1`).
189
+ - Requires repository secret `NPM_TOKEN` with publish permission to npm.
190
+ - Executes tests/build before `npm publish --access public --provenance`.
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Utilities for semantic comparison of two item lists using deterministic pre-processing
3
+ * plus LLM-assisted decisions for ambiguous cases.
4
+ *
5
+ * High-level flow:
6
+ * 1) Normalize/sort items and remove obvious case-insensitive exact matches.
7
+ * 2) Ask the LLM to classify remaining "before" items as removed or renamed.
8
+ * 3) Ask the LLM whether remaining "after" items should be considered added.
9
+ * 4) Return `removed`, `added`, `renamed`, and `unchanged` buckets.
10
+ *
11
+ * Key assumptions:
12
+ * - Item names are unique within each input list (case-insensitive).
13
+ * - Comparison is name-based (`string` value or object `name` field).
14
+ * - Optional object `description` is context-only and does not affect identity.
15
+ *
16
+ * Progress reporting:
17
+ * - `OnComparingItemCallback` can be provided to receive start/finish events for each item,
18
+ * including source list, result classification, optional rename target, and running counts.
19
+ */
20
+ import { OpenAI } from 'openai';
21
+ /**
22
+ * Item shape accepted by `compareItemLists` for semantic comparison.
23
+ *
24
+ * - A raw string is treated as the item's comparable name.
25
+ * - An object uses `name` as the comparable value and may include optional
26
+ * `description` to provide additional LLM context.
27
+ */
28
+ export type SemanticallyComparableListItem = string | {
29
+ name: string;
30
+ description?: string;
31
+ };
32
+ /**
33
+ * Final classification of an item during comparison.
34
+ */
35
+ export declare enum ItemComparisonResult {
36
+ /** Item existed in "before" and is considered deleted in "after". */
37
+ Removed = "removed",
38
+ /** Item exists in "after" and is considered newly introduced. */
39
+ Added = "added",
40
+ /** Item from "before" was matched to a different name in "after". */
41
+ Renamed = "renamed",
42
+ /** Item is treated as unchanged or unresolved for downstream purposes. */
43
+ Unchanged = "unchanged"
44
+ }
45
+ /**
46
+ * Progress callback for per-item comparison lifecycle.
47
+ *
48
+ * @param item The concrete item currently being evaluated.
49
+ * @param isFromBeforeList `true` when the item comes from `listBefore`, `false` when
50
+ * it comes from `listAfter`.
51
+ * @param isStarting `true` when evaluation for this item begins, `false` when that
52
+ * evaluation completes.
53
+ * @param result Current/final classification for this callback event. For start events,
54
+ * this is a provisional value; for finish events, it is final for that item.
55
+ * @param newName The matched new name when `result` is `Renamed`; otherwise `undefined`.
56
+ * @param error Optional warning/error message for this event; `undefined` when none.
57
+ * @param totalProcessedSoFar Number of items fully processed so far.
58
+ * @param totalLeftToProcess Number of items remaining after this event.
59
+ */
60
+ export type OnComparingItemCallback = (item: SemanticallyComparableListItem, isFromBeforeList: boolean, isStarting: boolean, result: ItemComparisonResult, newName: string | undefined, error: string | undefined, totalProcessedSoFar: number, totalLeftToProcess: number) => void;
61
+ /**
62
+ * Result of comparing two lists of strings.
63
+ */
64
+ export interface StringListComparison {
65
+ removed: string[];
66
+ added: string[];
67
+ renamed: Record<string, string>;
68
+ unchanged: string[];
69
+ }
70
+ /**
71
+ * Compares two lists of strings and identifies differences, including potential renames.
72
+ * The lists presumably use strings. However, in situations where the AI might benefit from
73
+ * additional context, the lists may contain objects with `name` and optional `description`
74
+ * properties; in these situations, it's the `name` property that is compared.
75
+ * The comparison is case insensitive.
76
+ *
77
+ * IMPORTANT: Item names are expected to be unique within each input list (case-insensitive).
78
+ * Duplicate names in either list are not supported and may produce incorrect results.
79
+ * @param before - The list of strings/items before the changes.
80
+ * @param after - The list of strings/items after the changes.
81
+ * @param explanation Optional explanation that provides context for the comparison, e.g.
82
+ * a description of the items or the nature of the changes.
83
+ * @param onComparingItem Optional callback invoked at the start and end of each item
84
+ * evaluation. It receives the current item, whether it is from the "before" list,
85
+ * whether processing is starting (`true`) or finishing (`false`), the
86
+ * current/final classification, renamed target (if applicable), and
87
+ * optional warning/error message, and processed/remaining item counts.
88
+ * `totalProcessedSoFar` increases only when an item
89
+ * finishes; `totalLeftToProcess` is the number of items not yet finished.
90
+ * @returns An object containing removed, added, renamed, and unchanged strings
91
+ */
92
+ export declare const compareItemLists: (openaiClient: OpenAI, listBefore: SemanticallyComparableListItem[], listAfter: SemanticallyComparableListItem[], explanation?: string, onComparingItem?: OnComparingItemCallback) => Promise<{
93
+ removed: string[];
94
+ added: string[];
95
+ renamed: Record<string, string>;
96
+ unchanged: string[];
97
+ }>;