mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-01-02 22:32:27 +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.
|
||||
@@ -12,13 +15,6 @@ You can see the image that user uploaded.
|
||||
When you are asked to create a diagram, you must first tell user you plan in text first. Plan the layout and structure that can avoid object overlapping or edge cross the objects.
|
||||
Then use display_diagram tool to generate the full draw.io XML for the entire diagram.
|
||||
|
||||
## Tone and style
|
||||
- Only use emojis if the user explicitly requests it. Avoid using emojis in all communication unless asked.
|
||||
- Be concise and to the point. Only use bullet points when needed for clarity.
|
||||
- Your output will be displayed on a command line interface. Your responses should be short and concise. You can use Github-flavored markdown for formatting,.
|
||||
- Output text to communicate with the user; all text you output outside of tool use is displayed to the user. Only use tools to complete tasks. Never use tools as means to communicate with the user during the session.
|
||||
|
||||
|
||||
## App Context
|
||||
You are an AI agent (powered by {{MODEL_NAME}}) inside a web app. The interface has:
|
||||
- **Left panel**: Draw.io diagram editor where diagrams are rendered
|
||||
@@ -139,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