@providerprotocol/ai 0.0.18 → 0.0.20
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 +364 -111
- package/dist/anthropic/index.d.ts +1 -1
- package/dist/anthropic/index.js +6 -6
- package/dist/chunk-P5IRTEM5.js +120 -0
- package/dist/chunk-P5IRTEM5.js.map +1 -0
- package/dist/{chunk-5FEAOEXV.js → chunk-U3FZWV4U.js} +53 -102
- package/dist/chunk-U3FZWV4U.js.map +1 -0
- package/dist/chunk-WAKD3OO5.js +224 -0
- package/dist/chunk-WAKD3OO5.js.map +1 -0
- package/dist/content-DEl3z_W2.d.ts +276 -0
- package/dist/google/index.d.ts +3 -1
- package/dist/google/index.js +123 -7
- package/dist/google/index.js.map +1 -1
- package/dist/http/index.d.ts +2 -2
- package/dist/http/index.js +4 -3
- package/dist/image-Dhq-Yuq4.d.ts +456 -0
- package/dist/index.d.ts +55 -163
- package/dist/index.js +81 -213
- package/dist/index.js.map +1 -1
- package/dist/ollama/index.d.ts +1 -1
- package/dist/ollama/index.js +6 -6
- package/dist/openai/index.d.ts +47 -20
- package/dist/openai/index.js +310 -7
- package/dist/openai/index.js.map +1 -1
- package/dist/openrouter/index.d.ts +1 -1
- package/dist/openrouter/index.js +6 -6
- package/dist/{provider-D5MO3-pS.d.ts → provider-BBMBZuGn.d.ts} +11 -11
- package/dist/proxy/index.d.ts +310 -86
- package/dist/proxy/index.js +33 -59
- package/dist/proxy/index.js.map +1 -1
- package/dist/{retry-DZ4Sqmxp.d.ts → retry-DR7YRJDz.d.ts} +1 -1
- package/dist/{stream-BjyVzBxV.d.ts → stream-DRHy6q1a.d.ts} +2 -275
- package/dist/xai/index.d.ts +29 -1
- package/dist/xai/index.js +119 -7
- package/dist/xai/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-5FEAOEXV.js.map +0 -1
- package/dist/chunk-DZQHVGNV.js +0 -71
- package/dist/chunk-DZQHVGNV.js.map +0 -1
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Content block types for multimodal messages.
|
|
3
|
+
*
|
|
4
|
+
* Defines the various content block types that can be included in
|
|
5
|
+
* user and assistant messages, supporting text, images, audio, video,
|
|
6
|
+
* and arbitrary binary data.
|
|
7
|
+
*
|
|
8
|
+
* @module types/content
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Image source variants for ImageBlock.
|
|
12
|
+
*
|
|
13
|
+
* Images can be provided as base64-encoded strings, URLs, or raw bytes.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* // Base64 encoded image
|
|
18
|
+
* const base64Source: ImageSource = {
|
|
19
|
+
* type: 'base64',
|
|
20
|
+
* data: 'iVBORw0KGgo...'
|
|
21
|
+
* };
|
|
22
|
+
*
|
|
23
|
+
* // URL reference
|
|
24
|
+
* const urlSource: ImageSource = {
|
|
25
|
+
* type: 'url',
|
|
26
|
+
* url: 'https://example.com/image.png'
|
|
27
|
+
* };
|
|
28
|
+
*
|
|
29
|
+
* // Raw bytes
|
|
30
|
+
* const bytesSource: ImageSource = {
|
|
31
|
+
* type: 'bytes',
|
|
32
|
+
* data: new Uint8Array([...])
|
|
33
|
+
* };
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
type ImageSource = {
|
|
37
|
+
type: 'base64';
|
|
38
|
+
data: string;
|
|
39
|
+
} | {
|
|
40
|
+
type: 'url';
|
|
41
|
+
url: string;
|
|
42
|
+
} | {
|
|
43
|
+
type: 'bytes';
|
|
44
|
+
data: Uint8Array;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Text content block.
|
|
48
|
+
*
|
|
49
|
+
* The most common content block type, containing plain text content.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* const textBlock: TextBlock = {
|
|
54
|
+
* type: 'text',
|
|
55
|
+
* text: 'Hello, world!'
|
|
56
|
+
* };
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
interface TextBlock {
|
|
60
|
+
/** Discriminator for text blocks */
|
|
61
|
+
type: 'text';
|
|
62
|
+
/** The text content */
|
|
63
|
+
text: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Image content block.
|
|
67
|
+
*
|
|
68
|
+
* Contains an image with its source data and metadata.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* const imageBlock: ImageBlock = {
|
|
73
|
+
* type: 'image',
|
|
74
|
+
* source: { type: 'url', url: 'https://example.com/photo.jpg' },
|
|
75
|
+
* mimeType: 'image/jpeg',
|
|
76
|
+
* width: 1920,
|
|
77
|
+
* height: 1080
|
|
78
|
+
* };
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
interface ImageBlock {
|
|
82
|
+
/** Discriminator for image blocks */
|
|
83
|
+
type: 'image';
|
|
84
|
+
/** The image data source */
|
|
85
|
+
source: ImageSource;
|
|
86
|
+
/** MIME type of the image (e.g., 'image/png', 'image/jpeg') */
|
|
87
|
+
mimeType: string;
|
|
88
|
+
/** Image width in pixels */
|
|
89
|
+
width?: number;
|
|
90
|
+
/** Image height in pixels */
|
|
91
|
+
height?: number;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Audio content block.
|
|
95
|
+
*
|
|
96
|
+
* Contains audio data with its metadata.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* const audioBlock: AudioBlock = {
|
|
101
|
+
* type: 'audio',
|
|
102
|
+
* data: audioBytes,
|
|
103
|
+
* mimeType: 'audio/mp3',
|
|
104
|
+
* duration: 120.5
|
|
105
|
+
* };
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
interface AudioBlock {
|
|
109
|
+
/** Discriminator for audio blocks */
|
|
110
|
+
type: 'audio';
|
|
111
|
+
/** Raw audio data */
|
|
112
|
+
data: Uint8Array;
|
|
113
|
+
/** MIME type of the audio (e.g., 'audio/mp3', 'audio/wav') */
|
|
114
|
+
mimeType: string;
|
|
115
|
+
/** Duration in seconds */
|
|
116
|
+
duration?: number;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Video content block.
|
|
120
|
+
*
|
|
121
|
+
* Contains video data with its metadata.
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```typescript
|
|
125
|
+
* const videoBlock: VideoBlock = {
|
|
126
|
+
* type: 'video',
|
|
127
|
+
* data: videoBytes,
|
|
128
|
+
* mimeType: 'video/mp4',
|
|
129
|
+
* duration: 30,
|
|
130
|
+
* width: 1920,
|
|
131
|
+
* height: 1080
|
|
132
|
+
* };
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
135
|
+
interface VideoBlock {
|
|
136
|
+
/** Discriminator for video blocks */
|
|
137
|
+
type: 'video';
|
|
138
|
+
/** Raw video data */
|
|
139
|
+
data: Uint8Array;
|
|
140
|
+
/** MIME type of the video (e.g., 'video/mp4', 'video/webm') */
|
|
141
|
+
mimeType: string;
|
|
142
|
+
/** Duration in seconds */
|
|
143
|
+
duration?: number;
|
|
144
|
+
/** Video width in pixels */
|
|
145
|
+
width?: number;
|
|
146
|
+
/** Video height in pixels */
|
|
147
|
+
height?: number;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Binary content block for arbitrary data.
|
|
151
|
+
*
|
|
152
|
+
* A generic block type for data that doesn't fit other categories.
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```typescript
|
|
156
|
+
* const binaryBlock: BinaryBlock = {
|
|
157
|
+
* type: 'binary',
|
|
158
|
+
* data: pdfBytes,
|
|
159
|
+
* mimeType: 'application/pdf',
|
|
160
|
+
* metadata: { filename: 'document.pdf', pages: 10 }
|
|
161
|
+
* };
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
interface BinaryBlock {
|
|
165
|
+
/** Discriminator for binary blocks */
|
|
166
|
+
type: 'binary';
|
|
167
|
+
/** Raw binary data */
|
|
168
|
+
data: Uint8Array;
|
|
169
|
+
/** MIME type of the data */
|
|
170
|
+
mimeType: string;
|
|
171
|
+
/** Additional metadata about the binary content */
|
|
172
|
+
metadata?: Record<string, unknown>;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Union of all content block types.
|
|
176
|
+
*
|
|
177
|
+
* Used when a function or property can accept any type of content block.
|
|
178
|
+
*/
|
|
179
|
+
type ContentBlock = TextBlock | ImageBlock | AudioBlock | VideoBlock | BinaryBlock;
|
|
180
|
+
/**
|
|
181
|
+
* Content types allowed in user messages.
|
|
182
|
+
*
|
|
183
|
+
* Users can send any type of content block including binary data.
|
|
184
|
+
*/
|
|
185
|
+
type UserContent = TextBlock | ImageBlock | AudioBlock | VideoBlock | BinaryBlock;
|
|
186
|
+
/**
|
|
187
|
+
* Content types allowed in assistant messages.
|
|
188
|
+
*
|
|
189
|
+
* Assistants can generate text and media but not arbitrary binary data.
|
|
190
|
+
*/
|
|
191
|
+
type AssistantContent = TextBlock | ImageBlock | AudioBlock | VideoBlock;
|
|
192
|
+
/**
|
|
193
|
+
* Creates a text content block from a string.
|
|
194
|
+
*
|
|
195
|
+
* @param content - The text content
|
|
196
|
+
* @returns A TextBlock containing the provided text
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* ```typescript
|
|
200
|
+
* const block = text('Hello, world!');
|
|
201
|
+
* // { type: 'text', text: 'Hello, world!' }
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
204
|
+
declare function text(content: string): TextBlock;
|
|
205
|
+
/**
|
|
206
|
+
* Type guard for TextBlock.
|
|
207
|
+
*
|
|
208
|
+
* @param block - The content block to check
|
|
209
|
+
* @returns True if the block is a TextBlock
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* ```typescript
|
|
213
|
+
* if (isTextBlock(block)) {
|
|
214
|
+
* console.log(block.text);
|
|
215
|
+
* }
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
declare function isTextBlock(block: ContentBlock): block is TextBlock;
|
|
219
|
+
/**
|
|
220
|
+
* Type guard for ImageBlock.
|
|
221
|
+
*
|
|
222
|
+
* @param block - The content block to check
|
|
223
|
+
* @returns True if the block is an ImageBlock
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* ```typescript
|
|
227
|
+
* if (isImageBlock(block)) {
|
|
228
|
+
* console.log(block.mimeType, block.width, block.height);
|
|
229
|
+
* }
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
232
|
+
declare function isImageBlock(block: ContentBlock): block is ImageBlock;
|
|
233
|
+
/**
|
|
234
|
+
* Type guard for AudioBlock.
|
|
235
|
+
*
|
|
236
|
+
* @param block - The content block to check
|
|
237
|
+
* @returns True if the block is an AudioBlock
|
|
238
|
+
*
|
|
239
|
+
* @example
|
|
240
|
+
* ```typescript
|
|
241
|
+
* if (isAudioBlock(block)) {
|
|
242
|
+
* console.log(block.mimeType, block.duration);
|
|
243
|
+
* }
|
|
244
|
+
* ```
|
|
245
|
+
*/
|
|
246
|
+
declare function isAudioBlock(block: ContentBlock): block is AudioBlock;
|
|
247
|
+
/**
|
|
248
|
+
* Type guard for VideoBlock.
|
|
249
|
+
*
|
|
250
|
+
* @param block - The content block to check
|
|
251
|
+
* @returns True if the block is a VideoBlock
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* ```typescript
|
|
255
|
+
* if (isVideoBlock(block)) {
|
|
256
|
+
* console.log(block.mimeType, block.duration);
|
|
257
|
+
* }
|
|
258
|
+
* ```
|
|
259
|
+
*/
|
|
260
|
+
declare function isVideoBlock(block: ContentBlock): block is VideoBlock;
|
|
261
|
+
/**
|
|
262
|
+
* Type guard for BinaryBlock.
|
|
263
|
+
*
|
|
264
|
+
* @param block - The content block to check
|
|
265
|
+
* @returns True if the block is a BinaryBlock
|
|
266
|
+
*
|
|
267
|
+
* @example
|
|
268
|
+
* ```typescript
|
|
269
|
+
* if (isBinaryBlock(block)) {
|
|
270
|
+
* console.log(block.mimeType, block.metadata);
|
|
271
|
+
* }
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
274
|
+
declare function isBinaryBlock(block: ContentBlock): block is BinaryBlock;
|
|
275
|
+
|
|
276
|
+
export { type AssistantContent as A, type BinaryBlock as B, type ContentBlock as C, type ImageBlock as I, type TextBlock as T, type UserContent as U, type VideoBlock as V, type AudioBlock as a, type ImageSource as b, isImageBlock as c, isAudioBlock as d, isVideoBlock as e, isBinaryBlock as f, isTextBlock as i, text as t };
|
package/dist/google/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { d as Provider } from '../provider-
|
|
1
|
+
import { d as Provider } from '../provider-BBMBZuGn.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Provider-specific parameters for Google Gemini API requests.
|
|
@@ -864,6 +864,8 @@ interface GoogleEmbedParams {
|
|
|
864
864
|
title?: string;
|
|
865
865
|
/** Output dimensionality */
|
|
866
866
|
outputDimensionality?: number;
|
|
867
|
+
/** Whether to automatically truncate inputs exceeding token limits (default: true) */
|
|
868
|
+
autoTruncate?: boolean;
|
|
867
869
|
}
|
|
868
870
|
|
|
869
871
|
/**
|
package/dist/google/index.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createProvider
|
|
3
3
|
} from "../chunk-MSR5P65T.js";
|
|
4
|
+
import {
|
|
5
|
+
Image
|
|
6
|
+
} from "../chunk-WAKD3OO5.js";
|
|
4
7
|
import {
|
|
5
8
|
AssistantMessage,
|
|
6
9
|
isAssistantMessage,
|
|
@@ -11,14 +14,14 @@ import {
|
|
|
11
14
|
parseSSEStream
|
|
12
15
|
} from "../chunk-Z7RBRCRN.js";
|
|
13
16
|
import {
|
|
14
|
-
doFetch,
|
|
15
|
-
doStreamFetch,
|
|
16
|
-
normalizeHttpError,
|
|
17
17
|
resolveApiKey
|
|
18
|
-
} from "../chunk-
|
|
18
|
+
} from "../chunk-P5IRTEM5.js";
|
|
19
19
|
import {
|
|
20
|
-
UPPError
|
|
21
|
-
|
|
20
|
+
UPPError,
|
|
21
|
+
doFetch,
|
|
22
|
+
doStreamFetch,
|
|
23
|
+
normalizeHttpError
|
|
24
|
+
} from "../chunk-U3FZWV4U.js";
|
|
22
25
|
|
|
23
26
|
// src/providers/google/transform.ts
|
|
24
27
|
function transformRequest(request, modelId) {
|
|
@@ -579,6 +582,9 @@ function createEmbeddingHandler() {
|
|
|
579
582
|
if (request.params?.outputDimensionality !== void 0) {
|
|
580
583
|
embedRequest.outputDimensionality = request.params.outputDimensionality;
|
|
581
584
|
}
|
|
585
|
+
if (request.params?.autoTruncate !== void 0) {
|
|
586
|
+
embedRequest.autoTruncate = request.params.autoTruncate;
|
|
587
|
+
}
|
|
582
588
|
return embedRequest;
|
|
583
589
|
});
|
|
584
590
|
const url = `${baseUrl}/models/${modelId}:batchEmbedContents?key=${apiKey}`;
|
|
@@ -622,6 +628,115 @@ function createEmbeddingHandler() {
|
|
|
622
628
|
};
|
|
623
629
|
}
|
|
624
630
|
|
|
631
|
+
// src/providers/google/image.ts
|
|
632
|
+
var GOOGLE_AI_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
|
|
633
|
+
function getCapabilities() {
|
|
634
|
+
return {
|
|
635
|
+
generate: true,
|
|
636
|
+
streaming: false,
|
|
637
|
+
edit: false,
|
|
638
|
+
maxImages: 4
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
function createImageHandler() {
|
|
642
|
+
let providerRef = null;
|
|
643
|
+
return {
|
|
644
|
+
_setProvider(provider) {
|
|
645
|
+
providerRef = provider;
|
|
646
|
+
},
|
|
647
|
+
bind(modelId) {
|
|
648
|
+
if (!providerRef) {
|
|
649
|
+
throw new UPPError(
|
|
650
|
+
"Provider reference not set. Handler must be used with createProvider().",
|
|
651
|
+
"INVALID_REQUEST",
|
|
652
|
+
"google",
|
|
653
|
+
"image"
|
|
654
|
+
);
|
|
655
|
+
}
|
|
656
|
+
const capabilities = getCapabilities();
|
|
657
|
+
const model = {
|
|
658
|
+
modelId,
|
|
659
|
+
capabilities,
|
|
660
|
+
get provider() {
|
|
661
|
+
return providerRef;
|
|
662
|
+
},
|
|
663
|
+
async generate(request) {
|
|
664
|
+
return executeGenerate(modelId, request);
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
return model;
|
|
668
|
+
}
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
async function executeGenerate(modelId, request) {
|
|
672
|
+
const apiKey = await resolveApiKey(
|
|
673
|
+
request.config,
|
|
674
|
+
"GOOGLE_API_KEY",
|
|
675
|
+
"google",
|
|
676
|
+
"image"
|
|
677
|
+
);
|
|
678
|
+
const baseUrl = request.config.baseUrl?.replace(/\/$/, "") ?? GOOGLE_AI_BASE_URL;
|
|
679
|
+
const url = `${baseUrl}/models/${modelId}:predict`;
|
|
680
|
+
const body = {
|
|
681
|
+
instances: [{
|
|
682
|
+
prompt: request.prompt
|
|
683
|
+
}],
|
|
684
|
+
parameters: buildParameters(request.params)
|
|
685
|
+
};
|
|
686
|
+
const headers = {
|
|
687
|
+
"Content-Type": "application/json",
|
|
688
|
+
"x-goog-api-key": apiKey
|
|
689
|
+
};
|
|
690
|
+
if (request.config.headers) {
|
|
691
|
+
for (const [key, value] of Object.entries(request.config.headers)) {
|
|
692
|
+
if (value !== void 0) {
|
|
693
|
+
headers[key] = value;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
const response = await doFetch(url, {
|
|
698
|
+
method: "POST",
|
|
699
|
+
headers,
|
|
700
|
+
body: JSON.stringify(body),
|
|
701
|
+
signal: request.signal
|
|
702
|
+
}, request.config, "google", "image");
|
|
703
|
+
const data = await response.json();
|
|
704
|
+
return transformResponse2(data);
|
|
705
|
+
}
|
|
706
|
+
function buildParameters(params) {
|
|
707
|
+
const parameters = {};
|
|
708
|
+
if (!params) return parameters;
|
|
709
|
+
if (params.sampleCount !== void 0) parameters.sampleCount = params.sampleCount;
|
|
710
|
+
if (params.imageSize !== void 0) parameters.imageSize = params.imageSize;
|
|
711
|
+
if (params.aspectRatio !== void 0) parameters.aspectRatio = params.aspectRatio;
|
|
712
|
+
if (params.personGeneration !== void 0) parameters.personGeneration = params.personGeneration;
|
|
713
|
+
if (params.safetyFilterLevel !== void 0) parameters.safetyFilterLevel = params.safetyFilterLevel;
|
|
714
|
+
if (params.addWatermark !== void 0) parameters.addWatermark = params.addWatermark;
|
|
715
|
+
if (params.negativePrompt !== void 0) parameters.negativePrompt = params.negativePrompt;
|
|
716
|
+
return parameters;
|
|
717
|
+
}
|
|
718
|
+
function transformResponse2(data) {
|
|
719
|
+
if (!data.predictions || data.predictions.length === 0) {
|
|
720
|
+
throw new UPPError(
|
|
721
|
+
"No images in response",
|
|
722
|
+
"PROVIDER_ERROR",
|
|
723
|
+
"google",
|
|
724
|
+
"image"
|
|
725
|
+
);
|
|
726
|
+
}
|
|
727
|
+
const images = data.predictions.map((prediction) => {
|
|
728
|
+
const mimeType = prediction.mimeType ?? "image/png";
|
|
729
|
+
const image = Image.fromBase64(prediction.bytesBase64Encoded, mimeType);
|
|
730
|
+
return { image };
|
|
731
|
+
});
|
|
732
|
+
return {
|
|
733
|
+
images,
|
|
734
|
+
usage: {
|
|
735
|
+
imagesGenerated: images.length
|
|
736
|
+
}
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
|
|
625
740
|
// src/providers/google/cache.ts
|
|
626
741
|
var CACHE_API_BASE = "https://generativelanguage.googleapis.com/v1beta/cachedContents";
|
|
627
742
|
async function create(options) {
|
|
@@ -757,7 +872,8 @@ var baseProvider = createProvider({
|
|
|
757
872
|
version: "1.0.0",
|
|
758
873
|
modalities: {
|
|
759
874
|
llm: createLLMHandler(),
|
|
760
|
-
embedding: createEmbeddingHandler()
|
|
875
|
+
embedding: createEmbeddingHandler(),
|
|
876
|
+
image: createImageHandler()
|
|
761
877
|
}
|
|
762
878
|
});
|
|
763
879
|
var google = Object.assign(baseProvider, { cache });
|