Skip to content

Commit 49793e2

Browse files
authored
Merge pull request #821 from w3-bounty/feature/cloudflare-ai-gateway-clean
feat: Add Cloudflare AI Gateway support
2 parents e162a0a + d2a5da2 commit 49793e2

File tree

3 files changed

+148
-20
lines changed

3 files changed

+148
-20
lines changed

.env.example

+6
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,12 @@ FLOW_ENDPOINT_URL= # Default: https://mainnet.onflow.org
363363
INTERNET_COMPUTER_PRIVATE_KEY=
364364
INTERNET_COMPUTER_ADDRESS=
365365

366+
367+
#Cloudflare AI Gateway
368+
CLOUDFLARE_GW_ENABLED= # Set to true to enable Cloudflare AI Gateway
369+
CLOUDFLARE_AI_ACCOUNT_ID= # Cloudflare AI Account ID - found in the Cloudflare Dashboard under AI Gateway
370+
CLOUDFLARE_AI_GATEWAY_ID= # Cloudflare AI Gateway ID - found in the Cloudflare Dashboard under AI Gateway
371+
366372
# Aptos
367373
APTOS_PRIVATE_KEY= # Aptos private key
368374
APTOS_NETWORK= # Must be one of mainnet, testnet

docs/docs/guides/configuration.md

