cloudassist-ai-provider 0.0.1
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 +1901 -0
- package/README.md +35 -0
- package/dist/convert-json-schema-to-openapi-schema.d.ts +6 -0
- package/dist/convert-json-schema-to-openapi-schema.d.ts.map +1 -0
- package/dist/convert-json-schema-to-openapi-schema.js +108 -0
- package/dist/convert-json-schema-to-openapi-schema.test.d.ts +2 -0
- package/dist/convert-json-schema-to-openapi-schema.test.d.ts.map +1 -0
- package/dist/convert-json-schema-to-openapi-schema.test.js +630 -0
- package/dist/convert-to-google-generative-ai-messages.d.ts +7 -0
- package/dist/convert-to-google-generative-ai-messages.d.ts.map +1 -0
- package/dist/convert-to-google-generative-ai-messages.js +195 -0
- package/dist/convert-to-google-generative-ai-messages.test.d.ts +2 -0
- package/dist/convert-to-google-generative-ai-messages.test.d.ts.map +1 -0
- package/dist/convert-to-google-generative-ai-messages.test.js +456 -0
- package/dist/get-model-path.d.ts +2 -0
- package/dist/get-model-path.d.ts.map +1 -0
- package/dist/get-model-path.js +3 -0
- package/dist/get-model-path.test.d.ts +2 -0
- package/dist/get-model-path.test.d.ts.map +1 -0
- package/dist/get-model-path.test.js +11 -0
- package/dist/google-error.d.ts +12 -0
- package/dist/google-error.d.ts.map +1 -0
- package/dist/google-error.js +13 -0
- package/dist/google-generative-ai-embedding-model.d.ts +21 -0
- package/dist/google-generative-ai-embedding-model.d.ts.map +1 -0
- package/dist/google-generative-ai-embedding-model.js +88 -0
- package/dist/google-generative-ai-embedding-model.test.d.ts +2 -0
- package/dist/google-generative-ai-embedding-model.test.d.ts.map +1 -0
- package/dist/google-generative-ai-embedding-model.test.js +148 -0
- package/dist/google-generative-ai-embedding-options.d.ts +8 -0
- package/dist/google-generative-ai-embedding-options.d.ts.map +1 -0
- package/dist/google-generative-ai-embedding-options.js +33 -0
- package/dist/google-generative-ai-image-model.d.ts +31 -0
- package/dist/google-generative-ai-image-model.d.ts.map +1 -0
- package/dist/google-generative-ai-image-model.js +96 -0
- package/dist/google-generative-ai-image-model.test.d.ts +2 -0
- package/dist/google-generative-ai-image-model.test.d.ts.map +1 -0
- package/dist/google-generative-ai-image-model.test.js +252 -0
- package/dist/google-generative-ai-image-settings.d.ts +8 -0
- package/dist/google-generative-ai-image-settings.d.ts.map +1 -0
- package/dist/google-generative-ai-image-settings.js +1 -0
- package/dist/google-generative-ai-language-model.d.ts +183 -0
- package/dist/google-generative-ai-language-model.d.ts.map +1 -0
- package/dist/google-generative-ai-language-model.js +1001 -0
- package/dist/google-generative-ai-language-model.test.d.ts +2 -0
- package/dist/google-generative-ai-language-model.test.d.ts.map +1 -0
- package/dist/google-generative-ai-language-model.test.js +3463 -0
- package/dist/google-generative-ai-options.d.ts +37 -0
- package/dist/google-generative-ai-options.d.ts.map +1 -0
- package/dist/google-generative-ai-options.js +149 -0
- package/dist/google-generative-ai-prompt.d.ts +52 -0
- package/dist/google-generative-ai-prompt.d.ts.map +1 -0
- package/dist/google-generative-ai-prompt.js +1 -0
- package/dist/google-prepare-tools.d.ts +27 -0
- package/dist/google-prepare-tools.d.ts.map +1 -0
- package/dist/google-prepare-tools.js +219 -0
- package/dist/google-prepare-tools.test.d.ts +2 -0
- package/dist/google-prepare-tools.test.d.ts.map +1 -0
- package/dist/google-prepare-tools.test.js +447 -0
- package/dist/google-provider.d.ts +65 -0
- package/dist/google-provider.d.ts.map +1 -0
- package/dist/google-provider.js +74 -0
- package/dist/google-provider.test.d.ts +2 -0
- package/dist/google-provider.test.d.ts.map +1 -0
- package/dist/google-provider.test.js +234 -0
- package/dist/google-supported-file-url.d.ts +2 -0
- package/dist/google-supported-file-url.d.ts.map +1 -0
- package/dist/google-supported-file-url.js +13 -0
- package/dist/google-supported-file-url.test.d.ts +2 -0
- package/dist/google-supported-file-url.test.d.ts.map +1 -0
- package/dist/google-supported-file-url.test.js +45 -0
- package/dist/google-tools.d.ts +76 -0
- package/dist/google-tools.d.ts.map +1 -0
- package/dist/google-tools.js +65 -0
- package/dist/index.d.mts +326 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2172 -0
- package/dist/index.mjs.map +1 -0
- package/dist/internal/index.d.mts +262 -0
- package/dist/internal/index.d.ts +4 -0
- package/dist/internal/index.d.ts.map +1 -0
- package/dist/internal/index.js +2 -0
- package/dist/internal/index.js.map +1 -0
- package/dist/internal/index.mjs +1810 -0
- package/dist/internal/index.mjs.map +1 -0
- package/dist/map-google-generative-ai-finish-reason.d.ts +6 -0
- package/dist/map-google-generative-ai-finish-reason.d.ts.map +1 -0
- package/dist/map-google-generative-ai-finish-reason.js +22 -0
- package/dist/tool/code-execution.d.ts +17 -0
- package/dist/tool/code-execution.d.ts.map +1 -0
- package/dist/tool/code-execution.js +25 -0
- package/dist/tool/enterprise-web-search.d.ts +2 -0
- package/dist/tool/enterprise-web-search.d.ts.map +1 -0
- package/dist/tool/enterprise-web-search.js +8 -0
- package/dist/tool/file-search.d.ts +16 -0
- package/dist/tool/file-search.d.ts.map +1 -0
- package/dist/tool/file-search.js +33 -0
- package/dist/tool/google-maps.d.ts +2 -0
- package/dist/tool/google-maps.d.ts.map +1 -0
- package/dist/tool/google-maps.js +9 -0
- package/dist/tool/google-search.d.ts +14 -0
- package/dist/tool/google-search.d.ts.map +1 -0
- package/dist/tool/google-search.js +15 -0
- package/dist/tool/url-context.d.ts +2 -0
- package/dist/tool/url-context.d.ts.map +1 -0
- package/dist/tool/url-context.js +7 -0
- package/dist/tool/vertex-rag-store.d.ts +16 -0
- package/dist/tool/vertex-rag-store.d.ts.map +1 -0
- package/dist/tool/vertex-rag-store.js +17 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +3 -0
- package/internal.d.ts +1 -0
- package/package.json +73 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { UnsupportedFunctionalityError, } from '@ai-sdk/provider';
|
|
2
|
+
import { convertToBase64 } from '@ai-sdk/provider-utils';
|
|
3
|
+
import { isClaudeModel } from './google-prepare-tools';
|
|
4
|
+
export function convertToGoogleGenerativeAIMessages(prompt, options) {
|
|
5
|
+
var _a, _b;
|
|
6
|
+
const systemInstructionParts = [];
|
|
7
|
+
const contents = [];
|
|
8
|
+
let systemMessagesAllowed = true;
|
|
9
|
+
const isGemmaModel = (_a = options === null || options === void 0 ? void 0 : options.isGemmaModel) !== null && _a !== void 0 ? _a : false;
|
|
10
|
+
const modelId = (_b = options === null || options === void 0 ? void 0 : options.modelId) !== null && _b !== void 0 ? _b : '';
|
|
11
|
+
const includeToolCallId = isClaudeModel(modelId);
|
|
12
|
+
for (const { role, content } of prompt) {
|
|
13
|
+
switch (role) {
|
|
14
|
+
case 'system': {
|
|
15
|
+
if (!systemMessagesAllowed) {
|
|
16
|
+
throw new UnsupportedFunctionalityError({
|
|
17
|
+
functionality: 'system messages are only supported at the beginning of the conversation',
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
systemInstructionParts.push({ text: content });
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
case 'user': {
|
|
24
|
+
systemMessagesAllowed = false;
|
|
25
|
+
const parts = [];
|
|
26
|
+
for (const part of content) {
|
|
27
|
+
switch (part.type) {
|
|
28
|
+
case 'text': {
|
|
29
|
+
parts.push({ text: part.text });
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
case 'file': {
|
|
33
|
+
// default to image/jpeg for unknown image/* types
|
|
34
|
+
const mediaType = part.mediaType === 'image/*' ? 'image/jpeg' : part.mediaType;
|
|
35
|
+
parts.push(part.data instanceof URL
|
|
36
|
+
? {
|
|
37
|
+
fileData: {
|
|
38
|
+
mimeType: mediaType,
|
|
39
|
+
fileUri: part.data.toString(),
|
|
40
|
+
},
|
|
41
|
+
}
|
|
42
|
+
: {
|
|
43
|
+
inlineData: {
|
|
44
|
+
mimeType: mediaType,
|
|
45
|
+
data: convertToBase64(part.data),
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
contents.push({ role: 'user', parts });
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
case 'assistant': {
|
|
56
|
+
systemMessagesAllowed = false;
|
|
57
|
+
contents.push({
|
|
58
|
+
role: 'model',
|
|
59
|
+
parts: content
|
|
60
|
+
.map(part => {
|
|
61
|
+
var _a, _b, _c;
|
|
62
|
+
const thoughtSignature = ((_b = (_a = part.providerOptions) === null || _a === void 0 ? void 0 : _a.google) === null || _b === void 0 ? void 0 : _b.thoughtSignature) != null
|
|
63
|
+
? String((_c = part.providerOptions.google) === null || _c === void 0 ? void 0 : _c.thoughtSignature)
|
|
64
|
+
: undefined;
|
|
65
|
+
switch (part.type) {
|
|
66
|
+
case 'text': {
|
|
67
|
+
return part.text.length === 0
|
|
68
|
+
? undefined
|
|
69
|
+
: {
|
|
70
|
+
text: part.text,
|
|
71
|
+
thoughtSignature,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
case 'reasoning': {
|
|
75
|
+
return part.text.length === 0
|
|
76
|
+
? undefined
|
|
77
|
+
: {
|
|
78
|
+
text: part.text,
|
|
79
|
+
thought: true,
|
|
80
|
+
thoughtSignature,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
case 'file': {
|
|
84
|
+
if (part.mediaType !== 'image/png') {
|
|
85
|
+
throw new UnsupportedFunctionalityError({
|
|
86
|
+
functionality: 'Only PNG images are supported in assistant messages',
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
if (part.data instanceof URL) {
|
|
90
|
+
throw new UnsupportedFunctionalityError({
|
|
91
|
+
functionality: 'File data URLs in assistant messages are not supported',
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
inlineData: {
|
|
96
|
+
mimeType: part.mediaType,
|
|
97
|
+
data: convertToBase64(part.data),
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
case 'tool-call': {
|
|
102
|
+
return {
|
|
103
|
+
functionCall: {
|
|
104
|
+
name: part.toolName,
|
|
105
|
+
args: part.input,
|
|
106
|
+
...(includeToolCallId && part.toolCallId
|
|
107
|
+
? { id: part.toolCallId }
|
|
108
|
+
: {}),
|
|
109
|
+
},
|
|
110
|
+
thoughtSignature,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
.filter(part => part !== undefined),
|
|
116
|
+
});
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
case 'tool': {
|
|
120
|
+
systemMessagesAllowed = false;
|
|
121
|
+
const parts = [];
|
|
122
|
+
for (const part of content) {
|
|
123
|
+
const output = part.output;
|
|
124
|
+
if (output.type === 'content') {
|
|
125
|
+
for (const contentPart of output.value) {
|
|
126
|
+
switch (contentPart.type) {
|
|
127
|
+
case 'text':
|
|
128
|
+
parts.push({
|
|
129
|
+
functionResponse: {
|
|
130
|
+
name: part.toolName,
|
|
131
|
+
response: {
|
|
132
|
+
name: part.toolName,
|
|
133
|
+
content: contentPart.text,
|
|
134
|
+
},
|
|
135
|
+
...(includeToolCallId && part.toolCallId
|
|
136
|
+
? { id: part.toolCallId }
|
|
137
|
+
: {}),
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
break;
|
|
141
|
+
case 'media':
|
|
142
|
+
parts.push({
|
|
143
|
+
inlineData: {
|
|
144
|
+
mimeType: contentPart.mediaType,
|
|
145
|
+
data: contentPart.data,
|
|
146
|
+
},
|
|
147
|
+
}, {
|
|
148
|
+
text: 'Tool executed successfully and returned this image as a response',
|
|
149
|
+
});
|
|
150
|
+
break;
|
|
151
|
+
default:
|
|
152
|
+
parts.push({ text: JSON.stringify(contentPart) });
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
parts.push({
|
|
159
|
+
functionResponse: {
|
|
160
|
+
name: part.toolName,
|
|
161
|
+
response: {
|
|
162
|
+
name: part.toolName,
|
|
163
|
+
content: output.value,
|
|
164
|
+
},
|
|
165
|
+
...(includeToolCallId && part.toolCallId
|
|
166
|
+
? { id: part.toolCallId }
|
|
167
|
+
: {}),
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
contents.push({
|
|
173
|
+
role: 'user',
|
|
174
|
+
parts,
|
|
175
|
+
});
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (isGemmaModel &&
|
|
181
|
+
systemInstructionParts.length > 0 &&
|
|
182
|
+
contents.length > 0 &&
|
|
183
|
+
contents[0].role === 'user') {
|
|
184
|
+
const systemText = systemInstructionParts
|
|
185
|
+
.map(part => part.text)
|
|
186
|
+
.join('\n\n');
|
|
187
|
+
contents[0].parts.unshift({ text: systemText + '\n\n' });
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
systemInstruction: systemInstructionParts.length > 0 && !isGemmaModel
|
|
191
|
+
? { parts: systemInstructionParts }
|
|
192
|
+
: undefined,
|
|
193
|
+
contents,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"convert-to-google-generative-ai-messages.test.d.ts","sourceRoot":"","sources":["../src/convert-to-google-generative-ai-messages.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
import { convertToGoogleGenerativeAIMessages } from './convert-to-google-generative-ai-messages';
|
|
2
|
+
import { describe, it, expect } from 'vitest';
|
|
3
|
+
describe('system messages', () => {
|
|
4
|
+
it('should store system message in system instruction', async () => {
|
|
5
|
+
const result = convertToGoogleGenerativeAIMessages([
|
|
6
|
+
{ role: 'system', content: 'Test' },
|
|
7
|
+
]);
|
|
8
|
+
expect(result).toEqual({
|
|
9
|
+
systemInstruction: { parts: [{ text: 'Test' }] },
|
|
10
|
+
contents: [],
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
it('should throw error when there was already a user message', async () => {
|
|
14
|
+
expect(() => convertToGoogleGenerativeAIMessages([
|
|
15
|
+
{ role: 'user', content: [{ type: 'text', text: 'Test' }] },
|
|
16
|
+
{ role: 'system', content: 'Test' },
|
|
17
|
+
])).toThrow('system messages are only supported at the beginning of the conversation');
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
describe('thought signatures', () => {
|
|
21
|
+
it('should preserve thought signatures in assistant messages', async () => {
|
|
22
|
+
const result = convertToGoogleGenerativeAIMessages([
|
|
23
|
+
{
|
|
24
|
+
role: 'assistant',
|
|
25
|
+
content: [
|
|
26
|
+
{
|
|
27
|
+
type: 'text',
|
|
28
|
+
text: 'Regular text',
|
|
29
|
+
providerOptions: { google: { thoughtSignature: 'sig1' } },
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
type: 'reasoning',
|
|
33
|
+
text: 'Reasoning text',
|
|
34
|
+
providerOptions: { google: { thoughtSignature: 'sig2' } },
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
type: 'tool-call',
|
|
38
|
+
toolCallId: 'call1',
|
|
39
|
+
toolName: 'test',
|
|
40
|
+
input: { value: 'test' },
|
|
41
|
+
providerOptions: { google: { thoughtSignature: 'sig3' } },
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
]);
|
|
46
|
+
expect(result).toMatchInlineSnapshot(`
|
|
47
|
+
{
|
|
48
|
+
"contents": [
|
|
49
|
+
{
|
|
50
|
+
"parts": [
|
|
51
|
+
{
|
|
52
|
+
"text": "Regular text",
|
|
53
|
+
"thoughtSignature": "sig1",
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"text": "Reasoning text",
|
|
57
|
+
"thought": true,
|
|
58
|
+
"thoughtSignature": "sig2",
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"functionCall": {
|
|
62
|
+
"args": {
|
|
63
|
+
"value": "test",
|
|
64
|
+
},
|
|
65
|
+
"name": "test",
|
|
66
|
+
},
|
|
67
|
+
"thoughtSignature": "sig3",
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
"role": "model",
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
"systemInstruction": undefined,
|
|
74
|
+
}
|
|
75
|
+
`);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
describe('Gemma model system instructions', () => {
|
|
79
|
+
it('should prepend system instruction to first user message for Gemma models', async () => {
|
|
80
|
+
const result = convertToGoogleGenerativeAIMessages([
|
|
81
|
+
{ role: 'system', content: 'You are a helpful assistant.' },
|
|
82
|
+
{ role: 'user', content: [{ type: 'text', text: 'Hello' }] },
|
|
83
|
+
], { isGemmaModel: true });
|
|
84
|
+
expect(result).toMatchInlineSnapshot(`
|
|
85
|
+
{
|
|
86
|
+
"contents": [
|
|
87
|
+
{
|
|
88
|
+
"parts": [
|
|
89
|
+
{
|
|
90
|
+
"text": "You are a helpful assistant.
|
|
91
|
+
|
|
92
|
+
",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"text": "Hello",
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
"role": "user",
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
"systemInstruction": undefined,
|
|
102
|
+
}
|
|
103
|
+
`);
|
|
104
|
+
});
|
|
105
|
+
it('should handle multiple system messages for Gemma models', async () => {
|
|
106
|
+
const result = convertToGoogleGenerativeAIMessages([
|
|
107
|
+
{ role: 'system', content: 'You are helpful.' },
|
|
108
|
+
{ role: 'system', content: 'Be concise.' },
|
|
109
|
+
{ role: 'user', content: [{ type: 'text', text: 'Hi' }] },
|
|
110
|
+
], { isGemmaModel: true });
|
|
111
|
+
expect(result).toMatchInlineSnapshot(`
|
|
112
|
+
{
|
|
113
|
+
"contents": [
|
|
114
|
+
{
|
|
115
|
+
"parts": [
|
|
116
|
+
{
|
|
117
|
+
"text": "You are helpful.
|
|
118
|
+
|
|
119
|
+
Be concise.
|
|
120
|
+
|
|
121
|
+
",
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"text": "Hi",
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
"role": "user",
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
"systemInstruction": undefined,
|
|
131
|
+
}
|
|
132
|
+
`);
|
|
133
|
+
});
|
|
134
|
+
it('should not affect non-Gemma models', async () => {
|
|
135
|
+
const result = convertToGoogleGenerativeAIMessages([
|
|
136
|
+
{ role: 'system', content: 'You are helpful.' },
|
|
137
|
+
{ role: 'user', content: [{ type: 'text', text: 'Hello' }] },
|
|
138
|
+
], { isGemmaModel: false });
|
|
139
|
+
expect(result).toMatchInlineSnapshot(`
|
|
140
|
+
{
|
|
141
|
+
"contents": [
|
|
142
|
+
{
|
|
143
|
+
"parts": [
|
|
144
|
+
{
|
|
145
|
+
"text": "Hello",
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
"role": "user",
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
"systemInstruction": {
|
|
152
|
+
"parts": [
|
|
153
|
+
{
|
|
154
|
+
"text": "You are helpful.",
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
},
|
|
158
|
+
}
|
|
159
|
+
`);
|
|
160
|
+
});
|
|
161
|
+
it('should handle Gemma model with system instruction but no user messages', async () => {
|
|
162
|
+
const result = convertToGoogleGenerativeAIMessages([{ role: 'system', content: 'You are helpful.' }], { isGemmaModel: true });
|
|
163
|
+
expect(result).toMatchInlineSnapshot(`
|
|
164
|
+
{
|
|
165
|
+
"contents": [],
|
|
166
|
+
"systemInstruction": undefined,
|
|
167
|
+
}
|
|
168
|
+
`);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
describe('user messages', () => {
|
|
172
|
+
it('should add image parts', async () => {
|
|
173
|
+
const result = convertToGoogleGenerativeAIMessages([
|
|
174
|
+
{
|
|
175
|
+
role: 'user',
|
|
176
|
+
content: [
|
|
177
|
+
{
|
|
178
|
+
type: 'file',
|
|
179
|
+
data: 'AAECAw==',
|
|
180
|
+
mediaType: 'image/png',
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
]);
|
|
185
|
+
expect(result).toEqual({
|
|
186
|
+
systemInstruction: undefined,
|
|
187
|
+
contents: [
|
|
188
|
+
{
|
|
189
|
+
role: 'user',
|
|
190
|
+
parts: [
|
|
191
|
+
{
|
|
192
|
+
inlineData: {
|
|
193
|
+
data: 'AAECAw==',
|
|
194
|
+
mimeType: 'image/png',
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
it('should add file parts for base64 encoded files', async () => {
|
|
203
|
+
const result = convertToGoogleGenerativeAIMessages([
|
|
204
|
+
{
|
|
205
|
+
role: 'user',
|
|
206
|
+
content: [{ type: 'file', data: 'AAECAw==', mediaType: 'image/png' }],
|
|
207
|
+
},
|
|
208
|
+
]);
|
|
209
|
+
expect(result).toEqual({
|
|
210
|
+
systemInstruction: undefined,
|
|
211
|
+
contents: [
|
|
212
|
+
{
|
|
213
|
+
role: 'user',
|
|
214
|
+
parts: [
|
|
215
|
+
{
|
|
216
|
+
inlineData: {
|
|
217
|
+
data: 'AAECAw==',
|
|
218
|
+
mimeType: 'image/png',
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
],
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
describe('tool messages', () => {
|
|
228
|
+
it('should convert tool result messages to function responses', async () => {
|
|
229
|
+
const result = convertToGoogleGenerativeAIMessages([
|
|
230
|
+
{
|
|
231
|
+
role: 'tool',
|
|
232
|
+
content: [
|
|
233
|
+
{
|
|
234
|
+
type: 'tool-result',
|
|
235
|
+
toolName: 'testFunction',
|
|
236
|
+
toolCallId: 'testCallId',
|
|
237
|
+
output: { type: 'json', value: { someData: 'test result' } },
|
|
238
|
+
},
|
|
239
|
+
],
|
|
240
|
+
},
|
|
241
|
+
]);
|
|
242
|
+
expect(result).toEqual({
|
|
243
|
+
systemInstruction: undefined,
|
|
244
|
+
contents: [
|
|
245
|
+
{
|
|
246
|
+
role: 'user',
|
|
247
|
+
parts: [
|
|
248
|
+
{
|
|
249
|
+
functionResponse: {
|
|
250
|
+
name: 'testFunction',
|
|
251
|
+
response: {
|
|
252
|
+
name: 'testFunction',
|
|
253
|
+
content: { someData: 'test result' },
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
},
|
|
259
|
+
],
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
describe('assistant messages', () => {
|
|
264
|
+
it('should add PNG image parts for base64 encoded files', async () => {
|
|
265
|
+
const result = convertToGoogleGenerativeAIMessages([
|
|
266
|
+
{
|
|
267
|
+
role: 'assistant',
|
|
268
|
+
content: [{ type: 'file', data: 'AAECAw==', mediaType: 'image/png' }],
|
|
269
|
+
},
|
|
270
|
+
]);
|
|
271
|
+
expect(result).toEqual({
|
|
272
|
+
systemInstruction: undefined,
|
|
273
|
+
contents: [
|
|
274
|
+
{
|
|
275
|
+
role: 'model',
|
|
276
|
+
parts: [
|
|
277
|
+
{
|
|
278
|
+
inlineData: {
|
|
279
|
+
data: 'AAECAw==',
|
|
280
|
+
mimeType: 'image/png',
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
],
|
|
284
|
+
},
|
|
285
|
+
],
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
it('should throw error for non-PNG images in assistant messages', async () => {
|
|
289
|
+
expect(() => convertToGoogleGenerativeAIMessages([
|
|
290
|
+
{
|
|
291
|
+
role: 'assistant',
|
|
292
|
+
content: [
|
|
293
|
+
{ type: 'file', data: 'AAECAw==', mediaType: 'image/jpeg' },
|
|
294
|
+
],
|
|
295
|
+
},
|
|
296
|
+
])).toThrow('Only PNG images are supported in assistant messages');
|
|
297
|
+
});
|
|
298
|
+
it('should throw error for URL file data in assistant messages', async () => {
|
|
299
|
+
expect(() => convertToGoogleGenerativeAIMessages([
|
|
300
|
+
{
|
|
301
|
+
role: 'assistant',
|
|
302
|
+
content: [
|
|
303
|
+
{
|
|
304
|
+
type: 'file',
|
|
305
|
+
data: new URL('https://example.com/image.png'),
|
|
306
|
+
mediaType: 'image/png',
|
|
307
|
+
},
|
|
308
|
+
],
|
|
309
|
+
},
|
|
310
|
+
])).toThrow('File data URLs in assistant messages are not supported');
|
|
311
|
+
});
|
|
312
|
+
it('should convert tool result messages with content type (multipart with images)', async () => {
|
|
313
|
+
const result = convertToGoogleGenerativeAIMessages([
|
|
314
|
+
{
|
|
315
|
+
role: 'tool',
|
|
316
|
+
content: [
|
|
317
|
+
{
|
|
318
|
+
type: 'tool-result',
|
|
319
|
+
toolName: 'imageGenerator',
|
|
320
|
+
toolCallId: 'testCallId',
|
|
321
|
+
output: {
|
|
322
|
+
type: 'content',
|
|
323
|
+
value: [
|
|
324
|
+
{
|
|
325
|
+
type: 'text',
|
|
326
|
+
text: 'Here is the generated image:',
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
type: 'media',
|
|
330
|
+
data: 'base64encodedimagedata',
|
|
331
|
+
mediaType: 'image/jpeg',
|
|
332
|
+
},
|
|
333
|
+
],
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
],
|
|
337
|
+
},
|
|
338
|
+
]);
|
|
339
|
+
expect(result).toEqual({
|
|
340
|
+
systemInstruction: undefined,
|
|
341
|
+
contents: [
|
|
342
|
+
{
|
|
343
|
+
role: 'user',
|
|
344
|
+
parts: [
|
|
345
|
+
{
|
|
346
|
+
functionResponse: {
|
|
347
|
+
name: 'imageGenerator',
|
|
348
|
+
response: {
|
|
349
|
+
name: 'imageGenerator',
|
|
350
|
+
content: 'Here is the generated image:',
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
inlineData: {
|
|
356
|
+
mimeType: 'image/jpeg',
|
|
357
|
+
data: 'base64encodedimagedata',
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
text: 'Tool executed successfully and returned this image as a response',
|
|
362
|
+
},
|
|
363
|
+
],
|
|
364
|
+
},
|
|
365
|
+
],
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
describe('parallel tool calls', () => {
|
|
370
|
+
it('should include thought signature on functionCall when provided', async () => {
|
|
371
|
+
const result = convertToGoogleGenerativeAIMessages([
|
|
372
|
+
{
|
|
373
|
+
role: 'assistant',
|
|
374
|
+
content: [
|
|
375
|
+
{
|
|
376
|
+
type: 'tool-call',
|
|
377
|
+
toolCallId: 'call1',
|
|
378
|
+
toolName: 'checkweather',
|
|
379
|
+
input: { city: 'paris' },
|
|
380
|
+
providerOptions: { google: { thoughtSignature: 'sig_parallel' } },
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
type: 'tool-call',
|
|
384
|
+
toolCallId: 'call2',
|
|
385
|
+
toolName: 'checkweather',
|
|
386
|
+
input: { city: 'london' },
|
|
387
|
+
},
|
|
388
|
+
],
|
|
389
|
+
},
|
|
390
|
+
]);
|
|
391
|
+
expect(result.contents[0].parts[0]).toEqual({
|
|
392
|
+
functionCall: {
|
|
393
|
+
args: { city: 'paris' },
|
|
394
|
+
name: 'checkweather',
|
|
395
|
+
},
|
|
396
|
+
thoughtSignature: 'sig_parallel',
|
|
397
|
+
});
|
|
398
|
+
expect(result.contents[0].parts[1]).toEqual({
|
|
399
|
+
functionCall: {
|
|
400
|
+
args: { city: 'london' },
|
|
401
|
+
name: 'checkweather',
|
|
402
|
+
},
|
|
403
|
+
thoughtSignature: undefined,
|
|
404
|
+
});
|
|
405
|
+
});
|
|
406
|
+
});
|
|
407
|
+
describe('tool results with thought signatures', () => {
|
|
408
|
+
it('should include thought signature on functionCall but not on functionResponse', async () => {
|
|
409
|
+
const result = convertToGoogleGenerativeAIMessages([
|
|
410
|
+
{
|
|
411
|
+
role: 'assistant',
|
|
412
|
+
content: [
|
|
413
|
+
{
|
|
414
|
+
type: 'tool-call',
|
|
415
|
+
toolCallId: 'call1',
|
|
416
|
+
toolName: 'readdata',
|
|
417
|
+
input: { userId: '123' },
|
|
418
|
+
providerOptions: { google: { thoughtSignature: 'sig_original' } },
|
|
419
|
+
},
|
|
420
|
+
],
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
role: 'tool',
|
|
424
|
+
content: [
|
|
425
|
+
{
|
|
426
|
+
type: 'tool-result',
|
|
427
|
+
toolCallId: 'call1',
|
|
428
|
+
toolName: 'readdata',
|
|
429
|
+
output: {
|
|
430
|
+
type: 'error-text',
|
|
431
|
+
value: 'file not found',
|
|
432
|
+
},
|
|
433
|
+
providerOptions: { google: { thoughtSignature: 'sig_original' } },
|
|
434
|
+
},
|
|
435
|
+
],
|
|
436
|
+
},
|
|
437
|
+
]);
|
|
438
|
+
expect(result.contents[0].parts[0]).toEqual({
|
|
439
|
+
functionCall: {
|
|
440
|
+
args: { userId: '123' },
|
|
441
|
+
name: 'readdata',
|
|
442
|
+
},
|
|
443
|
+
thoughtSignature: 'sig_original',
|
|
444
|
+
});
|
|
445
|
+
expect(result.contents[1].parts[0]).toEqual({
|
|
446
|
+
functionResponse: {
|
|
447
|
+
name: 'readdata',
|
|
448
|
+
response: {
|
|
449
|
+
content: 'file not found',
|
|
450
|
+
name: 'readdata',
|
|
451
|
+
},
|
|
452
|
+
},
|
|
453
|
+
});
|
|
454
|
+
expect(result.contents[1].parts[0]).not.toHaveProperty('thoughtSignature');
|
|
455
|
+
});
|
|
456
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-model-path.d.ts","sourceRoot":"","sources":["../src/get-model-path.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEpD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-model-path.test.d.ts","sourceRoot":"","sources":["../src/get-model-path.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { getModelPath } from './get-model-path';
|
|
2
|
+
import { it, expect } from 'vitest';
|
|
3
|
+
it('should pass through model path for models/*', async () => {
|
|
4
|
+
expect(getModelPath('models/some-model')).toEqual('models/some-model');
|
|
5
|
+
});
|
|
6
|
+
it('should pass through model path for tunedModels/*', async () => {
|
|
7
|
+
expect(getModelPath('tunedModels/some-model')).toEqual('tunedModels/some-model');
|
|
8
|
+
});
|
|
9
|
+
it('should add model path prefix to models without slash', async () => {
|
|
10
|
+
expect(getModelPath('some-model')).toEqual('models/some-model');
|
|
11
|
+
});
|