@hardlydifficult/ai-msg 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -50
- package/dist/extractTyped.d.ts +14 -2
- package/dist/extractTyped.d.ts.map +1 -1
- package/dist/extractTyped.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/multimodal.d.ts +25 -0
- package/dist/multimodal.d.ts.map +1 -0
- package/dist/multimodal.js +28 -0
- package/dist/multimodal.js.map +1 -0
- package/package.json +1 -9
package/README.md
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# @hardlydifficult/ai-msg
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
LLM responses often wrap structured data in markdown code blocks, preamble text, or trailing commentary. This package reliably extracts JSON and code blocks from messy AI output.
|
|
3
|
+
Extract structured data from AI model responses: JSON, typed schemas, code blocks, and multimodal content.
|
|
6
4
|
|
|
7
5
|
## Installation
|
|
8
6
|
|
|
@@ -10,60 +8,51 @@ LLM responses often wrap structured data in markdown code blocks, preamble text,
|
|
|
10
8
|
npm install @hardlydifficult/ai-msg
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
npm install @hardlydifficult/ai-msg zod
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Usage
|
|
11
|
+
## API
|
|
20
12
|
|
|
21
|
-
### `extractJson(text, sentinel
|
|
13
|
+
### `extractJson(text: string, sentinel?: string): unknown[]`
|
|
22
14
|
|
|
23
|
-
|
|
15
|
+
Extract JSON objects/arrays from AI response text. Uses a three-pass strategy:
|
|
24
16
|
|
|
25
|
-
1.
|
|
26
|
-
2.
|
|
27
|
-
3. Find
|
|
17
|
+
1. Parse the entire text as JSON
|
|
18
|
+
2. Extract from fenced code blocks (json-tagged first, then any)
|
|
19
|
+
3. Find balanced `{}`/`[]` in prose
|
|
28
20
|
|
|
29
|
-
|
|
21
|
+
Pass a `sentinel` string to return `[]` when the response contains it (useful for "no results" markers).
|
|
30
22
|
|
|
31
23
|
````typescript
|
|
32
24
|
import { extractJson } from "@hardlydifficult/ai-msg";
|
|
33
25
|
|
|
34
|
-
extractJson('{"key": "value"}');
|
|
35
|
-
// [{ key: "value" }]
|
|
36
|
-
|
|
37
26
|
extractJson('Here is the result:\n```json\n{"key": "value"}\n```\nDone.');
|
|
38
27
|
// [{ key: "value" }]
|
|
39
28
|
|
|
40
|
-
extractJson('
|
|
41
|
-
// [{
|
|
29
|
+
extractJson('First {"a": 1} then {"b": 2} done.');
|
|
30
|
+
// [{ a: 1 }, { b: 2 }]
|
|
42
31
|
|
|
43
|
-
extractJson("
|
|
32
|
+
extractJson("NO_FINDINGS: scan completed.", "NO_FINDINGS");
|
|
44
33
|
// []
|
|
45
34
|
````
|
|
46
35
|
|
|
47
|
-
|
|
36
|
+
### `extractTyped<T>(text: string, schema: SchemaLike<T>, sentinel?: string): T[]`
|
|
37
|
+
|
|
38
|
+
Extract and validate JSON against a schema. Works with Zod 3, Zod 4, or any object with a `safeParse` method. Only returns entries that pass validation.
|
|
48
39
|
|
|
49
40
|
```typescript
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
```
|
|
41
|
+
import { extractTyped } from "@hardlydifficult/ai-msg";
|
|
42
|
+
import { z } from "zod";
|
|
53
43
|
|
|
54
|
-
|
|
44
|
+
const Person = z.object({ name: z.string(), age: z.number() });
|
|
55
45
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
// []
|
|
46
|
+
extractTyped('{"name": "Alice", "age": 30}', Person);
|
|
47
|
+
// [{ name: "Alice", age: 30 }]
|
|
59
48
|
|
|
60
|
-
|
|
61
|
-
// [
|
|
49
|
+
extractTyped('{"name": "Alice", "age": "thirty"}', Person);
|
|
50
|
+
// [] — fails validation
|
|
62
51
|
```
|
|
63
52
|
|
|
64
|
-
### `extractCodeBlock(text, lang
|
|
53
|
+
### `extractCodeBlock(text: string, lang?: string): string[]`
|
|
65
54
|
|
|
66
|
-
|
|
55
|
+
Extract fenced code block contents. Optionally filter by language tag.
|
|
67
56
|
|
|
68
57
|
````typescript
|
|
69
58
|
import { extractCodeBlock } from "@hardlydifficult/ai-msg";
|
|
@@ -73,27 +62,22 @@ extractCodeBlock("```ts\nconst x = 1;\n```");
|
|
|
73
62
|
|
|
74
63
|
extractCodeBlock("```json\n{}\n```\n```ts\nconst x = 1;\n```", "json");
|
|
75
64
|
// ["{}"]
|
|
76
|
-
|
|
77
|
-
extractCodeBlock("no code blocks");
|
|
78
|
-
// []
|
|
79
65
|
````
|
|
80
66
|
|
|
81
|
-
### `
|
|
67
|
+
### `extractTextContent(content: string | { type: string; text?: string }[]): string`
|
|
82
68
|
|
|
83
|
-
|
|
69
|
+
Extract plain text from a message content field. Handles both plain strings and multimodal content arrays (AI SDK format).
|
|
84
70
|
|
|
85
71
|
```typescript
|
|
86
|
-
import {
|
|
87
|
-
import { z } from "zod";
|
|
72
|
+
import { extractTextContent } from "@hardlydifficult/ai-msg";
|
|
88
73
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
74
|
+
extractTextContent("hello"); // "hello"
|
|
75
|
+
extractTextContent([
|
|
76
|
+
{ type: "text", text: "hello" },
|
|
77
|
+
{ type: "image_url", url: "..." },
|
|
78
|
+
]); // "hello"
|
|
79
|
+
```
|
|
93
80
|
|
|
94
|
-
|
|
95
|
-
// []
|
|
81
|
+
### `toPlainTextMessages(messages: MultimodalMessage[]): { role, content }[]`
|
|
96
82
|
|
|
97
|
-
|
|
98
|
-
// []
|
|
99
|
-
```
|
|
83
|
+
Convert multimodal messages to plain text messages by flattening content arrays.
|
package/dist/extractTyped.d.ts
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Any schema with a safeParse method (e.g. Zod 3, Zod 4, or custom).
|
|
3
|
+
* Using a structural type avoids coupling to a specific Zod version.
|
|
4
|
+
*/
|
|
5
|
+
export interface SchemaLike<T> {
|
|
6
|
+
safeParse(data: unknown): {
|
|
7
|
+
success: true;
|
|
8
|
+
data: T;
|
|
9
|
+
} | {
|
|
10
|
+
success: false;
|
|
11
|
+
error?: unknown;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export declare function extractTyped<T>(text: string, schema: SchemaLike<T>, sentinel?: string): T[];
|
|
3
15
|
//# sourceMappingURL=extractTyped.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extractTyped.d.ts","sourceRoot":"","sources":["../src/extractTyped.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"extractTyped.d.ts","sourceRoot":"","sources":["../src/extractTyped.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,WAAW,UAAU,CAAC,CAAC;IAC3B,SAAS,CACP,IAAI,EAAE,OAAO,GACZ;QAAE,OAAO,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,CAAC,CAAA;KAAE,GAAG;QAAE,OAAO,EAAE,KAAK,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CACrE;AAED,wBAAgB,YAAY,CAAC,CAAC,EAC5B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,EACrB,QAAQ,CAAC,EAAE,MAAM,GAChB,CAAC,EAAE,CAUL"}
|
package/dist/extractTyped.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extractTyped.js","sourceRoot":"","sources":["../src/extractTyped.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"extractTyped.js","sourceRoot":"","sources":["../src/extractTyped.ts"],"names":[],"mappings":";;AAYA,oCAcC;AA1BD,qDAA+C;AAY/C,SAAgB,YAAY,CAC1B,IAAY,EACZ,MAAqB,EACrB,QAAiB;IAEjB,MAAM,OAAO,GAAG,IAAA,4BAAW,EAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAQ,EAAE,CAAC;IAC1B,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { extractCodeBlock } from "./extractCodeBlock.js";
|
|
2
2
|
export { extractJson } from "./extractJson.js";
|
|
3
|
-
export { extractTyped } from "./extractTyped.js";
|
|
3
|
+
export { extractTyped, type SchemaLike } from "./extractTyped.js";
|
|
4
|
+
export { extractTextContent, toPlainTextMessages, type MultimodalMessage, } from "./multimodal.js";
|
|
4
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.extractTyped = exports.extractJson = exports.extractCodeBlock = void 0;
|
|
3
|
+
exports.toPlainTextMessages = exports.extractTextContent = exports.extractTyped = exports.extractJson = exports.extractCodeBlock = void 0;
|
|
4
4
|
var extractCodeBlock_js_1 = require("./extractCodeBlock.js");
|
|
5
5
|
Object.defineProperty(exports, "extractCodeBlock", { enumerable: true, get: function () { return extractCodeBlock_js_1.extractCodeBlock; } });
|
|
6
6
|
var extractJson_js_1 = require("./extractJson.js");
|
|
7
7
|
Object.defineProperty(exports, "extractJson", { enumerable: true, get: function () { return extractJson_js_1.extractJson; } });
|
|
8
8
|
var extractTyped_js_1 = require("./extractTyped.js");
|
|
9
9
|
Object.defineProperty(exports, "extractTyped", { enumerable: true, get: function () { return extractTyped_js_1.extractTyped; } });
|
|
10
|
+
var multimodal_js_1 = require("./multimodal.js");
|
|
11
|
+
Object.defineProperty(exports, "extractTextContent", { enumerable: true, get: function () { return multimodal_js_1.extractTextContent; } });
|
|
12
|
+
Object.defineProperty(exports, "toPlainTextMessages", { enumerable: true, get: function () { return multimodal_js_1.toPlainTextMessages; } });
|
|
10
13
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,6DAAyD;AAAhD,uHAAA,gBAAgB,OAAA;AACzB,mDAA+C;AAAtC,6GAAA,WAAW,OAAA;AACpB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,6DAAyD;AAAhD,uHAAA,gBAAgB,OAAA;AACzB,mDAA+C;AAAtC,6GAAA,WAAW,OAAA;AACpB,qDAAkE;AAAzD,+GAAA,YAAY,OAAA;AACrB,iDAIyB;AAHvB,mHAAA,kBAAkB,OAAA;AAClB,oHAAA,mBAAmB,OAAA"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural type for messages that may contain multimodal content.
|
|
3
|
+
* Compatible with AI SDK message formats and ChatMessage from @ai/shared.
|
|
4
|
+
*/
|
|
5
|
+
export interface MultimodalMessage {
|
|
6
|
+
role: "system" | "user" | "assistant";
|
|
7
|
+
content: string | {
|
|
8
|
+
type: string;
|
|
9
|
+
text?: string;
|
|
10
|
+
}[];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Extract text content from a message content field.
|
|
14
|
+
* Handles both plain string and multimodal content arrays.
|
|
15
|
+
*/
|
|
16
|
+
export declare function extractTextContent(content: MultimodalMessage["content"]): string;
|
|
17
|
+
/**
|
|
18
|
+
* Convert multimodal messages to plain text messages.
|
|
19
|
+
* Flattens any multimodal content arrays to plain text strings.
|
|
20
|
+
*/
|
|
21
|
+
export declare function toPlainTextMessages(messages: MultimodalMessage[]): {
|
|
22
|
+
role: "system" | "user" | "assistant";
|
|
23
|
+
content: string;
|
|
24
|
+
}[];
|
|
25
|
+
//# sourceMappingURL=multimodal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multimodal.d.ts","sourceRoot":"","sources":["../src/multimodal.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;IACtC,OAAO,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACrD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,iBAAiB,CAAC,SAAS,CAAC,GACpC,MAAM,CAWR;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,iBAAiB,EAAE,GAC5B;IAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAAE,CAK9D"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractTextContent = extractTextContent;
|
|
4
|
+
exports.toPlainTextMessages = toPlainTextMessages;
|
|
5
|
+
/**
|
|
6
|
+
* Extract text content from a message content field.
|
|
7
|
+
* Handles both plain string and multimodal content arrays.
|
|
8
|
+
*/
|
|
9
|
+
function extractTextContent(content) {
|
|
10
|
+
if (typeof content === "string") {
|
|
11
|
+
return content;
|
|
12
|
+
}
|
|
13
|
+
return content
|
|
14
|
+
.filter((c) => c.type === "text" && typeof c.text === "string")
|
|
15
|
+
.map((c) => c.text)
|
|
16
|
+
.join("\n");
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Convert multimodal messages to plain text messages.
|
|
20
|
+
* Flattens any multimodal content arrays to plain text strings.
|
|
21
|
+
*/
|
|
22
|
+
function toPlainTextMessages(messages) {
|
|
23
|
+
return messages.map((m) => ({
|
|
24
|
+
role: m.role,
|
|
25
|
+
content: extractTextContent(m.content),
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=multimodal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multimodal.js","sourceRoot":"","sources":["../src/multimodal.ts"],"names":[],"mappings":";;AAaA,gDAaC;AAMD,kDAOC;AA9BD;;;GAGG;AACH,SAAgB,kBAAkB,CAChC,OAAqC;IAErC,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAChC,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,OAAO,OAAO;SACX,MAAM,CACL,CAAC,CAAC,EAAuC,EAAE,CACzC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,CAClD;SACA,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;SAClB,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,SAAgB,mBAAmB,CACjC,QAA6B;IAE7B,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC1B,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,OAAO,CAAC;KACvC,CAAC,CAAC,CAAC;AACN,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hardlydifficult/ai-msg",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -14,14 +14,6 @@
|
|
|
14
14
|
"lint": "tsc --noEmit",
|
|
15
15
|
"clean": "rm -rf dist"
|
|
16
16
|
},
|
|
17
|
-
"peerDependencies": {
|
|
18
|
-
"zod": ">=3.0.0"
|
|
19
|
-
},
|
|
20
|
-
"peerDependenciesMeta": {
|
|
21
|
-
"zod": {
|
|
22
|
-
"optional": true
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
17
|
"devDependencies": {
|
|
26
18
|
"typescript": "5.8.3",
|
|
27
19
|
"vitest": "1.6.1",
|