+53
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,59 @@ HEURIST_API_KEY=
7171
# Livepeer Settings
7272
LIVEPEER_GATEWAY_URL=
7373
```
74+
75+
### Cloudflare AI Gateway Integration
76+
77+
Eliza supports routing API calls through [Cloudflare AI Gateway](https://developers.cloudflare.com/ai-gateway/), which provides several benefits:
78+
79+
- Detailed analytics and monitoring of message traffic and response times
80+
- Cost optimization through request caching and usage tracking across providers
81+
- Improved latency through Cloudflare's global network
82+
- Comprehensive visibility into message content and token usage
83+
- Cost analysis and comparison between different AI providers
84+
- Usage patterns and trends visualization
85+
- Request/response logging for debugging and optimization
86+
87+
To enable Cloudflare AI Gateway:
88+
89+
```bash
90+
# Cloudflare AI Gateway Settings
91+
CLOUDFLARE_GW_ENABLED=true
92+
CLOUDFLARE_AI_ACCOUNT_ID=your-account-id
93+
CLOUDFLARE_AI_GATEWAY_ID=your-gateway-id
94+
```
95+
96+
Supported providers through Cloudflare AI Gateway:
97+
- OpenAI
98+
- Anthropic
99+
- Groq
100+
101+
When enabled, Eliza will automatically route requests through your Cloudflare AI Gateway endpoint. The gateway URL is constructed in the format:
102+
```
103+
https://gateway.ai.cloudflare.com/v1/${accountId}/${gatewayId}/${provider}
104+
```
105+
106+
If the gateway configuration is incomplete or disabled, Eliza will fall back to direct API calls.
107+
108+
```bash
109+
# Cloudflare AI Gateway Settings
110+
CLOUDFLARE_GW_ENABLED=true
111+
CLOUDFLARE_AI_ACCOUNT_ID=your-account-id
112+
CLOUDFLARE_AI_GATEWAY_ID=your-gateway-id
113+
```
114+
115+
Supported providers through Cloudflare AI Gateway:
116+
- OpenAI
117+
- Anthropic
118+
- Groq
119+
120+
When enabled, Eliza will automatically route requests through your Cloudflare AI Gateway endpoint. The gateway URL is constructed in the format:
121+
```
122+
https://gateway.ai.cloudflare.com/v1/${accountId}/${gatewayId}/${provider}
123+
```
124+
125+
If the gateway configuration is incomplete or disabled, Eliza will fall back to direct API calls.
126+
74127
### Image Generation
75128

76129
Configure image generation in your character file:

packages/core/src/generation.ts

+89-20
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,50 @@ async function truncateTiktoken(
163163
}
164164
}
165165

166+
/**
167+
* Gets the Cloudflare Gateway base URL for a specific provider if enabled
168+
* @param runtime The runtime environment
169+
* @param provider The model provider name
170+
* @returns The Cloudflare Gateway base URL if enabled, undefined otherwise
171+
*/
172+
function getCloudflareGatewayBaseURL(runtime: IAgentRuntime, provider: string): string | undefined {
173+
const isCloudflareEnabled = runtime.getSetting("CLOUDFLARE_GW_ENABLED") === "true";
174+
const cloudflareAccountId = runtime.getSetting("CLOUDFLARE_AI_ACCOUNT_ID");
175+
const cloudflareGatewayId = runtime.getSetting("CLOUDFLARE_AI_GATEWAY_ID");
176+
177+
elizaLogger.debug("Cloudflare Gateway Configuration:", {
178+
isEnabled: isCloudflareEnabled,
179+
hasAccountId: !!cloudflareAccountId,
180+
hasGatewayId: !!cloudflareGatewayId,
181+
provider: provider
182+
});
183+
184+
if (!isCloudflareEnabled) {
185+
elizaLogger.debug("Cloudflare Gateway is not enabled");
186+
return undefined;
187+
}
188+
189+
if (!cloudflareAccountId) {
190+
elizaLogger.warn("Cloudflare Gateway is enabled but CLOUDFLARE_AI_ACCOUNT_ID is not set");
191+
return undefined;
192+
}
193+
194+
if (!cloudflareGatewayId) {
195+
elizaLogger.warn("Cloudflare Gateway is enabled but CLOUDFLARE_AI_GATEWAY_ID is not set");
196+
return undefined;
197+
}
198+
199+
const baseURL = `https://gateway.ai.cloudflare.com/v1/${cloudflareAccountId}/${cloudflareGatewayId}/${provider.toLowerCase()}`;
200+
elizaLogger.info("Using Cloudflare Gateway:", {
201+
provider,
202+
baseURL,
203+
accountId: cloudflareAccountId,
204+
gatewayId: cloudflareGatewayId
205+
});
206+
207+
return baseURL;
208+
}
209+
166210
/**
167211
* Send a message to the model for a text generateText - receive a string back and parse how you'd like
168212
* @param opts - The options for the generateText request.
@@ -242,6 +286,16 @@ export async function generateText({
242286
}
243287

244288
const provider = runtime.modelProvider;
289+
elizaLogger.debug("Provider settings:", {
290+
provider,
291+
hasRuntime: !!runtime,
292+
runtimeSettings: {
293+
CLOUDFLARE_GW_ENABLED: runtime.getSetting("CLOUDFLARE_GW_ENABLED"),
294+
CLOUDFLARE_AI_ACCOUNT_ID: runtime.getSetting("CLOUDFLARE_AI_ACCOUNT_ID"),
295+
CLOUDFLARE_AI_GATEWAY_ID: runtime.getSetting("CLOUDFLARE_AI_GATEWAY_ID")
296+
}
297+
});
298+
245299
const endpoint =
246300
runtime.character.modelEndpointOverride || getEndpoint(provider);
247301
const modelSettings = getModelSettings(runtime.modelProvider, modelClass);
@@ -356,13 +410,17 @@ export async function generateText({
356410
case ModelProviderName.LLAMACLOUD:
357411
case ModelProviderName.NANOGPT:
358412
case ModelProviderName.HYPERBOLIC:
413+
case ModelProviderName.TOGETHER:
359414
case ModelProviderName.NINETEEN_AI:
360415
case ModelProviderName.TOGETHER:
361416
case ModelProviderName.AKASH_CHAT_API: {
362-
elizaLogger.debug("Initializing OpenAI model.");
417+
elizaLogger.debug("Initializing OpenAI model with Cloudflare check");
418+
const baseURL = getCloudflareGatewayBaseURL(runtime, 'openai') || endpoint;
419+
420+
//elizaLogger.debug("OpenAI baseURL result:", { baseURL });
363421
const openai = createOpenAI({
364422
apiKey,
365-
baseURL: endpoint,
423+
baseURL,
366424
fetch: runtime.fetch,
367425
});
368426

@@ -430,10 +488,7 @@ export async function generateText({
430488
const { text: openaiResponse } = await aiGenerateText({
431489
model: openai.languageModel(model),
432490
prompt: context,
433-
system:
434-
runtime.character.system ??
435-
settings.SYSTEM_PROMPT ??
436-
undefined,
491+
system: runtime.character.system ?? settings.SYSTEM_PROMPT ?? undefined,
437492
temperature: temperature,
438493
maxTokens: max_response_length,
439494
frequencyPenalty: frequency_penalty,
@@ -474,13 +529,11 @@ export async function generateText({
474529
}
475530

476531
case ModelProviderName.ANTHROPIC: {
477-
elizaLogger.debug("Initializing Anthropic model.");
478-
479-
const anthropic = createAnthropic({
480-
apiKey,
481-
fetch: runtime.fetch,
482-
});
532+
elizaLogger.debug("Initializing Anthropic model with Cloudflare check");
533+
const baseURL = getCloudflareGatewayBaseURL(runtime, 'anthropic') || "https://api.anthropic.com/v1";
534+
elizaLogger.debug("Anthropic baseURL result:", { baseURL });
483535

536+
const anthropic = createAnthropic({ apiKey, baseURL, fetch: runtime.fetch });
484537
const { text: anthropicResponse } = await aiGenerateText({
485538
model: anthropic.languageModel(model),
486539
prompt: context,
@@ -568,26 +621,30 @@ export async function generateText({
568621
}
569622

570623
case ModelProviderName.GROQ: {
571-
const groq = createGroq({ apiKey, fetch: runtime.fetch });
624+
elizaLogger.debug("Initializing Groq model with Cloudflare check");
625+
const baseURL = getCloudflareGatewayBaseURL(runtime, 'groq');
626+
elizaLogger.debug("Groq baseURL result:", { baseURL });
627+
const groq = createGroq({ apiKey, fetch: runtime.fetch, baseURL });
572628

573629
const { text: groqResponse } = await aiGenerateText({
574630
model: groq.languageModel(model),
575631
prompt: context,
576-
temperature: temperature,
632+
temperature,
577633
system:
578634
runtime.character.system ??
579635
settings.SYSTEM_PROMPT ??
580636
undefined,
581-
tools: tools,
637+
tools,
582638
onStepFinish: onStepFinish,
583-
maxSteps: maxSteps,
639+
maxSteps,
584640
maxTokens: max_response_length,
585641
frequencyPenalty: frequency_penalty,
586642
presencePenalty: presence_penalty,
587-
experimental_telemetry: experimental_telemetry,
643+
experimental_telemetry,
588644
});
589645

590646
response = groqResponse;
647+
elizaLogger.debug("Received response from Groq model.");
591648
break;
592649
}
593650

@@ -1833,8 +1890,10 @@ async function handleOpenAI({
18331890
schemaDescription,
18341891
mode = "json",
18351892
modelOptions,
1893+
provider,
1894+
runtime,
18361895
}: ProviderOptions): Promise<GenerateObjectResult<unknown>> {
1837-
const baseURL = models.openai.endpoint || undefined;
1896+
const baseURL = getCloudflareGatewayBaseURL(runtime, 'openai') || models.openai.endpoint;
18381897
const openai = createOpenAI({ apiKey, baseURL });
18391898
return await aiGenerateObject({
18401899
model: openai.languageModel(model),
@@ -1860,8 +1919,13 @@ async function handleAnthropic({
18601919
schemaDescription,
18611920
mode = "json",
18621921
modelOptions,
1922+
runtime,
18631923
}: ProviderOptions): Promise<GenerateObjectResult<unknown>> {
1864-
const anthropic = createAnthropic({ apiKey });
1924+
elizaLogger.debug("Handling Anthropic request with Cloudflare check");
1925+
const baseURL = getCloudflareGatewayBaseURL(runtime, 'anthropic');
1926+
elizaLogger.debug("Anthropic handleAnthropic baseURL:", { baseURL });
1927+
1928+
const anthropic = createAnthropic({ apiKey, baseURL });
18651929
return await aiGenerateObject({
18661930
model: anthropic.languageModel(model),
18671931
schema,
@@ -1912,8 +1976,13 @@ async function handleGroq({
19121976
schemaDescription,
19131977
mode = "json",
19141978
modelOptions,
1979+
runtime,
19151980
}: ProviderOptions): Promise<GenerateObjectResult<unknown>> {
1916-
const groq = createGroq({ apiKey });
1981+
elizaLogger.debug("Handling Groq request with Cloudflare check");
1982+
const baseURL = getCloudflareGatewayBaseURL(runtime, 'groq');
1983+
elizaLogger.debug("Groq handleGroq baseURL:", { baseURL });
1984+
1985+
const groq = createGroq({ apiKey, baseURL });
19171986
return await aiGenerateObject({
19181987
model: groq.languageModel(model),
19191988
schema,

0 commit comments

Comments
 (0)