mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-09-02 01:20:23 +08:00
Compare commits
3 Commits
chore/add-
...
cloudflare
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0dd7b2383e | ||
|
|
167f5ed36a | ||
|
|
cd8e0e2263 |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -41,4 +41,9 @@ yarn-error.log*
|
||||
next-env.d.ts
|
||||
push-via-ec2.sh
|
||||
.claude/settings.local.json
|
||||
.playwright-mcp/
|
||||
.playwright-mcp/
|
||||
|
||||
# cloudflare
|
||||
.open-next/
|
||||
.dev.vars
|
||||
.wrangler/
|
||||
3
cloudflare-env.d.ts
vendored
Normal file
3
cloudflare-env.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
interface CloudflareEnv {
|
||||
ASSETS: Fetcher
|
||||
}
|
||||
@@ -1,7 +1,15 @@
|
||||
import { LangfuseSpanProcessor } from "@langfuse/otel"
|
||||
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node"
|
||||
|
||||
export function register() {
|
||||
// Skip on edge/worker runtime (Cloudflare Workers, Vercel Edge)
|
||||
// OpenTelemetry Node SDK requires Node.js-specific APIs
|
||||
if (
|
||||
typeof process === "undefined" ||
|
||||
!process.versions?.node ||
|
||||
// @ts-expect-error - EdgeRuntime is a global in edge environments
|
||||
typeof EdgeRuntime !== "undefined"
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
// Skip telemetry if Langfuse env vars are not configured
|
||||
if (!process.env.LANGFUSE_PUBLIC_KEY || !process.env.LANGFUSE_SECRET_KEY) {
|
||||
console.warn(
|
||||
@@ -10,12 +18,16 @@ export function register() {
|
||||
return
|
||||
}
|
||||
|
||||
// Dynamic imports to avoid bundling Node.js-specific modules in edge builds
|
||||
const { LangfuseSpanProcessor } = require("@langfuse/otel")
|
||||
const { NodeTracerProvider } = require("@opentelemetry/sdk-trace-node")
|
||||
|
||||
const langfuseSpanProcessor = new LangfuseSpanProcessor({
|
||||
publicKey: process.env.LANGFUSE_PUBLIC_KEY,
|
||||
secretKey: process.env.LANGFUSE_SECRET_KEY,
|
||||
baseUrl: process.env.LANGFUSE_BASEURL,
|
||||
// Filter out Next.js HTTP request spans so AI SDK spans become root traces
|
||||
shouldExportSpan: ({ otelSpan }) => {
|
||||
shouldExportSpan: ({ otelSpan }: { otelSpan: { name: string } }) => {
|
||||
const spanName = otelSpan.name
|
||||
// Skip Next.js HTTP infrastructure spans
|
||||
if (
|
||||
|
||||
@@ -4,10 +4,16 @@ import { azure, createAzure } from "@ai-sdk/azure"
|
||||
import { createDeepSeek, deepseek } from "@ai-sdk/deepseek"
|
||||
import { createGoogleGenerativeAI, google } from "@ai-sdk/google"
|
||||
import { createOpenAI, openai } from "@ai-sdk/openai"
|
||||
import { fromNodeProviderChain } from "@aws-sdk/credential-providers"
|
||||
import { createOpenRouter } from "@openrouter/ai-sdk-provider"
|
||||
import { createOllama, ollama } from "ollama-ai-provider-v2"
|
||||
|
||||
// Detect if running in edge/worker runtime (Cloudflare Workers, Vercel Edge, etc.)
|
||||
const isEdgeRuntime =
|
||||
typeof process === "undefined" ||
|
||||
!process.versions?.node ||
|
||||
// @ts-expect-error - EdgeRuntime is a global in edge environments
|
||||
typeof EdgeRuntime !== "undefined"
|
||||
|
||||
export type ProviderName =
|
||||
| "bedrock"
|
||||
| "openai"
|
||||
@@ -166,12 +172,37 @@ export function getAIModel(): ModelConfig {
|
||||
|
||||
switch (provider) {
|
||||
case "bedrock": {
|
||||
// Use credential provider chain for IAM role support (Amplify, Lambda, etc.)
|
||||
// Falls back to env vars (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY) for local dev
|
||||
const bedrockProvider = createAmazonBedrock({
|
||||
// Edge runtime (Cloudflare Workers, etc.) requires explicit credentials
|
||||
// Node.js runtime can use IAM role chain (Amplify, Lambda, etc.)
|
||||
const bedrockConfig: Parameters<typeof createAmazonBedrock>[0] = {
|
||||
region: process.env.AWS_REGION || "us-west-2",
|
||||
credentialProvider: fromNodeProviderChain(),
|
||||
})
|
||||
}
|
||||
|
||||
if (isEdgeRuntime) {
|
||||
// Edge runtime: use explicit credentials from env vars
|
||||
if (
|
||||
!process.env.AWS_ACCESS_KEY_ID ||
|
||||
!process.env.AWS_SECRET_ACCESS_KEY
|
||||
) {
|
||||
throw new Error(
|
||||
"AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are required for Bedrock on edge runtime (Cloudflare Workers)",
|
||||
)
|
||||
}
|
||||
bedrockConfig.accessKeyId = process.env.AWS_ACCESS_KEY_ID
|
||||
bedrockConfig.secretAccessKey =
|
||||
process.env.AWS_SECRET_ACCESS_KEY
|
||||
if (process.env.AWS_SESSION_TOKEN) {
|
||||
bedrockConfig.sessionToken = process.env.AWS_SESSION_TOKEN
|
||||
}
|
||||
} else {
|
||||
// Node.js runtime: use credential provider chain for IAM role support
|
||||
const {
|
||||
fromNodeProviderChain,
|
||||
} = require("@aws-sdk/credential-providers")
|
||||
bedrockConfig.credentialProvider = fromNodeProviderChain()
|
||||
}
|
||||
|
||||
const bedrockProvider = createAmazonBedrock(bedrockConfig)
|
||||
model = bedrockProvider(modelId)
|
||||
// Add Anthropic beta options if using Claude models via Bedrock
|
||||
if (modelId.includes("anthropic.claude")) {
|
||||
|
||||
@@ -84,9 +84,7 @@ export function getTelemetryConfig(params: {
|
||||
|
||||
return {
|
||||
isEnabled: true,
|
||||
// Disable automatic input recording to avoid uploading large base64 images to Langfuse media
|
||||
// User text input is recorded manually via setTraceInput
|
||||
recordInputs: false,
|
||||
recordInputs: true,
|
||||
recordOutputs: true,
|
||||
metadata: {
|
||||
sessionId: params.sessionId,
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
/**
|
||||
* System prompts for different AI models
|
||||
* Extended prompt is used for models with higher cache token minimums (Opus 4.5, Haiku 4.5)
|
||||
*
|
||||
* Token counting utilities are in a separate file (token-counter.ts) to avoid
|
||||
* WebAssembly issues with Next.js server-side rendering.
|
||||
*/
|
||||
|
||||
// Default system prompt (~2700 tokens) - works with all models
|
||||
// Default system prompt (~1900 tokens) - works with all models
|
||||
export const DEFAULT_SYSTEM_PROMPT = `
|
||||
You are an expert diagram creation assistant specializing in draw.io XML generation.
|
||||
Your primary function is chat with user and crafting clear, well-organized visual diagrams through precise XML specifications.
|
||||
@@ -132,8 +135,8 @@ Common styles:
|
||||
|
||||
`
|
||||
|
||||
// Extended additions (~1800 tokens) - appended for models with 4000 token cache minimum
|
||||
// Total EXTENDED_SYSTEM_PROMPT = ~4500 tokens
|
||||
// Extended additions (~2600 tokens) - appended for models with 4000 token cache minimum
|
||||
// Total EXTENDED_SYSTEM_PROMPT = ~4400 tokens
|
||||
const EXTENDED_ADDITIONS = `
|
||||
|
||||
## Extended Tool Reference
|
||||
|
||||
38
lib/token-counter.ts
Normal file
38
lib/token-counter.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Token counting utilities using Anthropic's tokenizer
|
||||
*
|
||||
* This file is separate from system-prompts.ts because the @anthropic-ai/tokenizer
|
||||
* package uses WebAssembly which doesn't work well with Next.js server-side rendering.
|
||||
* Import this file only in scripts or client-side code, not in API routes.
|
||||
*/
|
||||
|
||||
import { countTokens } from "@anthropic-ai/tokenizer"
|
||||
import { DEFAULT_SYSTEM_PROMPT, EXTENDED_SYSTEM_PROMPT } from "./system-prompts"
|
||||
|
||||
/**
|
||||
* Count the number of tokens in a text string using Anthropic's tokenizer
|
||||
* @param text - The text to count tokens for
|
||||
* @returns The number of tokens
|
||||
*/
|
||||
export function countTextTokens(text: string): number {
|
||||
return countTokens(text)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get token counts for the system prompts
|
||||
* Useful for debugging and optimizing prompt sizes
|
||||
* @returns Object with token counts for default and extended prompts
|
||||
*/
|
||||
export function getSystemPromptTokenCounts(): {
|
||||
default: number
|
||||
extended: number
|
||||
additions: number
|
||||
} {
|
||||
const defaultTokens = countTokens(DEFAULT_SYSTEM_PROMPT)
|
||||
const extendedTokens = countTokens(EXTENDED_SYSTEM_PROMPT)
|
||||
return {
|
||||
default: defaultTokens,
|
||||
extended: extendedTokens,
|
||||
additions: extendedTokens - defaultTokens,
|
||||
}
|
||||
}
|
||||
3
open-next.config.ts
Normal file
3
open-next.config.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { defineCloudflareConfig } from "@opennextjs/cloudflare"
|
||||
|
||||
export default defineCloudflareConfig()
|
||||
8429
package-lock.json
generated
8429
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
@@ -10,7 +10,11 @@
|
||||
"lint": "biome lint .",
|
||||
"format": "biome check --write .",
|
||||
"check": "biome ci",
|
||||
"prepare": "husky"
|
||||
"prepare": "husky",
|
||||
"cf:build": "opennextjs-cloudflare build",
|
||||
"cf:preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview",
|
||||
"cf:deploy": "opennextjs-cloudflare build && opennextjs-cloudflare deploy",
|
||||
"cf:typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/amazon-bedrock": "^3.0.62",
|
||||
@@ -25,6 +29,7 @@
|
||||
"@langfuse/otel": "^4.4.4",
|
||||
"@langfuse/tracing": "^4.4.9",
|
||||
"@next/third-parties": "^16.0.6",
|
||||
"@opennextjs/cloudflare": "^1.14.4",
|
||||
"@openrouter/ai-sdk-provider": "^1.2.3",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.208.0",
|
||||
"@opentelemetry/sdk-trace-node": "^2.2.0",
|
||||
@@ -66,6 +71,7 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anthropic-ai/tokenizer": "^0.0.4",
|
||||
"@biomejs/biome": "2.3.8",
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"@tailwindcss/typography": "^0.5.19",
|
||||
@@ -78,6 +84,7 @@
|
||||
"husky": "^9.1.7",
|
||||
"lint-staged": "^16.2.7",
|
||||
"tailwindcss": "^4",
|
||||
"typescript": "^5"
|
||||
"typescript": "^5",
|
||||
"wrangler": "^4.53.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"cloudflare-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts",
|
||||
|
||||
8
wrangler.toml
Normal file
8
wrangler.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
main = ".open-next/worker.js"
|
||||
name = "next-ai-draw-io"
|
||||
compatibility_date = "2024-09-23"
|
||||
compatibility_flags = ["nodejs_compat"]
|
||||
|
||||
[assets]
|
||||
directory = ".open-next/assets"
|
||||
binding = "ASSETS"
|
||||
Reference in New Issue
Block a user