Skip to content

Commit 7910ae8

Browse files
authored
feat (providers): support custom fetch implementations (#1955)
1 parent 1af45ce commit 7910ae8

24 files changed

+276
-5
lines changed

.changeset/nasty-goats-hammer.md

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@ai-sdk/provider-utils': patch
3+
'@ai-sdk/anthropic': patch
4+
'@ai-sdk/mistral': patch
5+
'@ai-sdk/google': patch
6+
'@ai-sdk/openai': patch
7+
'@ai-sdk/azure': patch
8+
---
9+
10+
feat (providers): support custom fetch implementations

content/providers/01-ai-sdk-providers/01-openai.mdx

+7
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ You can use the following optional settings to customize the OpenAI provider ins
6767

6868
Custom headers to include in the requests.
6969

70+
- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
71+
72+
Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
73+
Defaults to the global `fetch` function.
74+
You can use it as a middleware to intercept requests,
75+
or to provide a custom fetch implementation for e.g. testing.
76+
7077
- **compatibility** _"strict" | "compatible"_
7178

7279
OpenAI compatibility mode. Should be set to `strict` when using the OpenAI API,

content/providers/01-ai-sdk-providers/02-azure.mdx

+7
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ You can use the following optional settings to customize the OpenAI provider ins
5454
API key that is being send using the `api-key` header.
5555
It defaults to the `AZURE_API_KEY` environment variable.
5656

57+
- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
58+
59+
Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
60+
Defaults to the global `fetch` function.
61+
You can use it as a middleware to intercept requests,
62+
or to provide a custom fetch implementation for e.g. testing.
63+
5764
## Language Models
5865

5966
The Azure OpenAI provider instance is a function that you can invoke to create a language model:

content/providers/01-ai-sdk-providers/05-anthropic.mdx

+7
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ You can use the following optional settings to customize the Google Generative A
5757

5858
Custom headers to include in the requests.
5959

60+
- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
61+
62+
Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
63+
Defaults to the global `fetch` function.
64+
You can use it as a middleware to intercept requests,
65+
or to provide a custom fetch implementation for e.g. testing.
66+
6067
## Language Models
6168

6269
You can create models that call the [Anthropic Messages API](https://docs.anthropic.com/claude/reference/messages_post) using the provider instance.

content/providers/01-ai-sdk-providers/10-google-generative-ai.mdx

+7
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ You can use the following optional settings to customize the Google Generative A
5757

5858
Custom headers to include in the requests.
5959

60+
- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
61+
62+
Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
63+
Defaults to the global `fetch` function.
64+
You can use it as a middleware to intercept requests,
65+
or to provide a custom fetch implementation for e.g. testing.
66+
6067
## Language Models
6168

6269
You can create models that call the [Google Generative AI API](https://ai.google.dev/api/rest) using the provider instance.

content/providers/01-ai-sdk-providers/20-mistral.mdx

+7
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ You can use the following optional settings to customize the Mistral provider in
5858

5959
Custom headers to include in the requests.
6060

61+
- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
62+
63+
Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
64+
Defaults to the global `fetch` function.
65+
You can use it as a middleware to intercept requests,
66+
or to provide a custom fetch implementation for e.g. testing.
67+
6168
## Language Models
6269

6370
You can create models that call the [Mistral chat API](https://docs.mistral.ai/api/#operation/createChatCompletion) using provider instance.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { createAnthropic } from '@ai-sdk/anthropic';
2+
import { generateText } from 'ai';
3+
import dotenv from 'dotenv';
4+
5+
dotenv.config();
6+
7+
const anthropic = createAnthropic({
8+
// example fetch wrapper that logs the URL:
9+
fetch: async (url, options) => {
10+
console.log(`Fetching ${url}`);
11+
const result = await fetch(url, options);
12+
console.log(`Fetched ${url}`);
13+
console.log();
14+
return result;
15+
},
16+
});
17+
18+
async function main() {
19+
const result = await generateText({
20+
model: anthropic('claude-3-haiku-20240307'),
21+
prompt: 'Invent a new holiday and describe its traditions.',
22+
});
23+
24+
console.log(result.text);
25+
}
26+
27+
main().catch(console.error);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { createAzure } from '@ai-sdk/azure';
2+
import { generateText } from 'ai';
3+
import dotenv from 'dotenv';
4+
5+
dotenv.config();
6+
7+
const azure = createAzure({
8+
// example fetch wrapper that logs the URL:
9+
fetch: async (url, options) => {
10+
console.log(`Fetching ${url}`);
11+
const result = await fetch(url, options);
12+
console.log(`Fetched ${url}`);
13+
console.log();
14+
return result;
15+
},
16+
});
17+
18+
async function main() {
19+
const result = await generateText({
20+
model: azure('v0-gpt-35-turbo'), // use your own deployment
21+
prompt: 'Invent a new holiday and describe its traditions.',
22+
});
23+
24+
console.log(result.text);
25+
}
26+
27+
main().catch(console.error);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { createGoogleGenerativeAI } from '@ai-sdk/google';
2+
import { generateText } from 'ai';
3+
import dotenv from 'dotenv';
4+
5+
dotenv.config();
6+
7+
const google = createGoogleGenerativeAI({
8+
// example fetch wrapper that logs the URL:
9+
fetch: async (url, options) => {
10+
console.log(`Fetching ${url}`);
11+
const result = await fetch(url, options);
12+
console.log(`Fetched ${url}`);
13+
console.log();
14+
return result;
15+
},
16+
});
17+
18+
async function main() {
19+
const result = await generateText({
20+
model: google('models/gemini-pro'),
21+
prompt: 'Invent a new holiday and describe its traditions.',
22+
});
23+
24+
console.log(result.text);
25+
}
26+
27+
main().catch(console.error);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { createMistral } from '@ai-sdk/mistral';
2+
import { generateText } from 'ai';
3+
import dotenv from 'dotenv';
4+
5+
dotenv.config();
6+
7+
const mistral = createMistral({
8+
// example fetch wrapper that logs the URL:
9+
fetch: async (url, options) => {
10+
console.log(`Fetching ${url}`);
11+
const result = await fetch(url, options);
12+
console.log(`Fetched ${url}`);
13+
console.log();
14+
return result;
15+
},
16+
});
17+
18+
async function main() {
19+
const result = await generateText({
20+
model: mistral('open-mistral-7b'),
21+
prompt: 'Invent a new holiday and describe its traditions.',
22+
});
23+
24+
console.log(result.text);
25+
}
26+
27+
main().catch(console.error);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { createOpenAI } from '@ai-sdk/openai';
2+
import { generateText } from 'ai';
3+
import dotenv from 'dotenv';
4+
5+
dotenv.config();
6+
7+
const openai = createOpenAI({
8+
// example fetch wrapper that logs the URL:
9+
fetch: async (url, options) => {
10+
console.log(`Fetching ${url}`);
11+
const result = await fetch(url, options);
12+
console.log(`Fetched ${url}`);
13+
console.log();
14+
return result;
15+
},
16+
});
17+
18+
async function main() {
19+
const result = await generateText({
20+
model: openai('gpt-3.5-turbo'),
21+
prompt: 'Invent a new holiday and describe its traditions.',
22+
});
23+
24+
console.log(result.text);
25+
}
26+
27+
main().catch(console.error);

packages/anthropic/src/anthropic-messages-language-model.ts

+3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ type AnthropicMessagesConfig = {
2525
provider: string;
2626
baseURL: string;
2727
headers: () => Record<string, string | undefined>;
28+
fetch?: typeof fetch;
2829
};
2930

3031
export class AnthropicMessagesLanguageModel implements LanguageModelV1 {
@@ -163,6 +164,7 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV1 {
163164
anthropicMessagesResponseSchema,
164165
),
165166
abortSignal: options.abortSignal,
167+
fetch: this.config.fetch,
166168
});
167169

168170
const { messages: rawPrompt, ...rawSettings } = args;
@@ -222,6 +224,7 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV1 {
222224
anthropicMessagesChunkSchema,
223225
),
224226
abortSignal: options.abortSignal,
227+
fetch: this.config.fetch,
225228
});
226229

227230
const { messages: rawPrompt, ...rawSettings } = args;

packages/anthropic/src/anthropic-provider.ts

+7
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ Custom headers to include in the requests.
5454
*/
5555
headers?: Record<string, string>;
5656

57+
/**
58+
Custom fetch implementation. You can use it as a middleware to intercept requests,
59+
or to provide a custom fetch implementation for e.g. testing.
60+
*/
61+
fetch?: typeof fetch;
62+
5763
generateId?: () => string;
5864
}
5965

