@hardlydifficult/ai-msg 1.0.0 → 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 CHANGED
@@ -1,8 +1,6 @@
1
1
  # @hardlydifficult/ai-msg
2
2
 
3
- Opinionated utilities for extracting structured output from AI text responses.
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,43 +8,53 @@ LLM responses often wrap structured data in markdown code blocks, preamble text,
10
8
  npm install @hardlydifficult/ai-msg
11
9
  ```
12
10
 
13
- For typed extraction with Zod validation:
14
-
15
- ```bash
16
- npm install @hardlydifficult/ai-msg zod
17
- ```
11
+ ## API
18
12
 
19
- ## Usage
13
+ ### `extractJson(text: string, sentinel?: string): unknown[]`
20
14
 
21
- ### `extractJson(text): unknown | null`
15
+ Extract JSON objects/arrays from AI response text. Uses a three-pass strategy:
22
16
 
23
- Extracts the first valid JSON value from text using a three-pass strategy:
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
24
20
 
25
- 1. Direct `JSON.parse` of the trimmed text
26
- 2. Search markdown code blocks (json-tagged first, then any)
27
- 3. Find balanced `{}` or `[]` substrings in prose
21
+ Pass a `sentinel` string to return `[]` when the response contains it (useful for "no results" markers).
28
22
 
29
- ```typescript
23
+ ````typescript
30
24
  import { extractJson } from "@hardlydifficult/ai-msg";
31
25
 
32
- extractJson('{"key": "value"}');
33
- // { key: "value" }
34
-
35
26
  extractJson('Here is the result:\n```json\n{"key": "value"}\n```\nDone.');
36
- // { key: "value" }
27
+ // [{ key: "value" }]
37
28
 
38
- extractJson('The answer is {"key": "value"} as shown.');
39
- // { key: "value" }
29
+ extractJson('First {"a": 1} then {"b": 2} done.');
30
+ // [{ a: 1 }, { b: 2 }]
40
31
 
41
- extractJson("no json here");
42
- // null
43
- ```
32
+ extractJson("NO_FINDINGS: scan completed.", "NO_FINDINGS");
33
+ // []
34
+ ````
44
35
 
45
- ### `extractCodeBlock(text, lang?): string[]`
36
+ ### `extractTyped<T>(text: string, schema: SchemaLike<T>, sentinel?: string): T[]`
46
37
 
47
- Extracts all markdown code blocks from text. Optionally filters by language tag.
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
41
+ import { extractTyped } from "@hardlydifficult/ai-msg";
42
+ import { z } from "zod";
43
+
44
+ const Person = z.object({ name: z.string(), age: z.number() });
45
+
46
+ extractTyped('{"name": "Alice", "age": 30}', Person);
47
+ // [{ name: "Alice", age: 30 }]
48
+
49
+ extractTyped('{"name": "Alice", "age": "thirty"}', Person);
50
+ // [] — fails validation
51
+ ```
52
+
53
+ ### `extractCodeBlock(text: string, lang?: string): string[]`
54
+
55
+ Extract fenced code block contents. Optionally filter by language tag.
56
+
57
+ ````typescript
50
58
  import { extractCodeBlock } from "@hardlydifficult/ai-msg";
51
59
 
52
60
  extractCodeBlock("```ts\nconst x = 1;\n```");
@@ -54,24 +62,22 @@ extractCodeBlock("```ts\nconst x = 1;\n```");
54
62
 
55
63
  extractCodeBlock("```json\n{}\n```\n```ts\nconst x = 1;\n```", "json");
56
64
  // ["{}"]
65
+ ````
57
66
 
58
- extractCodeBlock("no code blocks");
59
- // []
60
- ```
61
-
62
- ### `extractTyped<T>(text, schema): T | null`
67
+ ### `extractTextContent(content: string | { type: string; text?: string }[]): string`
63
68
 
64
- Extracts JSON from text and validates it against a Zod schema. Requires `zod` as a peer dependency.
69
+ Extract plain text from a message content field. Handles both plain strings and multimodal content arrays (AI SDK format).
65
70
 
66
71
  ```typescript
