@ai-sdk/harness 1.0.156 → 1.0.157
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/CHANGELOG.md +13 -0
- package/dist/utils/index.d.ts +36 -2
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +84 -2
- package/dist/utils/index.js.map +1 -1
- package/package.json +5 -5
- package/src/utils/extract-harness-prompt-content.ts +162 -0
- package/src/utils/index.ts +6 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/harness",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.157",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -44,9 +44,9 @@
|
|
|
44
44
|
}
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@ai-sdk/provider": "4.0.
|
|
48
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
49
|
-
"ai": "7.0.
|
|
47
|
+
"@ai-sdk/provider": "4.0.31",
|
|
48
|
+
"@ai-sdk/provider-utils": "5.0.64",
|
|
49
|
+
"ai": "7.0.146"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"ws": "^8.21.0",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
}
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
|
-
"@ai-sdk/otel": "1.0.
|
|
61
|
+
"@ai-sdk/otel": "1.0.146",
|
|
62
62
|
"@opentelemetry/sdk-trace-base": "2.7.1",
|
|
63
63
|
"@types/node": "22.19.19",
|
|
64
64
|
"@types/ws": "^8.5.13",
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import {
|
|
2
|
+
convertInlineFileDataToUint8Array,
|
|
3
|
+
convertUint8ArrayToBase64,
|
|
4
|
+
detectMediaType,
|
|
5
|
+
getTopLevelMediaType,
|
|
6
|
+
isFullMediaType,
|
|
7
|
+
isProviderReference,
|
|
8
|
+
type FilePart,
|
|
9
|
+
type TextPart,
|
|
10
|
+
} from '@ai-sdk/provider-utils';
|
|
11
|
+
import { z } from 'zod/v4';
|
|
12
|
+
import { HarnessCapabilityUnsupportedError } from '../errors/harness-capability-unsupported-error';
|
|
13
|
+
import type { HarnessV1Prompt } from '../v1/harness-v1-prompt';
|
|
14
|
+
|
|
15
|
+
const harnessPromptContentPartSchema = z.discriminatedUnion('type', [
|
|
16
|
+
z.object({ type: z.literal('text'), text: z.string() }),
|
|
17
|
+
z.object({
|
|
18
|
+
type: z.literal('image'),
|
|
19
|
+
data: z.string(),
|
|
20
|
+
mediaType: z.string(),
|
|
21
|
+
}),
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
export const harnessPromptContentSchema = z.union([
|
|
25
|
+
z.string(),
|
|
26
|
+
z.array(harnessPromptContentPartSchema),
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
export type HarnessPromptContentPart = z.infer<
|
|
30
|
+
typeof harnessPromptContentPartSchema
|
|
31
|
+
>;
|
|
32
|
+
export type HarnessPromptContent = z.infer<typeof harnessPromptContentSchema>;
|
|
33
|
+
|
|
34
|
+
export function extractHarnessPromptContent({
|
|
35
|
+
prompt,
|
|
36
|
+
}: {
|
|
37
|
+
prompt: HarnessV1Prompt;
|
|
38
|
+
}): HarnessPromptContent {
|
|
39
|
+
if (typeof prompt === 'string') return prompt;
|
|
40
|
+
const { content } = prompt;
|
|
41
|
+
if (typeof content === 'string') return content;
|
|
42
|
+
if (content.every((part): part is TextPart => part.type === 'text')) {
|
|
43
|
+
return content.map(part => part.text).join('\n\n');
|
|
44
|
+
}
|
|
45
|
+
return content.flatMap<HarnessPromptContentPart>(part => {
|
|
46
|
+
switch (part.type) {
|
|
47
|
+
case 'text':
|
|
48
|
+
return [{ type: 'text', text: part.text }];
|
|
49
|
+
case 'image':
|
|
50
|
+
return [
|
|
51
|
+
convertUserImage({
|
|
52
|
+
data: part.image,
|
|
53
|
+
mediaType: part.mediaType ?? 'image',
|
|
54
|
+
}),
|
|
55
|
+
];
|
|
56
|
+
case 'file':
|
|
57
|
+
if (getTopLevelMediaType(part.mediaType) !== 'image') {
|
|
58
|
+
throw unsupportedUserContent({
|
|
59
|
+
description: `files of media type '${part.mediaType}'`,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return [convertUserImage(part)];
|
|
63
|
+
default:
|
|
64
|
+
throw unsupportedUserContent({
|
|
65
|
+
description: 'content parts other than text, image, or file parts',
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function convertUserImage({
|
|
72
|
+
data,
|
|
73
|
+
mediaType,
|
|
74
|
+
}: {
|
|
75
|
+
data: FilePart['data'];
|
|
76
|
+
mediaType: string;
|
|
77
|
+
}): Extract<HarnessPromptContentPart, { type: 'image' }> {
|
|
78
|
+
const image = getInlineImage({ data });
|
|
79
|
+
const resolvedMediaType = isFullMediaType(mediaType)
|
|
80
|
+
? mediaType
|
|
81
|
+
: (image.mediaType ??
|
|
82
|
+
detectMediaType({ data: image.data, topLevelType: 'image' }));
|
|
83
|
+
if (resolvedMediaType == null) {
|
|
84
|
+
throw unsupportedUserContent({
|
|
85
|
+
description: 'images whose media type cannot be detected',
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return { type: 'image', data: image.data, mediaType: resolvedMediaType };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function getInlineImage({ data }: { data: FilePart['data'] }): {
|
|
92
|
+
data: string;
|
|
93
|
+
mediaType?: string;
|
|
94
|
+
} {
|
|
95
|
+
if (data instanceof URL) return getDataUrlImage({ url: data });
|
|
96
|
+
if (typeof data === 'string') {
|
|
97
|
+
let url: URL | undefined;
|
|
98
|
+
try {
|
|
99
|
+
url = new URL(data);
|
|
100
|
+
} catch {
|
|
101
|
+
return { data };
|
|
102
|
+
}
|
|
103
|
+
return getDataUrlImage({ url });
|
|
104
|
+
}
|
|
105
|
+
if (data instanceof Uint8Array || data instanceof ArrayBuffer) {
|
|
106
|
+
return {
|
|
107
|
+
data: convertUint8ArrayToBase64(
|
|
108
|
+
convertInlineFileDataToUint8Array({ type: 'data', data }),
|
|
109
|
+
),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (isProviderReference(data)) {
|
|
113
|
+
throw unsupportedUserContent({
|
|
114
|
+
description: 'provider references',
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
switch (data.type) {
|
|
118
|
+
case 'data':
|
|
119
|
+
return typeof data.data === 'string'
|
|
120
|
+
? { data: data.data }
|
|
121
|
+
: getInlineImage({ data: data.data });
|
|
122
|
+
case 'url':
|
|
123
|
+
return getDataUrlImage({ url: data.url });
|
|
124
|
+
case 'reference':
|
|
125
|
+
throw unsupportedUserContent({
|
|
126
|
+
description: 'provider references',
|
|
127
|
+
});
|
|
128
|
+
case 'text':
|
|
129
|
+
throw unsupportedUserContent({
|
|
130
|
+
description: 'text data for images',
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function getDataUrlImage({ url }: { url: URL }): {
|
|
136
|
+
data: string;
|
|
137
|
+
mediaType?: string;
|
|
138
|
+
} {
|
|
139
|
+
if (url.protocol !== 'data:') {
|
|
140
|
+
throw unsupportedUserContent({
|
|
141
|
+
description: 'remote image URLs',
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
const [header, data] = url.href.split(',', 2);
|
|
145
|
+
if (!header?.endsWith(';base64') || data == null) {
|
|
146
|
+
throw unsupportedUserContent({
|
|
147
|
+
description: 'data URLs that are not base64 encoded',
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
const mediaType = header.slice('data:'.length).split(';')[0];
|
|
151
|
+
return { data, ...(mediaType ? { mediaType } : {}) };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function unsupportedUserContent({
|
|
155
|
+
description,
|
|
156
|
+
}: {
|
|
157
|
+
description: string;
|
|
158
|
+
}): HarnessCapabilityUnsupportedError {
|
|
159
|
+
return new HarnessCapabilityUnsupportedError({
|
|
160
|
+
message: `The harness layer does not support ${description} in user messages. Pass text parts and inline images as bytes, base64 strings, or data URLs.`,
|
|
161
|
+
});
|
|
162
|
+
}
|
package/src/utils/index.ts
CHANGED
|
@@ -72,6 +72,12 @@ export { resolveSandboxDefaultWorkingDirectory } from './resolve-sandbox-default
|
|
|
72
72
|
export { getRestrictedSandboxSession } from './get-restricted-sandbox-session';
|
|
73
73
|
export { sleep } from './sleep';
|
|
74
74
|
export { normalizeHarnessToolModelOutput } from './normalize-harness-tool-model-output';
|
|
75
|
+
export {
|
|
76
|
+
harnessPromptContentSchema,
|
|
77
|
+
extractHarnessPromptContent,
|
|
78
|
+
type HarnessPromptContent,
|
|
79
|
+
type HarnessPromptContentPart,
|
|
80
|
+
} from './extract-harness-prompt-content';
|
|
75
81
|
export {
|
|
76
82
|
convertHarnessToolModelOutput,
|
|
77
83
|
type HarnessToolModelOutputContent,
|