@hardlydifficult/ai 1.0.0
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 +139 -0
- package/dist/claude.d.ts +11 -0
- package/dist/claude.d.ts.map +1 -0
- package/dist/claude.js +15 -0
- package/dist/claude.js.map +1 -0
- package/dist/createAI.d.ts +6 -0
- package/dist/createAI.d.ts.map +1 -0
- package/dist/createAI.js +100 -0
- package/dist/createAI.js.map +1 -0
- package/dist/extractCodeBlock.d.ts +3 -0
- package/dist/extractCodeBlock.d.ts.map +1 -0
- package/dist/extractCodeBlock.js +18 -0
- package/dist/extractCodeBlock.js.map +1 -0
- package/dist/extractJson.d.ts +3 -0
- package/dist/extractJson.d.ts.map +1 -0
- package/dist/extractJson.js +60 -0
- package/dist/extractJson.js.map +1 -0
- package/dist/extractTyped.d.ts +16 -0
- package/dist/extractTyped.d.ts.map +1 -0
- package/dist/extractTyped.js +17 -0
- package/dist/extractTyped.js.map +1 -0
- package/dist/findBalanced.d.ts +5 -0
- package/dist/findBalanced.d.ts.map +1 -0
- package/dist/findBalanced.js +61 -0
- package/dist/findBalanced.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- 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/dist/ollama.d.ts +4 -0
- package/dist/ollama.d.ts.map +1 -0
- package/dist/ollama.js +9 -0
- package/dist/ollama.js.map +1 -0
- package/dist/types.d.ts +25 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# @hardlydifficult/ai
|
|
2
|
+
|
|
3
|
+
Unified AI client with chainable structured output, required usage tracking, and response parsing utilities.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @hardlydifficult/ai
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## AI Client
|
|
12
|
+
|
|
13
|
+
### `createAI(model, tracker, options?)`
|
|
14
|
+
|
|
15
|
+
Creates an AI client. Usage tracking is **required** — every call automatically fires `tracker.record()`.
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { createAI, claude } from "@hardlydifficult/ai";
|
|
19
|
+
|
|
20
|
+
const ai = createAI(claude("sonnet"), tracker);
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### `chat(prompt, systemPrompt?)`
|
|
24
|
+
|
|
25
|
+
Send a prompt and get a rich response with usage info and conversation support.
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
const msg = await ai.chat("Explain closures");
|
|
29
|
+
msg.text; // "A closure is..."
|
|
30
|
+
msg.usage; // { inputTokens: 50, outputTokens: 200, durationMs: 1200 }
|
|
31
|
+
|
|
32
|
+
// With system prompt
|
|
33
|
+
const msg = await ai.chat("Explain closures", "You are a TypeScript tutor");
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### `.zod(schema)` — Structured Output
|
|
37
|
+
|
|
38
|
+
Chain `.zod(schema)` to constrain the model's output format AND validate the result. The schema does triple duty: constrains output, validates response, provides TypeScript types.
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { z } from "zod";
|
|
42
|
+
|
|
43
|
+
const TaskSchema = z.object({
|
|
44
|
+
title: z.string(),
|
|
45
|
+
priority: z.enum(["high", "medium", "low"]),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const msg = await ai.chat("Create a task for fixing the login bug").zod(TaskSchema);
|
|
49
|
+
msg.data; // { title: "Fix login bug", priority: "high" } — typed as z.infer<typeof TaskSchema>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### `.reply(prompt)` — Conversation
|
|
53
|
+
|
|
54
|
+
Continue a conversation. Message history accumulates automatically.
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
const msg1 = await ai.chat("What is a monad?");
|
|
58
|
+
const msg2 = await msg1.reply("Give me a TypeScript example");
|
|
59
|
+
const msg3 = await msg2.reply("Now formalize it").zod(DefinitionSchema);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Model Helpers
|
|
63
|
+
|
|
64
|
+
### `claude(variant)`
|
|
65
|
+
|
|
66
|
+
Short names for Anthropic models. Uses auto-resolving aliases — always gets the latest snapshot.
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import { claude } from "@hardlydifficult/ai";
|
|
70
|
+
|
|
71
|
+
claude("sonnet"); // claude-sonnet-4-5 → latest snapshot
|
|
72
|
+
claude("haiku"); // claude-haiku-4-5
|
|
73
|
+
claude("opus"); // claude-opus-4-6
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### `ollama(model)`
|
|
77
|
+
|
|
78
|
+
Ollama models. Names match whatever is installed locally.
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { ollama } from "@hardlydifficult/ai";
|
|
82
|
+
|
|
83
|
+
ollama("qwen3-coder-next:latest");
|
|
84
|
+
ollama("llama3.3");
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Usage Tracking
|
|
88
|
+
|
|
89
|
+
`createAI` requires an `AITracker` — no AI without tracking. The tracker fires for every call (`chat`, `reply`, `zod`).
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import type { AITracker } from "@hardlydifficult/ai";
|
|
93
|
+
|
|
94
|
+
const tracker: AITracker = {
|
|
95
|
+
record({ inputTokens, outputTokens, durationMs }) {
|
|
96
|
+
const cost = inputTokens * 3 / 1_000_000 + outputTokens * 15 / 1_000_000;
|
|
97
|
+
console.log(`Cost: $${cost.toFixed(4)}`);
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const ai = createAI(claude("sonnet"), tracker);
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Response Parsing
|
|
105
|
+
|
|
106
|
+
### `extractJson(text, sentinel?)`
|
|
107
|
+
|
|
108
|
+
Extract JSON from AI response text using a three-pass strategy: direct parse, code blocks, balanced braces.
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
import { extractJson } from "@hardlydifficult/ai";
|
|
112
|
+
|
|
113
|
+
extractJson('Here is the result:\n```json\n{"key": "value"}\n```');
|
|
114
|
+
// [{ key: "value" }]
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### `extractTyped(text, schema, sentinel?)`
|
|
118
|
+
|
|
119
|
+
Extract and validate JSON against a schema. Works with any object that has a `safeParse` method.
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
import { extractTyped } from "@hardlydifficult/ai";
|
|
123
|
+
|
|
124
|
+
const Person = z.object({ name: z.string(), age: z.number() });
|
|
125
|
+
extractTyped('{"name": "Alice", "age": 30}', Person);
|
|
126
|
+
// [{ name: "Alice", age: 30 }]
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### `extractCodeBlock(text, lang?)`
|
|
130
|
+
|
|
131
|
+
Extract fenced code block contents, optionally filtered by language tag.
|
|
132
|
+
|
|
133
|
+
### `extractTextContent(content)`
|
|
134
|
+
|
|
135
|
+
Extract plain text from multimodal content (string or content array).
|
|
136
|
+
|
|
137
|
+
### `toPlainTextMessages(messages)`
|
|
138
|
+
|
|
139
|
+
Convert multimodal messages to plain text messages.
|
package/dist/claude.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { LanguageModel } from "ai";
|
|
2
|
+
/** Anthropic aliases — auto-resolve to the latest snapshot. */
|
|
3
|
+
declare const MODELS: {
|
|
4
|
+
readonly sonnet: "claude-sonnet-4-5";
|
|
5
|
+
readonly haiku: "claude-haiku-4-5";
|
|
6
|
+
readonly opus: "claude-opus-4-6";
|
|
7
|
+
};
|
|
8
|
+
/** Creates an Anthropic language model from a short variant name. */
|
|
9
|
+
export declare function claude(variant: keyof typeof MODELS): LanguageModel;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=claude.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../src/claude.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAExC,+DAA+D;AAC/D,QAAA,MAAM,MAAM;;;;CAIF,CAAC;AAEX,qEAAqE;AACrE,wBAAgB,MAAM,CAAC,OAAO,EAAE,MAAM,OAAO,MAAM,GAAG,aAAa,CAElE"}
|
package/dist/claude.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.claude = claude;
|
|
4
|
+
const anthropic_1 = require("@ai-sdk/anthropic");
|
|
5
|
+
/** Anthropic aliases — auto-resolve to the latest snapshot. */
|
|
6
|
+
const MODELS = {
|
|
7
|
+
sonnet: "claude-sonnet-4-5",
|
|
8
|
+
haiku: "claude-haiku-4-5",
|
|
9
|
+
opus: "claude-opus-4-6",
|
|
10
|
+
};
|
|
11
|
+
/** Creates an Anthropic language model from a short variant name. */
|
|
12
|
+
function claude(variant) {
|
|
13
|
+
return (0, anthropic_1.createAnthropic)()(MODELS[variant]);
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=claude.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude.js","sourceRoot":"","sources":["../src/claude.ts"],"names":[],"mappings":";;AAWA,wBAEC;AAbD,iDAAoD;AAGpD,+DAA+D;AAC/D,MAAM,MAAM,GAAG;IACb,MAAM,EAAE,mBAAmB;IAC3B,KAAK,EAAE,kBAAkB;IACzB,IAAI,EAAE,iBAAiB;CACf,CAAC;AAEX,qEAAqE;AACrE,SAAgB,MAAM,CAAC,OAA4B;IACjD,OAAO,IAAA,2BAAe,GAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;AAC5C,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Logger } from "@hardlydifficult/logger";
|
|
2
|
+
import { type LanguageModel } from "ai";
|
|
3
|
+
import type { AI, AIOptions, AITracker } from "./types.js";
|
|
4
|
+
/** Creates an AI client with required usage tracking and logging. */
|
|
5
|
+
export declare function createAI(model: LanguageModel, tracker: AITracker, logger: Logger, options?: AIOptions): AI;
|
|
6
|
+
//# sourceMappingURL=createAI.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createAI.d.ts","sourceRoot":"","sources":["../src/createAI.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAgB,KAAK,aAAa,EAAU,MAAM,IAAI,CAAC;AAG9D,OAAO,KAAK,EACV,EAAE,EACF,SAAS,EACT,SAAS,EAIV,MAAM,YAAY,CAAC;AA6IpB,qEAAqE;AACrE,wBAAgB,QAAQ,CACtB,KAAK,EAAE,aAAa,EACpB,OAAO,EAAE,SAAS,EAClB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,SAAS,GAClB,EAAE,CAoBJ"}
|
package/dist/createAI.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createAI = createAI;
|
|
4
|
+
const ai_1 = require("ai");
|
|
5
|
+
const DEFAULT_MAX_TOKENS = 4096;
|
|
6
|
+
function createChatCall(model, tracker, logger, maxTokens, messages, systemPrompt) {
|
|
7
|
+
let zodSchema;
|
|
8
|
+
async function execute() {
|
|
9
|
+
const promptLength = messages.reduce((n, m) => n + m.content.length, 0);
|
|
10
|
+
logger.debug("AI request", {
|
|
11
|
+
promptLength,
|
|
12
|
+
hasSystemPrompt: systemPrompt !== undefined,
|
|
13
|
+
hasSchema: zodSchema !== undefined,
|
|
14
|
+
});
|
|
15
|
+
const startMs = Date.now();
|
|
16
|
+
const result = await (0, ai_1.generateText)({
|
|
17
|
+
model,
|
|
18
|
+
messages,
|
|
19
|
+
maxOutputTokens: maxTokens,
|
|
20
|
+
...(systemPrompt !== undefined && { system: systemPrompt }),
|
|
21
|
+
...(zodSchema !== undefined && {
|
|
22
|
+
output: ai_1.Output.object({ schema: zodSchema }),
|
|
23
|
+
}),
|
|
24
|
+
});
|
|
25
|
+
const durationMs = Date.now() - startMs;
|
|
26
|
+
const resultUsage = result.usage;
|
|
27
|
+
const usage = {
|
|
28
|
+
inputTokens: resultUsage.inputTokens ?? 0,
|
|
29
|
+
outputTokens: resultUsage.outputTokens ?? 0,
|
|
30
|
+
durationMs,
|
|
31
|
+
};
|
|
32
|
+
tracker.record(usage);
|
|
33
|
+
logger.debug("AI response", {
|
|
34
|
+
responseLength: result.text.length,
|
|
35
|
+
durationMs,
|
|
36
|
+
inputTokens: usage.inputTokens,
|
|
37
|
+
outputTokens: usage.outputTokens,
|
|
38
|
+
});
|
|
39
|
+
const responseMessages = [
|
|
40
|
+
...messages,
|
|
41
|
+
{ role: "assistant", content: result.text },
|
|
42
|
+
];
|
|
43
|
+
const msg = {
|
|
44
|
+
text: result.text,
|
|
45
|
+
usage,
|
|
46
|
+
reply(prompt) {
|
|
47
|
+
return createChatCall(model, tracker, logger, maxTokens, [...responseMessages, { role: "user", content: prompt }], systemPrompt);
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
if (zodSchema !== undefined) {
|
|
51
|
+
return { ...msg, data: result.output };
|
|
52
|
+
}
|
|
53
|
+
return msg;
|
|
54
|
+
}
|
|
55
|
+
const call = {
|
|
56
|
+
text() {
|
|
57
|
+
return {
|
|
58
|
+
then(onfulfilled, onrejected) {
|
|
59
|
+
return execute().then((msg) => {
|
|
60
|
+
return onfulfilled
|
|
61
|
+
? onfulfilled(msg.text)
|
|
62
|
+
: msg.text;
|
|
63
|
+
}, onrejected);
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
},
|
|
67
|
+
zod(schema) {
|
|
68
|
+
zodSchema = schema;
|
|
69
|
+
return {
|
|
70
|
+
then(onfulfilled, onrejected) {
|
|
71
|
+
return execute().then((msg) => {
|
|
72
|
+
const data = msg
|
|
73
|
+
.data;
|
|
74
|
+
return onfulfilled
|
|
75
|
+
? onfulfilled(data)
|
|
76
|
+
: data;
|
|
77
|
+
}, onrejected);
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
then(onfulfilled, onrejected) {
|
|
82
|
+
return execute().then(onfulfilled, onrejected);
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
return call;
|
|
86
|
+
}
|
|
87
|
+
/** Creates an AI client with required usage tracking and logging. */
|
|
88
|
+
function createAI(model, tracker, logger, options) {
|
|
89
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime guard for JS callers
|
|
90
|
+
if (tracker === undefined || tracker === null) {
|
|
91
|
+
throw new Error("AITracker is required — all AI usage must be tracked");
|
|
92
|
+
}
|
|
93
|
+
const maxTokens = options?.maxTokens ?? DEFAULT_MAX_TOKENS;
|
|
94
|
+
return {
|
|
95
|
+
chat(prompt, systemPrompt) {
|
|
96
|
+
return createChatCall(model, tracker, logger, maxTokens, [{ role: "user", content: prompt }], systemPrompt);
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=createAI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createAI.js","sourceRoot":"","sources":["../src/createAI.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractCodeBlock.d.ts","sourceRoot":"","sources":["../src/extractCodeBlock.ts"],"names":[],"mappings":"AAEA,sGAAsG;AACtG,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAatE"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractCodeBlock = extractCodeBlock;
|
|
4
|
+
const codeBlockRegex = /^```(\w*)\s*\n([\s\S]*?)^```/gm;
|
|
5
|
+
/** Extracts the contents of fenced code blocks from a string, optionally filtered by language tag. */
|
|
6
|
+
function extractCodeBlock(text, lang) {
|
|
7
|
+
const results = [];
|
|
8
|
+
for (const match of text.matchAll(codeBlockRegex)) {
|
|
9
|
+
const tag = match[1];
|
|
10
|
+
const content = match[2];
|
|
11
|
+
if (lang !== undefined && tag.toLowerCase() !== lang.toLowerCase()) {
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
results.push(content.trimEnd());
|
|
15
|
+
}
|
|
16
|
+
return results;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=extractCodeBlock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractCodeBlock.js","sourceRoot":"","sources":["../src/extractCodeBlock.ts"],"names":[],"mappings":";;AAGA,4CAaC;AAhBD,MAAM,cAAc,GAAG,gCAAgC,CAAC;AAExD,sGAAsG;AACtG,SAAgB,gBAAgB,CAAC,IAAY,EAAE,IAAa;IAC1D,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;QAClD,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACrB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACzB,IAAI,IAAI,KAAK,SAAS,IAAI,GAAG,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YACnE,SAAS;QACX,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAClC,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractJson.d.ts","sourceRoot":"","sources":["../src/extractJson.ts"],"names":[],"mappings":"AAWA,4HAA4H;AAC5H,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,EAAE,CAgDtE"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractJson = extractJson;
|
|
4
|
+
const extractCodeBlock_js_1 = require("./extractCodeBlock.js");
|
|
5
|
+
const findBalanced_js_1 = require("./findBalanced.js");
|
|
6
|
+
function tryParse(text) {
|
|
7
|
+
try {
|
|
8
|
+
return JSON.parse(text);
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/** Extracts JSON values from text using progressive strategies: direct parse, code blocks, then balanced-brace scanning. */
|
|
15
|
+
function extractJson(text, sentinel) {
|
|
16
|
+
// Sentinel check — if the text contains the sentinel, treat as "no findings"
|
|
17
|
+
if (sentinel !== undefined && text.includes(sentinel)) {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
// Pass 1: try the whole text
|
|
21
|
+
const direct = tryParse(text.trim());
|
|
22
|
+
if (direct !== undefined) {
|
|
23
|
+
return [direct];
|
|
24
|
+
}
|
|
25
|
+
// Pass 2: code blocks — json-tagged first, then any
|
|
26
|
+
const fromBlocks = [];
|
|
27
|
+
for (const block of (0, extractCodeBlock_js_1.extractCodeBlock)(text, "json")) {
|
|
28
|
+
const parsed = tryParse(block.trim());
|
|
29
|
+
if (parsed !== undefined) {
|
|
30
|
+
fromBlocks.push(parsed);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (fromBlocks.length === 0) {
|
|
34
|
+
for (const block of (0, extractCodeBlock_js_1.extractCodeBlock)(text)) {
|
|
35
|
+
const parsed = tryParse(block.trim());
|
|
36
|
+
if (parsed !== undefined) {
|
|
37
|
+
fromBlocks.push(parsed);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (fromBlocks.length > 0) {
|
|
42
|
+
return fromBlocks;
|
|
43
|
+
}
|
|
44
|
+
// Pass 3: all balanced braces / brackets in prose
|
|
45
|
+
const results = [];
|
|
46
|
+
for (const match of (0, findBalanced_js_1.findAllBalanced)(text, "{", "}")) {
|
|
47
|
+
const parsed = tryParse(match);
|
|
48
|
+
if (parsed !== undefined) {
|
|
49
|
+
results.push(parsed);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
for (const match of (0, findBalanced_js_1.findAllBalanced)(text, "[", "]")) {
|
|
53
|
+
const parsed = tryParse(match);
|
|
54
|
+
if (parsed !== undefined) {
|
|
55
|
+
results.push(parsed);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return results;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=extractJson.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractJson.js","sourceRoot":"","sources":["../src/extractJson.ts"],"names":[],"mappings":";;AAYA,kCAgDC;AA5DD,+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,4HAA4H;AAC5H,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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
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
|
+
/** Extracts JSON from text and validates each result against a schema, returning only values that pass. */
|
|
15
|
+
export declare function extractTyped<T>(text: string, schema: SchemaLike<T>, sentinel?: string): T[];
|
|
16
|
+
//# sourceMappingURL=extractTyped.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
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,2GAA2G;AAC3G,wBAAgB,YAAY,CAAC,CAAC,EAC5B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,EACrB,QAAQ,CAAC,EAAE,MAAM,GAChB,CAAC,EAAE,CAUL"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractTyped = extractTyped;
|
|
4
|
+
const extractJson_js_1 = require("./extractJson.js");
|
|
5
|
+
/** Extracts JSON from text and validates each result against a schema, returning only values that pass. */
|
|
6
|
+
function extractTyped(text, schema, sentinel) {
|
|
7
|
+
const results = (0, extractJson_js_1.extractJson)(text, sentinel);
|
|
8
|
+
const validated = [];
|
|
9
|
+
for (const json of results) {
|
|
10
|
+
const result = schema.safeParse(json);
|
|
11
|
+
if (result.success) {
|
|
12
|
+
validated.push(result.data);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return validated;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=extractTyped.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractTyped.js","sourceRoot":"","sources":["../src/extractTyped.ts"],"names":[],"mappings":";;AAaA,oCAcC;AA3BD,qDAA+C;AAY/C,2GAA2G;AAC3G,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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Finds the first substring enclosed by balanced open/close characters, respecting JSON string escaping. */
|
|
2
|
+
export declare function findBalanced(text: string, openChar: string, closeChar: string): string | null;
|
|
3
|
+
/** Finds all non-overlapping substrings enclosed by balanced open/close characters. */
|
|
4
|
+
export declare function findAllBalanced(text: string, openChar: string, closeChar: string): string[];
|
|
5
|
+
//# sourceMappingURL=findBalanced.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"findBalanced.d.ts","sourceRoot":"","sources":["../src/findBalanced.ts"],"names":[],"mappings":"AAAA,6GAA6G;AAC7G,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CA6Cf;AAED,uFAAuF;AACvF,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,MAAM,EAAE,CAiBV"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findBalanced = findBalanced;
|
|
4
|
+
exports.findAllBalanced = findAllBalanced;
|
|
5
|
+
/** Finds the first substring enclosed by balanced open/close characters, respecting JSON string escaping. */
|
|
6
|
+
function findBalanced(text, openChar, closeChar) {
|
|
7
|
+
const start = text.indexOf(openChar);
|
|
8
|
+
if (start === -1) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
let depth = 0;
|
|
12
|
+
let inString = false;
|
|
13
|
+
let escaped = false;
|
|
14
|
+
for (let i = start; i < text.length; i++) {
|
|
15
|
+
const ch = text[i];
|
|
16
|
+
if (escaped) {
|
|
17
|
+
escaped = false;
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (ch === "\\") {
|
|
21
|
+
if (inString) {
|
|
22
|
+
escaped = true;
|
|
23
|
+
}
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (ch === '"') {
|
|
27
|
+
inString = !inString;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (inString) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (ch === openChar) {
|
|
34
|
+
depth++;
|
|
35
|
+
}
|
|
36
|
+
else if (ch === closeChar) {
|
|
37
|
+
depth--;
|
|
38
|
+
if (depth === 0) {
|
|
39
|
+
return text.slice(start, i + 1);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
/** Finds all non-overlapping substrings enclosed by balanced open/close characters. */
|
|
46
|
+
function findAllBalanced(text, openChar, closeChar) {
|
|
47
|
+
const results = [];
|
|
48
|
+
let offset = 0;
|
|
49
|
+
while (offset < text.length) {
|
|
50
|
+
const remaining = text.slice(offset);
|
|
51
|
+
const match = findBalanced(remaining, openChar, closeChar);
|
|
52
|
+
if (match === null) {
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
results.push(match);
|
|
56
|
+
const matchStart = remaining.indexOf(openChar);
|
|
57
|
+
offset += matchStart + match.length;
|
|
58
|
+
}
|
|
59
|
+
return results;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=findBalanced.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"findBalanced.js","sourceRoot":"","sources":["../src/findBalanced.ts"],"names":[],"mappings":";;AACA,oCAiDC;AAGD,0CAqBC;AA1ED,6GAA6G;AAC7G,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,uFAAuF;AACvF,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
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { createAI } from "./createAI.js";
|
|
2
|
+
export { claude } from "./claude.js";
|
|
3
|
+
export { ollama } from "./ollama.js";
|
|
4
|
+
export type { AI, AIOptions, AITracker, ChatCall, ChatMessage, Usage, } from "./types.js";
|
|
5
|
+
export { extractCodeBlock } from "./extractCodeBlock.js";
|
|
6
|
+
export { extractJson } from "./extractJson.js";
|
|
7
|
+
export { extractTyped, type SchemaLike } from "./extractTyped.js";
|
|
8
|
+
export { extractTextContent, toPlainTextMessages, type MultimodalMessage, } from "./multimodal.js";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EACV,EAAE,EACF,SAAS,EACT,SAAS,EACT,QAAQ,EACR,WAAW,EACX,KAAK,GACN,MAAM,YAAY,CAAC;AACpB,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
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toPlainTextMessages = exports.extractTextContent = exports.extractTyped = exports.extractJson = exports.extractCodeBlock = exports.ollama = exports.claude = exports.createAI = void 0;
|
|
4
|
+
var createAI_js_1 = require("./createAI.js");
|
|
5
|
+
Object.defineProperty(exports, "createAI", { enumerable: true, get: function () { return createAI_js_1.createAI; } });
|
|
6
|
+
var claude_js_1 = require("./claude.js");
|
|
7
|
+
Object.defineProperty(exports, "claude", { enumerable: true, get: function () { return claude_js_1.claude; } });
|
|
8
|
+
var ollama_js_1 = require("./ollama.js");
|
|
9
|
+
Object.defineProperty(exports, "ollama", { enumerable: true, get: function () { return ollama_js_1.ollama; } });
|
|
10
|
+
var extractCodeBlock_js_1 = require("./extractCodeBlock.js");
|
|
11
|
+
Object.defineProperty(exports, "extractCodeBlock", { enumerable: true, get: function () { return extractCodeBlock_js_1.extractCodeBlock; } });
|
|
12
|
+
var extractJson_js_1 = require("./extractJson.js");
|
|
13
|
+
Object.defineProperty(exports, "extractJson", { enumerable: true, get: function () { return extractJson_js_1.extractJson; } });
|
|
14
|
+
var extractTyped_js_1 = require("./extractTyped.js");
|
|
15
|
+
Object.defineProperty(exports, "extractTyped", { enumerable: true, get: function () { return extractTyped_js_1.extractTyped; } });
|
|
16
|
+
var multimodal_js_1 = require("./multimodal.js");
|
|
17
|
+
Object.defineProperty(exports, "extractTextContent", { enumerable: true, get: function () { return multimodal_js_1.extractTextContent; } });
|
|
18
|
+
Object.defineProperty(exports, "toPlainTextMessages", { enumerable: true, get: function () { return multimodal_js_1.toPlainTextMessages; } });
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,6CAAyC;AAAhC,uGAAA,QAAQ,OAAA;AACjB,yCAAqC;AAA5B,mGAAA,MAAM,OAAA;AACf,yCAAqC;AAA5B,mGAAA,MAAM,OAAA;AASf,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/dist/ollama.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ollama.d.ts","sourceRoot":"","sources":["../src/ollama.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAGxC,yFAAyF;AACzF,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAEnD"}
|
package/dist/ollama.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ollama = ollama;
|
|
4
|
+
const ollama_ai_provider_v2_1 = require("ollama-ai-provider-v2");
|
|
5
|
+
/** Creates an Ollama language model. Model names match whatever is installed locally. */
|
|
6
|
+
function ollama(model) {
|
|
7
|
+
return (0, ollama_ai_provider_v2_1.createOllama)()(model);
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=ollama.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ollama.js","sourceRoot":"","sources":["../src/ollama.ts"],"names":[],"mappings":";;AAIA,wBAEC;AALD,iEAAqD;AAErD,yFAAyF;AACzF,SAAgB,MAAM,CAAC,KAAa;IAClC,OAAO,IAAA,oCAAY,GAAE,CAAC,KAAK,CAAC,CAAC;AAC/B,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
export interface Usage {
|
|
3
|
+
inputTokens: number;
|
|
4
|
+
outputTokens: number;
|
|
5
|
+
durationMs: number;
|
|
6
|
+
}
|
|
7
|
+
export interface AITracker {
|
|
8
|
+
record(usage: Usage): void;
|
|
9
|
+
}
|
|
10
|
+
export interface AIOptions {
|
|
11
|
+
maxTokens?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface ChatMessage {
|
|
14
|
+
text: string;
|
|
15
|
+
usage: Usage;
|
|
16
|
+
reply(prompt: string): ChatCall;
|
|
17
|
+
}
|
|
18
|
+
export interface ChatCall extends PromiseLike<ChatMessage> {
|
|
19
|
+
text(): PromiseLike<string>;
|
|
20
|
+
zod<TSchema extends z.ZodType>(schema: TSchema): PromiseLike<z.infer<TSchema>>;
|
|
21
|
+
}
|
|
22
|
+
export interface AI {
|
|
23
|
+
chat(prompt: string, systemPrompt?: string): ChatCall;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,MAAM,WAAW,KAAK;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,SAAS;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;CACjC;AAED,MAAM,WAAW,QAAS,SAAQ,WAAW,CAAC,WAAW,CAAC;IACxD,IAAI,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;IAC5B,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,OAAO,EAC3B,MAAM,EAAE,OAAO,GACd,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,EAAE;IACjB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;CACvD"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hardlydifficult/ai",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"main": "./dist/index.js",
|
|
5
|
+
"types": "./dist/index.d.ts",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"test": "vitest run",
|
|
12
|
+
"test:watch": "vitest",
|
|
13
|
+
"test:coverage": "vitest run --coverage",
|
|
14
|
+
"lint": "tsc --noEmit",
|
|
15
|
+
"clean": "rm -rf dist"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@ai-sdk/anthropic": "3.0.40",
|
|
19
|
+
"@hardlydifficult/logger": "file:../logger",
|
|
20
|
+
"ai": "6.0.78",
|
|
21
|
+
"ollama-ai-provider-v2": "3.0.4",
|
|
22
|
+
"zod": "4.3.6"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/node": "20.19.31",
|
|
26
|
+
"typescript": "5.8.3",
|
|
27
|
+
"vitest": "1.6.1"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"@hardlydifficult/logger": ">=1.0.0"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=18.0.0"
|
|
34
|
+
}
|
|
35
|
+
}
|