@ai-sdk/alibaba 0.0.0-6b196531-20260710185421

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.
@@ -0,0 +1,178 @@
1
+ import {
2
+ TooManyEmbeddingValuesForCallError,
3
+ UnsupportedFunctionalityError,
4
+ type EmbeddingModelV4,
5
+ } from '@ai-sdk/provider';
6
+ import {
7
+ combineHeaders,
8
+ createJsonErrorResponseHandler,
9
+ createJsonResponseHandler,
10
+ parseProviderOptions,
11
+ postJsonToApi,
12
+ serializeModelOptions,
13
+ WORKFLOW_SERIALIZE,
14
+ WORKFLOW_DESERIALIZE,
15
+ } from '@ai-sdk/provider-utils';
16
+ import { z } from 'zod/v4';
17
+ import {
18
+ alibabaEmbeddingModelOptions,
19
+ type AlibabaEmbeddingModelId,
20
+ } from './alibaba-embedding-model-options';
21
+ import type { AlibabaConfig } from './alibaba-config';
22
+
23
+ // TODO: Add Alibaba multimodal embedding support in a follow-up change.
24
+ const alibabaEmbeddingFailedResponseHandler = createJsonErrorResponseHandler({
25
+ errorSchema: z.object({
26
+ code: z.string().nullish(),
27
+ message: z.string(),
28
+ request_id: z.string().nullish(),
29
+ }),
30
+ errorToMessage: data => data.message,
31
+ });
32
+
33
+ export class AlibabaEmbeddingModel implements EmbeddingModelV4 {
34
+ readonly specificationVersion = 'v4';
35
+ readonly modelId: AlibabaEmbeddingModelId;
36
+ readonly maxEmbeddingsPerCall = 10;
37
+ readonly supportsParallelCalls = false;
38
+
39
+ private readonly config: AlibabaConfig;
40
+
41
+ static [WORKFLOW_SERIALIZE](model: AlibabaEmbeddingModel) {
42
+ return serializeModelOptions({
43
+ modelId: model.modelId,
44
+ config: model.config,
45
+ });
46
+ }
47
+
48
+ static [WORKFLOW_DESERIALIZE](options: {
49
+ modelId: AlibabaEmbeddingModelId;
50
+ config: AlibabaConfig;
51
+ }) {
52
+ return new AlibabaEmbeddingModel(options.modelId, options.config);
53
+ }
54
+
55
+ constructor(modelId: AlibabaEmbeddingModelId, config: AlibabaConfig) {
56
+ this.modelId = modelId;
57
+ this.config = config;
58
+ }
59
+
60
+ get provider(): string {
61
+ return this.config.provider;
62
+ }
63
+
64
+ async doEmbed({
65
+ values,
66
+ headers,
67
+ abortSignal,
68
+ providerOptions,
69
+ }: Parameters<EmbeddingModelV4['doEmbed']>[0]): Promise<
70
+ Awaited<ReturnType<EmbeddingModelV4['doEmbed']>>
71
+ > {
72
+ if (values.length > this.maxEmbeddingsPerCall) {
73
+ throw new TooManyEmbeddingValuesForCallError({
74
+ provider: this.provider,
75
+ modelId: this.modelId,
76
+ maxEmbeddingsPerCall: this.maxEmbeddingsPerCall,
77
+ values,
78
+ });
79
+ }
80
+
81
+ const alibabaOptions = await parseProviderOptions({
82
+ provider: 'alibaba',
83
+ providerOptions,
84
+ schema: alibabaEmbeddingModelOptions,
85
+ });
86
+
87
+ // TODO: Explore first-class sparse embedding support in AI SDK core.
88
+ if (alibabaOptions?.outputType === 'sparse') {
89
+ throw new UnsupportedFunctionalityError({
90
+ functionality: "Alibaba embedding outputType 'sparse'",
91
+ message:
92
+ "Alibaba embedding outputType 'sparse' is not supported because AI SDK embeddings require dense number arrays. Use 'dense' or 'dense&sparse' instead.",
93
+ });
94
+ }
95
+
96
+ const {
97
+ responseHeaders,
98
+ value: response,
99
+ rawValue,
100
+ } = await postJsonToApi({
101
+ url: `${this.config.baseURL}/services/embeddings/text-embedding/text-embedding`,
102
+ headers: combineHeaders(this.config.headers?.(), headers),
103
+ body: {
104
+ model: this.modelId,
105
+ input: {
106
+ texts: values,
107
+ },
108
+ parameters: {
109
+ text_type: alibabaOptions?.textType,
110
+ dimension: alibabaOptions?.dimension,
111
+ output_type: alibabaOptions?.outputType,
112
+ },
113
+ },
114
+ failedResponseHandler: alibabaEmbeddingFailedResponseHandler,
115
+ successfulResponseHandler: createJsonResponseHandler(
116
+ alibabaTextEmbeddingResponseSchema,
117
+ ),
118
+ abortSignal,
119
+ fetch: this.config.fetch,
120
+ });
121
+
122
+ const sortedEmbeddings = response.output.embeddings.sort(
123
+ (a, b) => a.text_index - b.text_index,
124
+ );
125
+ const sparseEmbeddings = sortedEmbeddings
126
+ .map(item =>
127
+ item.sparse_embedding == null
128
+ ? undefined
129
+ : {
130
+ textIndex: item.text_index,
131
+ sparseEmbedding: item.sparse_embedding,
132
+ },
133
+ )
134
+ .filter(item => item != null);
135
+
136
+ return {
137
+ warnings: [],
138
+ embeddings: sortedEmbeddings.map(item => item.embedding),
139
+ usage: response.usage
140
+ ? { tokens: response.usage.total_tokens }
141
+ : undefined,
142
+ providerMetadata:
143
+ sparseEmbeddings.length > 0
144
+ ? {
145
+ alibaba: {
146
+ sparseEmbeddings,
147
+ },
148
+ }
149
+ : undefined,
150
+ response: { headers: responseHeaders, body: rawValue },
151
+ };
152
+ }
153
+ }
154
+
155
+ const alibabaTextEmbeddingSparseEmbeddingSchema = z.object({
156
+ index: z.number(),
157
+ value: z.number(),
158
+ token: z.string().nullish(),
159
+ });
160
+
161
+ const alibabaTextEmbeddingResponseSchema = z.object({
162
+ output: z.object({
163
+ embeddings: z.array(
164
+ z.object({
165
+ embedding: z.array(z.number()),
166
+ text_index: z.number(),
167
+ sparse_embedding: z
168
+ .array(alibabaTextEmbeddingSparseEmbeddingSchema)
169
+ .nullish(),
170
+ }),
171
+ ),
172
+ }),
173
+ usage: z
174
+ .object({
175
+ total_tokens: z.number(),
176
+ })
177
+ .nullish(),
178
+ });
@@ -0,0 +1,17 @@
1
+ import { createJsonErrorResponseHandler } from '@ai-sdk/provider-utils';
2
+ import { z } from 'zod/v4';
3
+
4
+ const alibabaErrorDataSchema = z.object({
5
+ error: z.object({
6
+ message: z.string(),
7
+ code: z.string().nullish(),
8
+ type: z.string().nullish(),
9
+ }),
10
+ });
11
+
12
+ export type AlibabaErrorData = z.infer<typeof alibabaErrorDataSchema>;
13
+
14
+ export const alibabaFailedResponseHandler = createJsonErrorResponseHandler({
15
+ errorSchema: alibabaErrorDataSchema,
16
+ errorToMessage: data => data.error.message,
17
+ });
@@ -0,0 +1,187 @@
1
+ import {
2
+ NoSuchModelError,
3
+ type EmbeddingModelV4,
4
+ type Experimental_VideoModelV4,
5
+ type LanguageModelV4,
6
+ type ProviderV4,
7
+ } from '@ai-sdk/provider';
8
+ import {
9
+ loadApiKey,
10
+ withoutTrailingSlash,
11
+ withUserAgentSuffix,
12
+ type FetchFunction,
13
+ } from '@ai-sdk/provider-utils';
14
+ import { AlibabaChatLanguageModel } from './alibaba-chat-language-model';
15
+ import type { AlibabaChatModelId } from './alibaba-chat-language-model-options';
16
+ import { AlibabaEmbeddingModel } from './alibaba-embedding-model';
17
+ import type { AlibabaEmbeddingModelId } from './alibaba-embedding-model-options';
18
+ import { AlibabaVideoModel } from './alibaba-video-model';
19
+ import type { AlibabaVideoModelId } from './alibaba-video-settings';
20
+ import { VERSION } from './version';
21
+
22
+ export type { AlibabaErrorData } from './alibaba-error';
23
+ export { alibabaFailedResponseHandler } from './alibaba-error';
24
+
25
+ export interface AlibabaProvider extends ProviderV4 {
26
+ (modelId: AlibabaChatModelId): LanguageModelV4;
27
+
28
+ /**
29
+ * Creates a model for text generation.
30
+ */
31
+ languageModel(modelId: AlibabaChatModelId): LanguageModelV4;
32
+
33
+ /**
34
+ * Creates a chat model for text generation.
35
+ */
36
+ chatModel(modelId: AlibabaChatModelId): LanguageModelV4;
37
+
38
+ /**
39
+ * Creates a model for text embeddings.
40
+ */
41
+ embedding(modelId: AlibabaEmbeddingModelId): EmbeddingModelV4;
42
+
43
+ /**
44
+ * Creates a model for text embeddings.
45
+ */
46
+ embeddingModel(modelId: AlibabaEmbeddingModelId): EmbeddingModelV4;
47
+
48
+ /**
49
+ * Creates a model for video generation.
50
+ */
51
+ video(modelId: AlibabaVideoModelId): Experimental_VideoModelV4;
52
+
53
+ /**
54
+ * Creates a model for video generation.
55
+ */
56
+ videoModel(modelId: AlibabaVideoModelId): Experimental_VideoModelV4;
57
+ }
58
+
59
+ export interface AlibabaProviderSettings {
60
+ /**
61
+ * Use a different URL prefix for API calls, e.g. to use proxy servers or regional endpoints.
62
+ * The default prefix is `https://dashscope-intl.aliyuncs.com/compatible-mode/v1`.
63
+ */
64
+ baseURL?: string;
65
+
66
+ /**
67
+ * Use a different URL prefix for video generation API calls.
68
+ * The video API uses the DashScope native endpoint (not the OpenAI-compatible endpoint).
69
+ * The default prefix is `https://dashscope-intl.aliyuncs.com`.
70
+ */
71
+ videoBaseURL?: string;
72
+
73
+ /**
74
+ * Use a different URL prefix for embedding API calls.
75
+ * The embedding API uses the DashScope native endpoint (not the OpenAI-compatible endpoint).
76
+ * The default prefix is `https://dashscope-intl.aliyuncs.com/api/v1`.
77
+ */
78
+ embeddingBaseURL?: string;
79
+
80
+ /**
81
+ * API key that is being sent using the `Authorization` header.
82
+ * It defaults to the `ALIBABA_API_KEY` environment variable.
83
+ */
84
+ apiKey?: string;
85
+
86
+ /**
87
+ * Custom headers to include in the requests.
88
+ */
89
+ headers?: Record<string, string>;
90
+
91
+ /**
92
+ * Custom fetch implementation. You can use it as a middleware to intercept requests,
93
+ * or to provide a custom fetch implementation for e.g. testing.
94
+ */
95
+ fetch?: FetchFunction;
96
+
97
+ /**
98
+ * Include usage information in streaming responses.
99
+ * When enabled, token usage will be included in the final chunk.
100
+ *
101
+ * @default true
102
+ */
103
+ includeUsage?: boolean;
104
+ }
105
+
106
+ /**
107
+ * Create an Alibaba Cloud (Qwen) provider instance.
108
+ */
109
+ export function createAlibaba(
110
+ options: AlibabaProviderSettings = {},
111
+ ): AlibabaProvider {
112
+ const baseURL =
113
+ withoutTrailingSlash(options.baseURL) ??
114
+ 'https://dashscope-intl.aliyuncs.com/compatible-mode/v1';
115
+
116
+ const videoBaseURL =
117
+ withoutTrailingSlash(options.videoBaseURL) ??
118
+ 'https://dashscope-intl.aliyuncs.com';
119
+
120
+ const embeddingBaseURL =
121
+ withoutTrailingSlash(options.embeddingBaseURL) ??
122
+ 'https://dashscope-intl.aliyuncs.com/api/v1';
123
+
124
+ const getHeaders = () =>
125
+ withUserAgentSuffix(
126
+ {
127
+ Authorization: `Bearer ${loadApiKey({
128
+ apiKey: options.apiKey,
129
+ environmentVariableName: 'ALIBABA_API_KEY',
130
+ description: 'Alibaba Cloud (DashScope)',
131
+ })}`,
132
+ ...options.headers,
133
+ },
134
+ `ai-sdk/alibaba/${VERSION}`,
135
+ );
136
+
137
+ const createLanguageModel = (modelId: AlibabaChatModelId) =>
138
+ new AlibabaChatLanguageModel(modelId, {
139
+ provider: 'alibaba.chat',
140
+ baseURL,
141
+ headers: getHeaders,
142
+ fetch: options.fetch,
143
+ includeUsage: options.includeUsage ?? true,
144
+ });
145
+
146
+ const createEmbeddingModel = (modelId: AlibabaEmbeddingModelId) =>
147
+ new AlibabaEmbeddingModel(modelId, {
148
+ provider: 'alibaba.embedding',
149
+ baseURL: embeddingBaseURL,
150
+ headers: getHeaders,
151
+ fetch: options.fetch,
152
+ });
153
+
154
+ const createVideoModel = (modelId: AlibabaVideoModelId) =>
155
+ new AlibabaVideoModel(modelId, {
156
+ provider: 'alibaba.video',
157
+ baseURL: videoBaseURL,
158
+ headers: getHeaders,
159
+ fetch: options.fetch,
160
+ });
161
+
162
+ const provider = function (modelId: AlibabaChatModelId) {
163
+ if (new.target) {
164
+ throw new Error(
165
+ 'The Alibaba model function cannot be called with the new keyword.',
166
+ );
167
+ }
168
+
169
+ return createLanguageModel(modelId);
170
+ };
171
+
172
+ provider.specificationVersion = 'v4' as const;
173
+ provider.languageModel = createLanguageModel;
174
+ provider.chatModel = createLanguageModel;
175
+ provider.embedding = createEmbeddingModel;
176
+ provider.embeddingModel = createEmbeddingModel;
177
+ provider.video = createVideoModel;
178
+ provider.videoModel = createVideoModel;
179
+
180
+ provider.imageModel = (modelId: string) => {
181
+ throw new NoSuchModelError({ modelId, modelType: 'imageModel' });
182
+ };
183
+
184
+ return provider;
185
+ }
186
+
187
+ export const alibaba = createAlibaba();
@@ -0,0 +1,77 @@
1
+ import { lazySchema, zodSchema } from '@ai-sdk/provider-utils';
2
+ import { z } from 'zod/v4';
3
+
4
+ export type AlibabaVideoModelOptions = {
5
+ /** Negative prompt to specify what to avoid (max 500 chars). */
6
+ negativePrompt?: string | null;
7
+ /** URL to audio file for audio-video sync (WAV/MP3, 3-30s, max 15MB). */
8
+ audioUrl?: string | null;
9
+ /** Enable prompt extension/rewriting for better generation. Defaults to true. */
10
+ promptExtend?: boolean | null;
11
+ /**
12
+ * Shot type: 'single' for single-shot or 'multi' for multi-shot narrative
13
+ * (wan2.6 and earlier; wan2.7 models describe shot structure in the prompt).
14
+ */
15
+ shotType?: 'single' | 'multi' | null;
16
+ /** Whether to add watermark to generated video. Defaults to false. */
17
+ watermark?: boolean | null;
18
+ /**
19
+ * Enable audio generation (for wan2.6 I2V/R2V models;
20
+ * wan2.7 models always generate audio).
21
+ */
22
+ audio?: boolean | null;
23
+ /**
24
+ * Reference URLs for reference-to-video mode (wan2.6 models).
25
+ * Array of URLs to images (0-5) and/or videos (0-3), max 5 total.
26
+ * Use character identifiers (character1, character2) in prompts to reference them.
27
+ */
28
+ referenceUrls?: string[] | null;
29
+ /**
30
+ * Explicit media array for reference-to-video mode (wan2.7 models).
31
+ * Overrides the automatic mapping from `inputReferences` and `frameImages`.
32
+ * Use `Image 1`, `Video 1`, etc. in prompts to reference media items
33
+ * (images and videos are counted separately, in array order).
34
+ */
35
+ media?: Array<{
36
+ type: 'reference_image' | 'reference_video' | 'first_frame';
37
+ /** Public URL, or a `data:{mime};base64,{data}` URI for images. */
38
+ url: string;
39
+ /** URL to an audio file used as voice reference for this media item. */
40
+ referenceVoice?: string | null;
41
+ }> | null;
42
+ /**
43
+ * Aspect ratio (wan2.7 text-to-video and reference-to-video models).
44
+ */
45
+ ratio?: '16:9' | '9:16' | '1:1' | '4:3' | '3:4' | null;
46
+ /** Polling interval in milliseconds. Defaults to 5000 (5 seconds). */
47
+ pollIntervalMs?: number | null;
48
+ /** Maximum wait time in milliseconds for video generation. Defaults to 600000 (10 minutes). */
49
+ pollTimeoutMs?: number | null;
50
+ [key: string]: unknown;
51
+ };
52
+
53
+ export const alibabaVideoModelOptionsSchema = lazySchema(() =>
54
+ zodSchema(
55
+ z.looseObject({
56
+ negativePrompt: z.string().nullish(),
57
+ audioUrl: z.string().nullish(),
58
+ promptExtend: z.boolean().nullish(),
59
+ shotType: z.enum(['single', 'multi']).nullish(),
60
+ watermark: z.boolean().nullish(),
61
+ audio: z.boolean().nullish(),
62
+ referenceUrls: z.array(z.string()).nullish(),
63
+ media: z
64
+ .array(
65
+ z.object({
66
+ type: z.enum(['reference_image', 'reference_video', 'first_frame']),
67
+ url: z.string(),
68
+ referenceVoice: z.string().nullish(),
69
+ }),
70
+ )
71
+ .nullish(),
72
+ ratio: z.enum(['16:9', '9:16', '1:1', '4:3', '3:4']).nullish(),
73
+ pollIntervalMs: z.number().positive().nullish(),
74
+ pollTimeoutMs: z.number().positive().nullish(),
75
+ }),
76
+ ),
77
+ );