@@ -86,6 +92,7 @@ export function createAnthropic(
8692
provider: 'anthropic.messages',
8793
baseURL,
8894
headers: getHeaders,
95+
fetch: options.fetch,
8996
});
9097

9198
const provider = function (

packages/azure/src/azure-openai-provider.ts

+7
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ Name of the Azure OpenAI resource.
2929
API key for authenticating requests.
3030
*/
3131
apiKey?: string;
32+
33+
/**
34+
Custom fetch implementation. You can use it as a middleware to intercept requests,
35+
or to provide a custom fetch implementation for e.g. testing.
36+
*/
37+
fetch?: typeof fetch;
3238
}
3339

3440
/**
@@ -63,6 +69,7 @@ export function createAzure(
6369
url: ({ path, modelId }) =>
6470
`https://${getResourceName()}.openai.azure.com/openai/deployments/${modelId}${path}?api-version=2024-05-01-preview`,
6571
compatibility: 'compatible',
72+
fetch: options.fetch,
6673
});
6774

6875
const provider = function (

packages/google/src/google-generative-ai-language-model.ts

+3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ type GoogleGenerativeAIConfig = {
2626
baseURL: string;
2727
headers: () => Record<string, string | undefined>;
2828
generateId: () => string;
29+
fetch?: typeof fetch;
2930
};
3031

3132
export class GoogleGenerativeAILanguageModel implements LanguageModelV1 {
@@ -156,6 +157,7 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV1 {
156157
failedResponseHandler: googleFailedResponseHandler,
157158
successfulResponseHandler: createJsonResponseHandler(responseSchema),
158159
abortSignal: options.abortSignal,
160+
fetch: this.config.fetch,
159161
});
160162

161163
const { contents: rawPrompt, ...rawSettings } = args;
@@ -197,6 +199,7 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV1 {
197199
failedResponseHandler: googleFailedResponseHandler,
198200
successfulResponseHandler: createEventSourceResponseHandler(chunkSchema),
199201
abortSignal: options.abortSignal,
202+
fetch: this.config.fetch,
200203
});
201204

202205
const { contents: rawPrompt, ...rawSettings } = args;

packages/google/src/google-provider.ts

+38-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { Google } from './google-facade';
1+
import {
2+
generateId,
3+
loadApiKey,
4+
withoutTrailingSlash,
5+
} from '@ai-sdk/provider-utils';
26
import { GoogleGenerativeAILanguageModel } from './google-generative-ai-language-model';
37
import {
48
GoogleGenerativeAIModelId,
@@ -48,6 +52,12 @@ Custom headers to include in the requests.
4852
*/
4953
headers?: Record<string, string>;
5054