67
- import { extractTyped } from "@hardlydifficult/ai-msg";
68
- import { z } from "zod";
72
+ import { extractTextContent } from "@hardlydifficult/ai-msg";
69
73
 
70
- const Person = z.object({ name: z.string(), age: z.number() });
74
+ extractTextContent("hello"); // "hello"
75
+ extractTextContent([
76
+ { type: "text", text: "hello" },
77
+ { type: "image_url", url: "..." },
78
+ ]); // "hello"
79
+ ```
71
80
 
72
- extractTyped('{"name": "Alice", "age": 30}', Person);
73
- // { name: "Alice", age: 30 }
81
+ ### `toPlainTextMessages(messages: MultimodalMessage[]): { role, content }[]`
74
82
 
75
- extractTyped('{"name": "Alice", "age": "thirty"}', Person);
76
- // null
77
- ```
83
+ Convert multimodal messages to plain text messages by flattening content arrays.
@@ -1,2 +1,2 @@
1
- export declare function extractJson(text: string): unknown;
1
+ export declare function extractJson(text: string, sentinel?: string): unknown[];
2
2
  //# sourceMappingURL=extractJson.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"extractJson.d.ts","sourceRoot":"","sources":["../src/extractJson.ts"],"names":[],"mappings":"AAWA,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAuCjD"}
1
+ {"version":3,"file":"extractJson.d.ts","sourceRoot":"","sources":["../src/extractJson.ts"],"names":[],"mappings":"AAWA,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,EAAE,CAgDtE"}
@@ -11,40 +11,49 @@ function tryParse(text) {
11
11
  return undefined;
12
12
  }
13
13
  }
