@jigs.design/ai-sdk 0.3.0 → 0.3.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/dist/chat.js +2 -1
- package/dist/client.d.ts +2 -1
- package/dist/client.js +3 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/schemas.d.ts +1 -0
- package/dist/schemas.js +1 -0
- package/dist/tool-data.d.ts +48 -0
- package/dist/tool-data.js +172 -0
- package/package.json +5 -5
package/dist/chat.js
CHANGED
|
@@ -204,7 +204,7 @@ async function reviveJigs(jigs, messages) {
|
|
|
204
204
|
...message,
|
|
205
205
|
parts: await Promise.all(message.parts.map(async (part) => {
|
|
206
206
|
if (part.type !== "data-jigs" || !("data" in part)) return part;
|
|
207
|
-
const stored = storedBlock.parse(part.data);
|
|
207
|
+
const { toolKeys: _stored, ...stored } = storedBlock.parse(part.data);
|
|
208
208
|
const compiled = await jigs.compile(stored.source, { strict: false });
|
|
209
209
|
const data = {
|
|
210
210
|
...stored,
|
|
@@ -212,6 +212,7 @@ async function reviveJigs(jigs, messages) {
|
|
|
212
212
|
program: compiled.program,
|
|
213
213
|
status: "done"
|
|
214
214
|
};
|
|
215
|
+
if (compiled.toolKeys !== void 0) data.toolKeys = compiled.toolKeys;
|
|
215
216
|
return {
|
|
216
217
|
...part,
|
|
217
218
|
data
|
package/dist/client.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
import { JigsBlockData, JigsDataTypes, JigsUIMessage, StoredJigsBlock, StoredJigsUIMessage, recentMessages, withoutPrograms } from "./stored.js";
|
|
2
|
-
|
|
2
|
+
import { ToolDataOptions, ToolPart, toolData, toolValue } from "./tool-data.js";
|
|
3
|
+
export { type JigsBlockData, type JigsDataTypes, type JigsUIMessage, type StoredJigsBlock, type StoredJigsUIMessage, type ToolDataOptions, type ToolPart, recentMessages, toolData, toolValue, withoutPrograms };
|
package/dist/client.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { JigsBlockData, JigsDataTypes, JigsUIMessage, StoredJigsBlock, StoredJigsUIMessage, recentMessages, withoutPrograms } from "./stored.js";
|
|
2
|
+
import { ToolDataOptions, ToolPart, toolData, toolValue } from "./tool-data.js";
|
|
2
3
|
import { JigsChat, jigsChat, reviveJigs } from "./chat.js";
|
|
3
4
|
import { ModelSettings, aiSdkModel, cachedInstructions } from "./model.js";
|
|
4
5
|
import { jigsDataSchemas } from "./schemas.js";
|
|
5
|
-
export { type JigsBlockData, type JigsChat, type JigsDataTypes, type JigsUIMessage, type ModelSettings, type StoredJigsBlock, type StoredJigsUIMessage, aiSdkModel, cachedInstructions, jigsChat, jigsDataSchemas, recentMessages, reviveJigs, withoutPrograms };
|
|
6
|
+
export { type JigsBlockData, type JigsChat, type JigsDataTypes, type JigsUIMessage, type ModelSettings, type StoredJigsBlock, type StoredJigsUIMessage, type ToolDataOptions, type ToolPart, aiSdkModel, cachedInstructions, jigsChat, jigsDataSchemas, recentMessages, reviveJigs, toolData, toolValue, withoutPrograms };
|
package/dist/index.js
CHANGED
|
@@ -2,4 +2,5 @@ import { jigsDataSchemas } from "./schemas.js";
|
|
|
2
2
|
import { jigsChat, reviveJigs } from "./chat.js";
|
|
3
3
|
import { aiSdkModel, cachedInstructions } from "./model.js";
|
|
4
4
|
import { recentMessages, withoutPrograms } from "./stored.js";
|
|
5
|
-
|
|
5
|
+
import { toolData, toolValue } from "./tool-data.js";
|
|
6
|
+
export { aiSdkModel, cachedInstructions, jigsChat, jigsDataSchemas, recentMessages, reviveJigs, toolData, toolValue, withoutPrograms };
|
package/dist/schemas.d.ts
CHANGED
package/dist/schemas.js
CHANGED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { JsonValue } from "@jigs.design/protocol";
|
|
2
|
+
import { DynamicToolUIPart, ToolUIPart, UIMessage } from "ai";
|
|
3
|
+
//#region src/tool-data.d.ts
|
|
4
|
+
/** A tool call in a message: a `tool-<name>` part, or a `dynamic-tool` one. */
|
|
5
|
+
export type ToolPart = DynamicToolUIPart | ToolUIPart;
|
|
6
|
+
type JsonObject = {
|
|
7
|
+
readonly [key: string]: JsonValue;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* A tool part's output as a block reads it: what the model was shown, as
|
|
11
|
+
* JSON. Undefined for no value: a call not finished (a preliminary output
|
|
12
|
+
* too, which the model never sees), one that failed or was refused, an MCP
|
|
13
|
+
* result with `isError`, or an output over 32 KB of JSON. An output is read
|
|
14
|
+
* as JSON, as it crossed the wire. A tool from an MCP client
|
|
15
|
+
* (`toolMetadata.clientName`, which `@ai-sdk/mcp` sets) is read as
|
|
16
|
+
* `mcpToModelOutput` shows it: its structured content, else its text parts,
|
|
17
|
+
* parsed when there is one and it is JSON.
|
|
18
|
+
*/
|
|
19
|
+
export declare function toolValue(part: ToolPart): JsonValue | undefined;
|
|
20
|
+
export interface ToolDataOptions {
|
|
21
|
+
/**
|
|
22
|
+
* A tool's value for a block instead of `toolValue`'s, for an app tool
|
|
23
|
+
* whose `toModelOutput` shows the model a different shape from its
|
|
24
|
+
* output; undefined for no value. Keep the same function for a page:
|
|
25
|
+
* results are reused by it.
|
|
26
|
+
*/
|
|
27
|
+
value?: (toolName: string, part: ToolPart) => JsonValue | undefined;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Each jig's provided data from the tool calls of the conversation, by
|
|
31
|
+
* block key, for `JigsView`'s `data` on a server with `toolResults: true`:
|
|
32
|
+
* `<JigsView data={{ ...hostData, ...tools.get(block.key) }} … />`. Only a
|
|
33
|
+
* block that reads the slot (`ChatBlock.toolKeys`) has an entry, cut to the
|
|
34
|
+
* tools it reads.
|
|
35
|
+
*
|
|
36
|
+
* A block version reads, for each tool: the latest value before it in the
|
|
37
|
+
* reply that wrote it; else its previous version's value; else the latest
|
|
38
|
+
* value earlier in the conversation. A failed or refused call in that reply
|
|
39
|
+
* removes the tool's entry, so the block shows its empty state rather than
|
|
40
|
+
* an older result. A tool called twice in one step leaves the last result.
|
|
41
|
+
* While a block's results are the same, it gets the same object, so the view
|
|
42
|
+
* does not compare them again.
|
|
43
|
+
*/
|
|
44
|
+
export declare function toolData<TMessage extends UIMessage>(messages: ReadonlyArray<TMessage>, options?: ToolDataOptions): ReadonlyMap<string, {
|
|
45
|
+
toolResults: JsonObject;
|
|
46
|
+
}>;
|
|
47
|
+
//#endregion
|
|
48
|
+
export {};
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
//#region src/tool-data.ts
|
|
2
|
+
/** The most JSON one tool's value may take before a block gets none of it. */
|
|
3
|
+
const MAX_VALUE_CHARS = 32768;
|
|
4
|
+
function isList(value) {
|
|
5
|
+
return Array.isArray(value);
|
|
6
|
+
}
|
|
7
|
+
function isObject(value) {
|
|
8
|
+
return typeof value === "object" && value !== null && !isList(value);
|
|
9
|
+
}
|
|
10
|
+
function isText(value) {
|
|
11
|
+
return typeof value === "string";
|
|
12
|
+
}
|
|
13
|
+
/** JSON text read back; the caller made it, so it is JSON. */
|
|
14
|
+
function parsed(text) {
|
|
15
|
+
return JSON.parse(text);
|
|
16
|
+
}
|
|
17
|
+
/** The tool a part called, or null for a part that is not a tool call. */
|
|
18
|
+
function toolNameOf(part) {
|
|
19
|
+
if (part.type === "dynamic-tool") return part.toolName;
|
|
20
|
+
return part.type.startsWith("tool-") ? part.type.slice(5) : null;
|
|
21
|
+
}
|
|
22
|
+
function isToolPart(part) {
|
|
23
|
+
return toolNameOf(part) !== null && "state" in part;
|
|
24
|
+
}
|
|
25
|
+
/** A block's `data-jigs` part, with its data as `jigsChat` sent it. */
|
|
26
|
+
function isBlockPart(part) {
|
|
27
|
+
return part.type === "data-jigs" && "data" in part && typeof part.data === "object" && part.data !== null && "key" in part.data && "name" in part.data;
|
|
28
|
+
}
|
|
29
|
+
/** What `mcpToModelOutput` (`@ai-sdk/mcp`) shows the model of one content part. */
|
|
30
|
+
function contentText(part) {
|
|
31
|
+
if (!isObject(part)) return null;
|
|
32
|
+
if (part.type === "text" && isText(part.text)) return part.text;
|
|
33
|
+
return part.type === "image" ? null : JSON.stringify(part);
|
|
34
|
+
}
|
|
35
|
+
/** An MCP tool's `CallToolResult` as the model was shown it. */
|
|
36
|
+
function mcpValue(output) {
|
|
37
|
+
if (!isObject(output) || output.isError === true) return;
|
|
38
|
+
const structured = output.structuredContent;
|
|
39
|
+
if (structured !== void 0) return structured;
|
|
40
|
+
const content = output.content;
|
|
41
|
+
if (content === void 0 || !isList(content)) return output;
|
|
42
|
+
const texts = content.flatMap((part) => {
|
|
43
|
+
const text = contentText(part);
|
|
44
|
+
return text === null ? [] : [text];
|
|
45
|
+
});
|
|
46
|
+
const [only] = texts;
|
|
47
|
+
if (texts.length === 1 && only !== void 0) try {
|
|
48
|
+
return parsed(only);
|
|
49
|
+
} catch {
|
|
50
|
+
return only;
|
|
51
|
+
}
|
|
52
|
+
return texts.length === 0 ? void 0 : texts.join("\n");
|
|
53
|
+
}
|
|
54
|
+
/** Values already worked out, by part; null for none. */
|
|
55
|
+
const values = /* @__PURE__ */ new WeakMap();
|
|
56
|
+
/**
|
|
57
|
+
* A tool part's output as a block reads it: what the model was shown, as
|
|
58
|
+
* JSON. Undefined for no value: a call not finished (a preliminary output
|
|
59
|
+
* too, which the model never sees), one that failed or was refused, an MCP
|
|
60
|
+
* result with `isError`, or an output over 32 KB of JSON. An output is read
|
|
61
|
+
* as JSON, as it crossed the wire. A tool from an MCP client
|
|
62
|
+
* (`toolMetadata.clientName`, which `@ai-sdk/mcp` sets) is read as
|
|
63
|
+
* `mcpToModelOutput` shows it: its structured content, else its text parts,
|
|
64
|
+
* parsed when there is one and it is JSON.
|
|
65
|
+
*/
|
|
66
|
+
function toolValue(part) {
|
|
67
|
+
if (part.state !== "output-available" || part.preliminary === true) return;
|
|
68
|
+
const known = values.get(part);
|
|
69
|
+
if (known !== void 0) return known ?? void 0;
|
|
70
|
+
const text = JSON.stringify(part.output);
|
|
71
|
+
let value;
|
|
72
|
+
if (text !== void 0 && text.length <= MAX_VALUE_CHARS) {
|
|
73
|
+
const output = parsed(text);
|
|
74
|
+
value = part.toolMetadata?.clientName === void 0 ? output : mcpValue(output);
|
|
75
|
+
}
|
|
76
|
+
values.set(part, value ?? null);
|
|
77
|
+
return value;
|
|
78
|
+
}
|
|
79
|
+
/** What a finished tool part does to a snapshot: sets its tool's value, or removes it. */
|
|
80
|
+
function apply(snapshot, part, value) {
|
|
81
|
+
const name = toolNameOf(part) ?? "";
|
|
82
|
+
if (value === void 0) snapshot.delete(name);
|
|
83
|
+
else snapshot.set(name, {
|
|
84
|
+
id: `${part.toolCallId}:${part.state}`,
|
|
85
|
+
value
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/** Whether a part settles its tool's value: a final output, an error or a refusal. */
|
|
89
|
+
const settles = (part) => part.state === "output-available" && part.preliminary !== true || part.state === "output-error" || part.state === "output-denied";
|
|
90
|
+
/** `snapshot` cut to `keys`, as the record a block reads. */
|
|
91
|
+
function project(snapshot, keys) {
|
|
92
|
+
const names = (keys === "all" ? [...snapshot.keys()] : keys).toSorted();
|
|
93
|
+
const record = {};
|
|
94
|
+
const ids = [];
|
|
95
|
+
for (const name of names) {
|
|
96
|
+
const entry = snapshot.get(name);
|
|
97
|
+
if (entry !== void 0) {
|
|
98
|
+
record[name] = entry.value;
|
|
99
|
+
ids.push(`${name}=${entry.id}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
fingerprint: ids.join("\n"),
|
|
104
|
+
record
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
/** A block's results as last passed, by its block data, for each `value` function. */
|
|
108
|
+
const remembered = /* @__PURE__ */ new WeakMap();
|
|
109
|
+
const DEFAULT_VALUE = {};
|
|
110
|
+
/**
|
|
111
|
+
* Each jig's provided data from the tool calls of the conversation, by
|
|
112
|
+
* block key, for `JigsView`'s `data` on a server with `toolResults: true`:
|
|
113
|
+
* `<JigsView data={{ ...hostData, ...tools.get(block.key) }} … />`. Only a
|
|
114
|
+
* block that reads the slot (`ChatBlock.toolKeys`) has an entry, cut to the
|
|
115
|
+
* tools it reads.
|
|
116
|
+
*
|
|
117
|
+
* A block version reads, for each tool: the latest value before it in the
|
|
118
|
+
* reply that wrote it; else its previous version's value; else the latest
|
|
119
|
+
* value earlier in the conversation. A failed or refused call in that reply
|
|
120
|
+
* removes the tool's entry, so the block shows its empty state rather than
|
|
121
|
+
* an older result. A tool called twice in one step leaves the last result.
|
|
122
|
+
* While a block's results are the same, it gets the same object, so the view
|
|
123
|
+
* does not compare them again.
|
|
124
|
+
*/
|
|
125
|
+
function toolData(messages, options = {}) {
|
|
126
|
+
const valueOf = options.value;
|
|
127
|
+
const bucket = valueOf ?? DEFAULT_VALUE;
|
|
128
|
+
const cache = remembered.get(bucket) ?? /* @__PURE__ */ new WeakMap();
|
|
129
|
+
remembered.set(bucket, cache);
|
|
130
|
+
const result = /* @__PURE__ */ new Map();
|
|
131
|
+
const conversation = /* @__PURE__ */ new Map();
|
|
132
|
+
const versions = /* @__PURE__ */ new Map();
|
|
133
|
+
for (const message of messages) {
|
|
134
|
+
const before = new Map(conversation);
|
|
135
|
+
/** The settled tool calls of this message so far, in order. */
|
|
136
|
+
const settled = [];
|
|
137
|
+
for (const part of message.parts) {
|
|
138
|
+
if (isToolPart(part)) {
|
|
139
|
+
if (settles(part)) {
|
|
140
|
+
const value = valueOf === void 0 ? toolValue(part) : valueOf(toolNameOf(part) ?? "", part);
|
|
141
|
+
settled.push({
|
|
142
|
+
part,
|
|
143
|
+
value
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (!isBlockPart(part)) continue;
|
|
149
|
+
const block = part.data;
|
|
150
|
+
const snapshot = new Map(versions.get(block.name) ?? before);
|
|
151
|
+
for (const entry of settled) apply(snapshot, entry.part, entry.value);
|
|
152
|
+
versions.set(block.name, snapshot);
|
|
153
|
+
if (block.toolKeys === void 0) continue;
|
|
154
|
+
const projected = project(snapshot, block.toolKeys);
|
|
155
|
+
const known = cache.get(block);
|
|
156
|
+
if (known !== void 0 && known.fingerprint === projected.fingerprint) {
|
|
157
|
+
result.set(block.key, known.data);
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const data = { toolResults: projected.record };
|
|
161
|
+
cache.set(block, {
|
|
162
|
+
...projected,
|
|
163
|
+
data
|
|
164
|
+
});
|
|
165
|
+
result.set(block.key, data);
|
|
166
|
+
}
|
|
167
|
+
for (const entry of settled) apply(conversation, entry.part, entry.value);
|
|
168
|
+
}
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
//#endregion
|
|
172
|
+
export { toolData, toolValue };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jigs.design/ai-sdk",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Jigs on the AI SDK: a model function for createJigsServer, and chat mode for useChat.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"access": "public"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@jigs.design/protocol": "0.3.
|
|
33
|
-
"@jigs.design/server": "0.3.
|
|
32
|
+
"@jigs.design/protocol": "0.3.2",
|
|
33
|
+
"@jigs.design/server": "0.3.2"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"ai": "^7.0.0",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"@ai-sdk/provider": "4.0.27",
|
|
41
41
|
"ai": "7.0.140",
|
|
42
42
|
"zod": "4.6.7",
|
|
43
|
-
"@jigs.design/catalog": "0.3.
|
|
44
|
-
"@jigs.design/host": "0.3.
|
|
43
|
+
"@jigs.design/catalog": "0.3.2",
|
|
44
|
+
"@jigs.design/host": "0.3.2"
|
|
45
45
|
}
|
|
46
46
|
}
|