55+
/**
56+
Custom fetch implementation. You can use it as a middleware to intercept requests,
57+
or to provide a custom fetch implementation for e.g. testing.
58+
*/
59+
fetch?: typeof fetch;
60+
5161
generateId?: () => string;
5262
}
5363

@@ -57,7 +67,30 @@ Create a Google Generative AI provider instance.
5767
export function createGoogleGenerativeAI(
5868
options: GoogleGenerativeAIProviderSettings = {},
5969
): GoogleGenerativeAIProvider {
60-
const google = new Google(options);
70+
const baseURL =
71+
withoutTrailingSlash(options.baseURL ?? options.baseUrl) ??
72+
'https://generativelanguage.googleapis.com/v1beta';
73+
74+
const getHeaders = () => ({
75+
'x-goog-api-key': loadApiKey({
76+
apiKey: options.apiKey,
77+
environmentVariableName: 'GOOGLE_GENERATIVE_AI_API_KEY',
78+
description: 'Google Generative AI',
79+
}),
80+
...options.headers,
81+
});
82+
83+
const createChatModel = (
84+
modelId: GoogleGenerativeAIModelId,
85+
settings: GoogleGenerativeAISettings = {},
86+
) =>
87+
new GoogleGenerativeAILanguageModel(modelId, settings, {
88+
provider: 'google.generative-ai',
89+
baseURL,
90+
headers: getHeaders,
91+
generateId: options.generateId ?? generateId,
92+
fetch: options.fetch,
93+
});
6194

6295
const provider = function (
6396
modelId: GoogleGenerativeAIModelId,
@@ -69,11 +102,11 @@ export function createGoogleGenerativeAI(
69102
);
70103
}
71104

72-
return google.chat(modelId, settings);
105+
return createChatModel(modelId, settings);
73106
};
74107

75-
provider.chat = google.chat.bind(google);
76-
provider.generativeAI = google.generativeAI.bind(google);
108+
provider.chat = createChatModel;
109+
provider.generativeAI = createChatModel;
77110

78111
return provider as GoogleGenerativeAIProvider;
79112
}

0 commit comments

Comments
 (0)