14
- function extractJson(text) {
14
+ function extractJson(text, sentinel) {
15
+ // Sentinel check — if the text contains the sentinel, treat as "no findings"
16
+ if (sentinel !== undefined && text.includes(sentinel)) {
17
+ return [];
18
+ }
15
19
  // Pass 1: try the whole text
16
20
  const direct = tryParse(text.trim());
17
21
  if (direct !== undefined) {
18
- return direct;
22
+ return [direct];
19
23
  }
20
24
  // Pass 2: code blocks — json-tagged first, then any
25
+ const fromBlocks = [];
21
26
  for (const block of (0, extractCodeBlock_js_1.extractCodeBlock)(text, "json")) {
22
27
  const parsed = tryParse(block.trim());
23
28
  if (parsed !== undefined) {
24
- return parsed;
29
+ fromBlocks.push(parsed);
25
30
  }
26
31
  }
27
- for (const block of (0, extractCodeBlock_js_1.extractCodeBlock)(text)) {
28
- const parsed = tryParse(block.trim());
29
- if (parsed !== undefined) {
30
- return parsed;
32
+ if (fromBlocks.length === 0) {
33
+ for (const block of (0, extractCodeBlock_js_1.extractCodeBlock)(text)) {
34
+ const parsed = tryParse(block.trim());
35
+ if (parsed !== undefined) {
36
+ fromBlocks.push(parsed);
37
+ }
31
38
  }
32
39
  }
33
- // Pass 3: balanced braces / brackets
34
- const obj = (0, findBalanced_js_1.findBalanced)(text, "{", "}");
35
- if (obj !== null) {
36
- const parsed = tryParse(obj);
40
+ if (fromBlocks.length > 0) {
41
+ return fromBlocks;
42
+ }
43
+ // Pass 3: all balanced braces / brackets in prose
44
+ const results = [];
45
+ for (const match of (0, findBalanced_js_1.findAllBalanced)(text, "{", "}")) {
46
+ const parsed = tryParse(match);
37
47
  if (parsed !== undefined) {
38
- return parsed;
48
+ results.push(parsed);
39
49
  }
40
50
  }
41
- const arr = (0, findBalanced_js_1.findBalanced)(text, "[", "]");
42
- if (arr !== null) {
43
- const parsed = tryParse(arr);
51
+ for (const match of (0, findBalanced_js_1.findAllBalanced)(text, "[", "]")) {
52
+ const parsed = tryParse(match);
44
53
  if (parsed !== undefined) {
45
- return parsed;
54
+ results.push(parsed);
46
55
  }
47
56
  }
48
- return null;
57
+ return results;
49
58
  }
50
59
  //# sourceMappingURL=extractJson.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"extractJson.js","sourceRoot":"","sources":["../src/extractJson.ts"],"names":[],"mappings":";;AAWA,kCAuCC;AAlDD,+DAAyD;AACzD,uDAAiD;AAEjD,SAAS,QAAQ,CAAC,IAAY;IAC5B,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAgB,WAAW,CAAC,IAAY;IACtC,6BAA6B;IAC7B,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACrC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,oDAAoD;IACpD,KAAK,MAAM,KAAK,IAAI,IAAA,sCAAgB,EAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC;QACnD,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACtC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,IAAA,sCAAgB,EAAC,IAAI,CAAC,EAAE,CAAC;QAC3C,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACtC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IAED,qCAAqC;IACrC,MAAM,GAAG,GAAG,IAAA,8BAAY,EAAC,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IACzC,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACjB,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IAED,MAAM,GAAG,GAAG,IAAA,8BAAY,EAAC,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IACzC,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACjB,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"extractJson.js","sourceRoot":"","sources":["../src/extractJson.ts"],"names":[],"mappings":";;AAWA,kCAgDC;AA3DD,+DAAyD;AACzD,uDAAoD;AAEpD,SAAS,QAAQ,CAAC,IAAY;IAC5B,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAgB,WAAW,CAAC,IAAY,EAAE,QAAiB;IACzD,6EAA6E;IAC7E,IAAI,QAAQ,KAAK,SAAS,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QACtD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,6BAA6B;IAC7B,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACrC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,CAAC,MAAM,CAAC,CAAC;IAClB,CAAC;IAED,oDAAoD;IACpD,MAAM,UAAU,GAAc,EAAE,CAAC;IACjC,KAAK,MAAM,KAAK,IAAI,IAAA,sCAAgB,EAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC;QACnD,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACtC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IACD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,KAAK,MAAM,KAAK,IAAI,IAAA,sCAAgB,EAAC,IAAI,CAAC,EAAE,CAAC;YAC3C,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YACtC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBACzB,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;IACH,CAAC;IACD,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,kDAAkD;IAClD,MAAM,OAAO,GAAc,EAAE,CAAC;IAC9B,KAAK,MAAM,KAAK,IAAI,IAAA,iCAAe,EAAC,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC;QACpD,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC/B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,IAAA,iCAAe,EAAC,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC;QACpD,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC/B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -1,3 +1,15 @@
1
- import { type ZodType } from "zod";
2
- export declare function extractTyped<T>(text: string, schema: ZodType<T>): T | null;
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":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,KAAK,CAAC;AAInC,wBAAgB,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAQ1E"}
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"}
@@ -2,12 +2,15 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.extractTyped = extractTyped;
4
4
  const extractJson_js_1 = require("./extractJson.js");
5
- function extractTyped(text, schema) {
6
- const json = (0, extractJson_js_1.extractJson)(text);
7
- if (json === null) {
8
- return null;
5
+ function extractTyped(text, schema, sentinel) {
6
+ const results = (0, extractJson_js_1.extractJson)(text, sentinel);
7
+ const validated = [];
8
+ for (const json of results) {
9
+ const result = schema.safeParse(json);
10
+ if (result.success) {
11
+ validated.push(result.data);
12
+ }
9
13
  }
10
- const result = schema.safeParse(json);
11
- return result.success ? result.data : null;
14
+ return validated;
12
15
  }
13
16
  //# sourceMappingURL=extractTyped.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"extractTyped.js","sourceRoot":"","sources":["../src/extractTyped.ts"],"names":[],"mappings":";;AAIA,oCAQC;AAVD,qDAA+C;AAE/C,SAAgB,YAAY,CAAI,IAAY,EAAE,MAAkB;IAC9D,MAAM,IAAI,GAAG,IAAA,4BAAW,EAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACtC,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7C,CAAC"}
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"}
@@ -1,2 +1,3 @@
1
1
  export declare function findBalanced(text: string, openChar: string, closeChar: string): string | null;
2
+ export declare function findAllBalanced(text: string, openChar: string, closeChar: string): string[];
2
3
  //# sourceMappingURL=findBalanced.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"findBalanced.d.ts","sourceRoot":"","sources":["../src/findBalanced.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CA6Cf"}
1
+ {"version":3,"file":"findBalanced.d.ts","sourceRoot":"","sources":["../src/findBalanced.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CA6Cf;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,MAAM,EAAE,CAiBV"}
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.findBalanced = findBalanced;
4
+ exports.findAllBalanced = findAllBalanced;
4
5
  function findBalanced(text, openChar, closeChar) {
5
6
  const start = text.indexOf(openChar);
6
7
  if (start === -1) {
@@ -40,4 +41,19 @@ function findBalanced(text, openChar, closeChar) {
40
41
  }
41
42
  return null;
42
43
  }
44
+ function findAllBalanced(text, openChar, closeChar) {
45
+ const results = [];
46
+ let offset = 0;
47
+ while (offset < text.length) {
48
+ const remaining = text.slice(offset);
49
+ const match = findBalanced(remaining, openChar, closeChar);
50
+ if (match === null) {
51
+ break;
52
+ }
53
+ results.push(match);
54
+ const matchStart = remaining.indexOf(openChar);
55
+ offset += matchStart + match.length;
56
+ }
57
+ return results;
58
+ }
43
59
  //# sourceMappingURL=findBalanced.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"findBalanced.js","sourceRoot":"","sources":["../src/findBalanced.ts"],"names":[],"mappings":";;AAAA,oCAiDC;AAjDD,SAAgB,YAAY,CAC1B,IAAY,EACZ,QAAgB,EAChB,SAAiB;IAEjB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACrC,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;QACjB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAEnB,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,GAAG,KAAK,CAAC;YAChB,SAAS;QACX,CAAC;QAED,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YAChB,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC;YACD,SAAS;QACX,CAAC;QAED,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACf,QAAQ,GAAG,CAAC,QAAQ,CAAC;YACrB,SAAS;QACX,CAAC;QAED,IAAI,QAAQ,EAAE,CAAC;YACb,SAAS;QACX,CAAC;QAED,IAAI,EAAE,KAAK,QAAQ,EAAE,CAAC;YACpB,KAAK,EAAE,CAAC;QACV,CAAC;aAAM,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;YAC5B,KAAK,EAAE,CAAC;YACR,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;gBAChB,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;YAClC,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"findBalanced.js","sourceRoot":"","sources":["../src/findBalanced.ts"],"names":[],"mappings":";;AAAA,oCAiDC;AAED,0CAqBC;AAxED,SAAgB,YAAY,CAC1B,IAAY,EACZ,QAAgB,EAChB,SAAiB;IAEjB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACrC,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;QACjB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAEnB,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,GAAG,KAAK,CAAC;YAChB,SAAS;QACX,CAAC;QAED,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YAChB,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC;YACD,SAAS;QACX,CAAC;QAED,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACf,QAAQ,GAAG,CAAC,QAAQ,CAAC;YACrB,SAAS;QACX,CAAC;QAED,IAAI,QAAQ,EAAE,CAAC;YACb,SAAS;QACX,CAAC;QAED,IAAI,EAAE,KAAK,QAAQ,EAAE,CAAC;YACpB,KAAK,EAAE,CAAC;QACV,CAAC;aAAM,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;YAC5B,KAAK,EAAE,CAAC;YACR,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;gBAChB,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;YAClC,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAgB,eAAe,CAC7B,IAAY,EACZ,QAAgB,EAChB,SAAiB;IAEjB,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,MAAM,GAAG,CAAC,CAAC;IAEf,OAAO,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QAC5B,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACrC,MAAM,KAAK,GAAG,YAAY,CAAC,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;QAC3D,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,MAAM;QACR,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpB,MAAM,UAAU,GAAG,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC/C,MAAM,IAAI,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC;IACtC,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,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
@@ -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,qDAAiD;AAAxC,+GAAA,YAAY,OAAA"}
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.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",