mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-01-02 22:32:27 +08:00
Compare commits
19 Commits
feature/im
...
feat/add-d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45ab934288 | ||
|
|
cd012f5e2f | ||
|
|
d4fb635d98 | ||
|
|
14740e35a8 | ||
|
|
5b31216917 | ||
|
|
c7d0260328 | ||
|
|
d2d4dd01cc | ||
|
|
b4679f6598 | ||
|
|
0d0d553e23 | ||
|
|
6e6de1eba6 | ||
|
|
00af87edbe | ||
|
|
468d6c0276 | ||
|
|
14f74b076f | ||
|
|
78d9229ca3 | ||
|
|
d8e0a1daad | ||
|
|
32e75ab556 | ||
|
|
b87e3a2de9 | ||
|
|
b32d42d962 | ||
|
|
b4d0c6c18b |
6
.eslintrc.json
Normal file
6
.eslintrc.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"extends": [
|
||||||
|
"next/core-web-vitals",
|
||||||
|
"next/typescript"
|
||||||
|
]
|
||||||
|
}
|
||||||
114
.github/workflows/claude-code.yml
vendored
114
.github/workflows/claude-code.yml
vendored
@@ -1,114 +0,0 @@
|
|||||||
name: Claude Code
|
|
||||||
|
|
||||||
on:
|
|
||||||
issue_comment:
|
|
||||||
types: [created]
|
|
||||||
pull_request_review_comment:
|
|
||||||
types: [created]
|
|
||||||
issues:
|
|
||||||
types: [opened, assigned]
|
|
||||||
pull_request_review:
|
|
||||||
types: [submitted]
|
|
||||||
pull_request_target:
|
|
||||||
types: [opened, synchronize, reopened]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
claude:
|
|
||||||
if: |
|
|
||||||
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
|
|
||||||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
|
|
||||||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
|
|
||||||
(github.event_name == 'issues' && contains(github.event.issue.body, '@claude'))
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
|
||||||
issues: write
|
|
||||||
id-token: write
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Configure AWS Credentials
|
|
||||||
uses: aws-actions/configure-aws-credentials@v4
|
|
||||||
with:
|
|
||||||
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
|
|
||||||
aws-region: us-east-1
|
|
||||||
|
|
||||||
- name: Run Claude Code
|
|
||||||
uses: anthropics/claude-code-action@v1
|
|
||||||
with:
|
|
||||||
use_bedrock: "true"
|
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
pr-review:
|
|
||||||
if: github.event_name == 'pull_request_target'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pull-requests: write
|
|
||||||
id-token: write
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Configure AWS Credentials
|
|
||||||
uses: aws-actions/configure-aws-credentials@v4
|
|
||||||
with:
|
|
||||||
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
|
|
||||||
aws-region: us-east-1
|
|
||||||
|
|
||||||
- name: Run Claude Code PR Review
|
|
||||||
uses: anthropics/claude-code-action@v1
|
|
||||||
with:
|
|
||||||
use_bedrock: "true"
|
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
allowed_non_write_users: "*"
|
|
||||||
prompt: |
|
|
||||||
REPO: ${{ github.repository }}
|
|
||||||
PR NUMBER: ${{ github.event.pull_request.number }}
|
|
||||||
|
|
||||||
This is a personal project - an AI-powered draw.io diagram generator built with:
|
|
||||||
- Next.js 15 with React 19
|
|
||||||
- Vercel AI SDK (streamText, useChat, tool calling)
|
|
||||||
- Multiple AI providers: Bedrock, Anthropic, OpenAI, Google, Azure, OpenRouter, Ollama
|
|
||||||
|
|
||||||
First, check previous review comments from github-actions bot using `gh api repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/comments`.
|
|
||||||
For each previous comment:
|
|
||||||
- If the issue is fixed in the current code, resolve the comment thread using:
|
|
||||||
`gh api graphql -f query='mutation { resolveReviewThread(input: {threadId: "THREAD_ID"}) { thread { isResolved } } }'`
|
|
||||||
Get the thread ID from the comment's node_id field.
|
|
||||||
- If the issue still exists, leave it alone
|
|
||||||
|
|
||||||
Then review the current diff for NEW issues only:
|
|
||||||
|
|
||||||
Review this PR for these issues (report ALL that apply):
|
|
||||||
1. Bugs that would cause runtime errors or broken functionality
|
|
||||||
2. Security issues (exposed secrets, API key leaks)
|
|
||||||
3. AI SDK misuse - specifically check for:
|
|
||||||
- Client-side: Should use useChat/useCompletion/useObject hooks, NOT raw fetch()
|
|
||||||
- Server-side: Should use streamText/generateText/streamObject/generateObject
|
|
||||||
- Message handling: Access message.parts array, not legacy content property
|
|
||||||
- Tool definitions: Must use Zod schemas for inputSchema
|
|
||||||
- Status handling: Check status (submitted/streaming/ready/error) before actions
|
|
||||||
- Stream cleanup: Call stop() when aborting streams
|
|
||||||
4. Unrelated changes that should be in separate PRs (scope creep)
|
|
||||||
5. Suspicious .gitignore additions or accidentally committed files
|
|
||||||
6. UI/UX inconsistencies (e.g., alignment issues)
|
|
||||||
|
|
||||||
When reviewing AI SDK usage, fetch https://ai-sdk.dev/docs/ to verify correct patterns.
|
|
||||||
Key doc pages: /docs/ai-sdk-ui/chatbot, /docs/ai-sdk-core/generating-text, /docs/ai-sdk-core/tools-and-tool-calling
|
|
||||||
|
|
||||||
DO NOT comment on:
|
|
||||||
- Minor performance optimizations
|
|
||||||
- Code style preferences (unless clearly wrong)
|
|
||||||
- Type annotations that don't affect functionality
|
|
||||||
|
|
||||||
Use `mcp__github_inline_comment__create_inline_comment` for inline comments.
|
|
||||||
Report ALL issues found - create multiple inline comments if needed. Only say "LGTM" if there are truly no issues.
|
|
||||||
claude_args: |
|
|
||||||
--allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh api:*),WebFetch(domain:ai-sdk.dev)"
|
|
||||||
11
README.md
11
README.md
@@ -73,6 +73,8 @@ Diagrams are represented as XML that can be rendered in draw.io. The AI processe
|
|||||||
- Google AI
|
- Google AI
|
||||||
- Azure OpenAI
|
- Azure OpenAI
|
||||||
- Ollama
|
- Ollama
|
||||||
|
- OpenRouter
|
||||||
|
- DeepSeek
|
||||||
|
|
||||||
Note that `claude-sonnet-4-5` has trained on draw.io diagrams with AWS logos, so if you want to create AWS architecture diagrams, this is the best choice.
|
Note that `claude-sonnet-4-5` has trained on draw.io diagrams with AWS logos, so if you want to create AWS architecture diagrams, this is the best choice.
|
||||||
|
|
||||||
@@ -105,7 +107,7 @@ cp env.example .env.local
|
|||||||
|
|
||||||
Edit `.env.local` and configure your chosen provider:
|
Edit `.env.local` and configure your chosen provider:
|
||||||
|
|
||||||
- Set `AI_PROVIDER` to your chosen provider (bedrock, openai, anthropic, google, azure, ollama)
|
- Set `AI_PROVIDER` to your chosen provider (bedrock, openai, anthropic, google, azure, ollama, openrouter, deepseek)
|
||||||
- Set `AI_MODEL` to the specific model you want to use
|
- Set `AI_MODEL` to the specific model you want to use
|
||||||
- Add the required API keys for your provider
|
- Add the required API keys for your provider
|
||||||
|
|
||||||
@@ -149,7 +151,8 @@ public/ # Static assets including example images
|
|||||||
- [x] Allow the LLM to modify the XML instead of generating it from scratch everytime.
|
- [x] Allow the LLM to modify the XML instead of generating it from scratch everytime.
|
||||||
- [x] Improve the smoothness of shape streaming updates.
|
- [x] Improve the smoothness of shape streaming updates.
|
||||||
- [x] Add multiple AI provider support (OpenAI, Anthropic, Google, Azure, Ollama)
|
- [x] Add multiple AI provider support (OpenAI, Anthropic, Google, Azure, Ollama)
|
||||||
- [ ] Solve the bug that generation will fail for session that longer than 60s.
|
- [x] Solve the bug that generation will fail for session that longer than 60s.
|
||||||
|
- [ ] Add API config on the UI.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
@@ -161,4 +164,8 @@ For support or inquiries, please open an issue on the GitHub repository or conta
|
|||||||
|
|
||||||
- Email: me[at]jiang.jp
|
- Email: me[at]jiang.jp
|
||||||
|
|
||||||
|
## Star History
|
||||||
|
|
||||||
|
[](https://www.star-history.com/#DayuanJiang/next-ai-draw-io&type=date&legend=top-left)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,13 +1,55 @@
|
|||||||
import { streamText, convertToModelMessages } from 'ai';
|
import { streamText, convertToModelMessages, createUIMessageStream, createUIMessageStreamResponse } from 'ai';
|
||||||
import { getAIModel } from '@/lib/ai-providers';
|
import { getAIModel } from '@/lib/ai-providers';
|
||||||
|
import { findCachedResponse } from '@/lib/cached-responses';
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
export const maxDuration = 60;
|
export const maxDuration = 300;
|
||||||
|
|
||||||
|
// Helper function to check if diagram is minimal/empty
|
||||||
|
function isMinimalDiagram(xml: string): boolean {
|
||||||
|
const stripped = xml.replace(/\s/g, '');
|
||||||
|
return !stripped.includes('id="2"');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to create cached stream response
|
||||||
|
function createCachedStreamResponse(xml: string): Response {
|
||||||
|
const toolCallId = `cached-${Date.now()}`;
|
||||||
|
|
||||||
|
const stream = createUIMessageStream({
|
||||||
|
execute: async ({ writer }) => {
|
||||||
|
writer.write({ type: 'start' });
|
||||||
|
writer.write({ type: 'tool-input-start', toolCallId, toolName: 'display_diagram' });
|
||||||
|
writer.write({ type: 'tool-input-delta', toolCallId, inputTextDelta: xml });
|
||||||
|
writer.write({ type: 'tool-input-available', toolCallId, toolName: 'display_diagram', input: { xml } });
|
||||||
|
writer.write({ type: 'finish' });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return createUIMessageStreamResponse({ stream });
|
||||||
|
}
|
||||||
|
|
||||||
export async function POST(req: Request) {
|
export async function POST(req: Request) {
|
||||||
try {
|
try {
|
||||||
const { messages, xml } = await req.json();
|
const { messages, xml } = await req.json();
|
||||||
|
|
||||||
|
// === CACHE CHECK START ===
|
||||||
|
const isFirstMessage = messages.length === 1;
|
||||||
|
const isEmptyDiagram = !xml || xml.trim() === '' || isMinimalDiagram(xml);
|
||||||
|
|
||||||
|
if (isFirstMessage && isEmptyDiagram) {
|
||||||
|
const lastMessage = messages[0];
|
||||||
|
const textPart = lastMessage.parts?.find((p: any) => p.type === 'text');
|
||||||
|
const filePart = lastMessage.parts?.find((p: any) => p.type === 'file');
|
||||||
|
|
||||||
|
const cached = findCachedResponse(textPart?.text || '', !!filePart);
|
||||||
|
|
||||||
|
if (cached) {
|
||||||
|
console.log('[Cache] Returning cached response for:', textPart?.text);
|
||||||
|
return createCachedStreamResponse(cached.xml);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// === CACHE CHECK END ===
|
||||||
|
|
||||||
const systemMessage = `
|
const systemMessage = `
|
||||||
You are an expert diagram creation assistant specializing in draw.io XML generation.
|
You are an expert diagram creation assistant specializing in draw.io XML generation.
|
||||||
Your primary function is crafting clear, well-organized visual diagrams through precise XML specifications.
|
Your primary function is crafting clear, well-organized visual diagrams through precise XML specifications.
|
||||||
@@ -90,7 +132,30 @@ ${lastMessageText}
|
|||||||
|
|
||||||
// Convert UIMessages to ModelMessages and add system message
|
// Convert UIMessages to ModelMessages and add system message
|
||||||
const modelMessages = convertToModelMessages(messages);
|
const modelMessages = convertToModelMessages(messages);
|
||||||
let enhancedMessages = [...modelMessages];
|
|
||||||
|
// Log messages with empty content for debugging (helps identify root cause)
|
||||||
|
const emptyMessages = modelMessages.filter((msg: any) =>
|
||||||
|
!msg.content || !Array.isArray(msg.content) || msg.content.length === 0
|
||||||
|
);
|
||||||
|
if (emptyMessages.length > 0) {
|
||||||
|
console.warn('[Chat API] Messages with empty content detected:',
|
||||||
|
JSON.stringify(emptyMessages.map((m: any) => ({ role: m.role, contentLength: m.content?.length })))
|
||||||
|
);
|
||||||
|
console.warn('[Chat API] Original UI messages structure:',
|
||||||
|
JSON.stringify(messages.map((m: any) => ({
|
||||||
|
id: m.id,
|
||||||
|
role: m.role,
|
||||||
|
partsCount: m.parts?.length,
|
||||||
|
partTypes: m.parts?.map((p: any) => p.type)
|
||||||
|
})))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter out messages with empty content arrays (Bedrock API rejects these)
|
||||||
|
// This is a safety measure - ideally convertToModelMessages should handle all cases
|
||||||
|
let enhancedMessages = modelMessages.filter((msg: any) =>
|
||||||
|
msg.content && Array.isArray(msg.content) && msg.content.length > 0
|
||||||
|
);
|
||||||
|
|
||||||
// Update the last message with formatted content if it's a user message
|
// Update the last message with formatted content if it's a user message
|
||||||
if (enhancedMessages.length >= 1) {
|
if (enhancedMessages.length >= 1) {
|
||||||
@@ -117,16 +182,49 @@ ${lastMessageText}
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Enhanced messages:", enhancedMessages);
|
// Add cache point to the last assistant message in conversation history
|
||||||
|
// This caches the entire conversation prefix for subsequent requests
|
||||||
|
// Strategy: system (cached) + history with last assistant (cached) + new user message
|
||||||
|
if (enhancedMessages.length >= 2) {
|
||||||
|
// Find the last assistant message (should be second-to-last, before current user message)
|
||||||
|
for (let i = enhancedMessages.length - 2; i >= 0; i--) {
|
||||||
|
if (enhancedMessages[i].role === 'assistant') {
|
||||||
|
enhancedMessages[i] = {
|
||||||
|
...enhancedMessages[i],
|
||||||
|
providerOptions: {
|
||||||
|
bedrock: { cachePoint: { type: 'default' } },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
break; // Only cache the last assistant message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get AI model from environment configuration
|
// Get AI model from environment configuration
|
||||||
const { model, providerOptions } = getAIModel();
|
const { model, providerOptions, headers } = getAIModel();
|
||||||
|
|
||||||
|
// System message with cache point for Bedrock (requires 1024+ tokens)
|
||||||
|
const systemMessageWithCache = {
|
||||||
|
role: 'system' as const,
|
||||||
|
content: systemMessage,
|
||||||
|
providerOptions: {
|
||||||
|
bedrock: { cachePoint: { type: 'default' } },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const result = streamText({
|
const result = streamText({
|
||||||
model,
|
model,
|
||||||
system: systemMessage,
|
messages: [systemMessageWithCache, ...enhancedMessages],
|
||||||
messages: enhancedMessages,
|
|
||||||
...(providerOptions && { providerOptions }),
|
...(providerOptions && { providerOptions }),
|
||||||
|
...(headers && { headers }),
|
||||||
|
onFinish: ({ usage, providerMetadata }) => {
|
||||||
|
console.log('[Cache] Usage:', JSON.stringify({
|
||||||
|
inputTokens: usage?.inputTokens,
|
||||||
|
outputTokens: usage?.outputTokens,
|
||||||
|
cachedInputTokens: usage?.cachedInputTokens,
|
||||||
|
}, null, 2));
|
||||||
|
console.log('[Cache] Provider metadata:', JSON.stringify(providerMetadata, null, 2));
|
||||||
|
},
|
||||||
tools: {
|
tools: {
|
||||||
// Client-side tool that will be executed on the client
|
// Client-side tool that will be executed on the client
|
||||||
display_diagram: {
|
display_diagram: {
|
||||||
|
|||||||
@@ -50,7 +50,10 @@ export default function ExamplePanel({
|
|||||||
{" "}
|
{" "}
|
||||||
You can also upload images to use as references.
|
You can also upload images to use as references.
|
||||||
</p>
|
</p>
|
||||||
<p className="text-sm text-gray-500 mb-2">Try these examples:</p>
|
<p className="text-sm text-gray-500 mb-2">
|
||||||
|
Try these examples{" "}
|
||||||
|
<span className="text-xs text-gray-400">(cached for instant response)</span>:
|
||||||
|
</p>
|
||||||
<div className="flex flex-wrap gap-5">
|
<div className="flex flex-wrap gap-5">
|
||||||
<button
|
<button
|
||||||
className="text-xs bg-gray-100 hover:bg-gray-200 text-gray-800 font-medium py-1 px-2 rounded"
|
className="text-xs bg-gray-100 hover:bg-gray-200 text-gray-800 font-medium py-1 px-2 rounded"
|
||||||
|
|||||||
@@ -1,15 +1,23 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import type React from "react";
|
|
||||||
import { useRef, useEffect, useState, useCallback } from "react";
|
import { useRef, useEffect, useState, useCallback } from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
import ExamplePanel from "./chat-example-panel";
|
import ExamplePanel from "./chat-example-panel";
|
||||||
import { UIMessage } from "ai";
|
import { UIMessage } from "ai";
|
||||||
import { convertToLegalXml, replaceNodes } from "@/lib/utils";
|
import { convertToLegalXml, replaceNodes } from "@/lib/utils";
|
||||||
|
import { Copy, Check, X } from "lucide-react";
|
||||||
|
|
||||||
import { useDiagram } from "@/contexts/diagram-context";
|
import { useDiagram } from "@/contexts/diagram-context";
|
||||||
|
|
||||||
|
const getMessageTextContent = (message: UIMessage): string => {
|
||||||
|
if (!message.parts) return "";
|
||||||
|
return message.parts
|
||||||
|
.filter((part: any) => part.type === "text")
|
||||||
|
.map((part: any) => part.text)
|
||||||
|
.join("\n");
|
||||||
|
};
|
||||||
|
|
||||||
interface ChatMessageDisplayProps {
|
interface ChatMessageDisplayProps {
|
||||||
messages: UIMessage[];
|
messages: UIMessage[];
|
||||||
error?: Error | null;
|
error?: Error | null;
|
||||||
@@ -30,6 +38,21 @@ export function ChatMessageDisplay({
|
|||||||
const [expandedTools, setExpandedTools] = useState<Record<string, boolean>>(
|
const [expandedTools, setExpandedTools] = useState<Record<string, boolean>>(
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
const [copiedMessageId, setCopiedMessageId] = useState<string | null>(null);
|
||||||
|
const [copyFailedMessageId, setCopyFailedMessageId] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const copyMessageToClipboard = async (messageId: string, text: string) => {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(text);
|
||||||
|
setCopiedMessageId(messageId);
|
||||||
|
setTimeout(() => setCopiedMessageId(null), 2000);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Failed to copy message:", err);
|
||||||
|
setCopyFailedMessageId(messageId);
|
||||||
|
setTimeout(() => setCopyFailedMessageId(null), 2000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleDisplayChart = useCallback(
|
const handleDisplayChart = useCallback(
|
||||||
(xml: string) => {
|
(xml: string) => {
|
||||||
const currentXml = xml || "";
|
const currentXml = xml || "";
|
||||||
@@ -137,16 +160,16 @@ export function ChatMessageDisplay({
|
|||||||
{output || (toolName === "display_diagram"
|
{output || (toolName === "display_diagram"
|
||||||
? "Diagram generated"
|
? "Diagram generated"
|
||||||
: toolName === "edit_diagram"
|
: toolName === "edit_diagram"
|
||||||
? "Diagram edited"
|
? "Diagram edited"
|
||||||
: "Tool executed")}
|
: "Tool executed")}
|
||||||
</div>
|
</div>
|
||||||
) : state === "output-error" ? (
|
) : state === "output-error" ? (
|
||||||
<div className="text-red-600">
|
<div className="text-red-600">
|
||||||
{output || (toolName === "display_diagram"
|
{output || (toolName === "display_diagram"
|
||||||
? "Error generating diagram"
|
? "Error generating diagram"
|
||||||
: toolName === "edit_diagram"
|
: toolName === "edit_diagram"
|
||||||
? "Error editing diagram"
|
? "Error editing diagram"
|
||||||
: "Tool error")}
|
: "Tool error")}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
@@ -160,51 +183,66 @@ export function ChatMessageDisplay({
|
|||||||
{messages.length === 0 ? (
|
{messages.length === 0 ? (
|
||||||
<ExamplePanel setInput={setInput} setFiles={setFiles} />
|
<ExamplePanel setInput={setInput} setFiles={setFiles} />
|
||||||
) : (
|
) : (
|
||||||
messages.map((message) => (
|
messages.map((message) => {
|
||||||
<div
|
const userMessageText = message.role === "user" ? getMessageTextContent(message) : "";
|
||||||
key={message.id}
|
return (
|
||||||
className={`mb-4 ${
|
|
||||||
message.role === "user" ? "text-right" : "text-left"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
className={`inline-block px-4 py-2 whitespace-pre-wrap text-sm rounded-lg max-w-[85%] break-words ${
|
key={message.id}
|
||||||
message.role === "user"
|
className={`mb-4 flex ${message.role === "user" ? "justify-end" : "justify-start"}`}
|
||||||
? "bg-primary text-primary-foreground"
|
|
||||||
: "bg-muted text-muted-foreground"
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
{message.parts?.map((part: any, index: number) => {
|
{message.role === "user" && userMessageText && (
|
||||||
switch (part.type) {
|
<button
|
||||||
case "text":
|
onClick={() => copyMessageToClipboard(message.id, userMessageText)}
|
||||||
return (
|
className="p-1 text-gray-400 hover:text-gray-600 transition-colors self-center mr-1"
|
||||||
<div key={index}>{part.text}</div>
|
title={copiedMessageId === message.id ? "Copied!" : copyFailedMessageId === message.id ? "Failed to copy" : "Copy message"}
|
||||||
);
|
>
|
||||||
case "file":
|
{copiedMessageId === message.id ? (
|
||||||
return (
|
<Check className="h-3.5 w-3.5 text-green-500" />
|
||||||
<div key={index} className="mt-2">
|
) : copyFailedMessageId === message.id ? (
|
||||||
<Image
|
<X className="h-3.5 w-3.5 text-red-500" />
|
||||||
src={part.url}
|
) : (
|
||||||
width={200}
|
<Copy className="h-3.5 w-3.5" />
|
||||||
height={200}
|
)}
|
||||||
alt={`Uploaded diagram or image for AI analysis`}
|
</button>
|
||||||
className="rounded-md border"
|
)}
|
||||||
style={{
|
<div
|
||||||
objectFit: "contain",
|
className={`px-4 py-2 whitespace-pre-wrap text-sm rounded-lg max-w-[85%] break-words ${message.role === "user"
|
||||||
}}
|
? "bg-primary text-primary-foreground"
|
||||||
/>
|
: "bg-muted text-muted-foreground"
|
||||||
</div>
|
}`}
|
||||||
);
|
>
|
||||||
default:
|
{message.parts?.map((part: any, index: number) => {
|
||||||
if (part.type?.startsWith("tool-")) {
|
switch (part.type) {
|
||||||
return renderToolPart(part);
|
case "text":
|
||||||
}
|
return (
|
||||||
return null;
|
<div key={index}>{part.text}</div>
|
||||||
}
|
);
|
||||||
})}
|
case "file":
|
||||||
|
return (
|
||||||
|
<div key={index} className="mt-2">
|
||||||
|
<Image
|
||||||
|
src={part.url}
|
||||||
|
width={200}
|
||||||
|
height={200}
|
||||||
|
alt={`Uploaded diagram or image for AI analysis`}
|
||||||
|
className="rounded-md border"
|
||||||
|
style={{
|
||||||
|
objectFit: "contain",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
if (part.type?.startsWith("tool-")) {
|
||||||
|
return renderToolPart(part);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
))
|
})
|
||||||
)}
|
)}
|
||||||
{error && (
|
{error && (
|
||||||
<div className="text-red-500 text-sm mt-2">
|
<div className="text-red-500 text-sm mt-2">
|
||||||
|
|||||||
10
env.example
10
env.example
@@ -1,6 +1,6 @@
|
|||||||
# AI Provider Configuration
|
# AI Provider Configuration
|
||||||
# AI_PROVIDER: Which provider to use
|
# AI_PROVIDER: Which provider to use
|
||||||
# Options: bedrock, openai, anthropic, google, azure, ollama, openrouter
|
# Options: bedrock, openai, anthropic, google, azure, ollama, openrouter, deepseek
|
||||||
# Default: bedrock
|
# Default: bedrock
|
||||||
AI_PROVIDER=bedrock
|
AI_PROVIDER=bedrock
|
||||||
|
|
||||||
@@ -20,16 +20,24 @@ AI_MODEL=global.anthropic.claude-sonnet-4-5-20250929-v1:0
|
|||||||
|
|
||||||
# Anthropic (Direct) Configuration
|
# Anthropic (Direct) Configuration
|
||||||
# ANTHROPIC_API_KEY=sk-ant-...
|
# ANTHROPIC_API_KEY=sk-ant-...
|
||||||
|
# ANTHROPIC_BASE_URL=https://your-custom-anthropic/v1
|
||||||
|
|
||||||
# Google Generative AI Configuration
|
# Google Generative AI Configuration
|
||||||
# GOOGLE_GENERATIVE_AI_API_KEY=...
|
# GOOGLE_GENERATIVE_AI_API_KEY=...
|
||||||
|
# GOOGLE_BASE_URL=https://generativelanguage.googleapis.com/v1beta # Optional: Custom endpoint
|
||||||
|
|
||||||
# Azure OpenAI Configuration
|
# Azure OpenAI Configuration
|
||||||
# AZURE_RESOURCE_NAME=your-resource-name
|
# AZURE_RESOURCE_NAME=your-resource-name
|
||||||
# AZURE_API_KEY=...
|
# AZURE_API_KEY=...
|
||||||
|
# AZURE_BASE_URL=https://your-resource.openai.azure.com # Optional: Custom endpoint (overrides resourceName)
|
||||||
|
|
||||||
# Ollama (Local) Configuration
|
# Ollama (Local) Configuration
|
||||||
# OLLAMA_BASE_URL=http://localhost:11434/api # Optional, defaults to localhost
|
# OLLAMA_BASE_URL=http://localhost:11434/api # Optional, defaults to localhost
|
||||||
|
|
||||||
# OpenRouter Configuration
|
# OpenRouter Configuration
|
||||||
# OPENROUTER_API_KEY=sk-or-v1-...
|
# OPENROUTER_API_KEY=sk-or-v1-...
|
||||||
|
# OPENROUTER_BASE_URL=https://openrouter.ai/api/v1 # Optional: Custom endpoint
|
||||||
|
|
||||||
|
# DeepSeek Configuration
|
||||||
|
# DEEPSEEK_API_KEY=sk-...
|
||||||
|
# DEEPSEEK_BASE_URL=https://api.deepseek.com/v1 # Optional: Custom endpoint
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { bedrock } from '@ai-sdk/amazon-bedrock';
|
import { bedrock } from '@ai-sdk/amazon-bedrock';
|
||||||
import { openai, createOpenAI } from '@ai-sdk/openai';
|
import { openai, createOpenAI } from '@ai-sdk/openai';
|
||||||
import { anthropic } from '@ai-sdk/anthropic';
|
import { createAnthropic } from '@ai-sdk/anthropic';
|
||||||
import { google } from '@ai-sdk/google';
|
import { google, createGoogleGenerativeAI } from '@ai-sdk/google';
|
||||||
import { azure } from '@ai-sdk/azure';
|
import { azure, createAzure } from '@ai-sdk/azure';
|
||||||
import { ollama } from 'ollama-ai-provider-v2';
|
import { ollama, createOllama } from 'ollama-ai-provider-v2';
|
||||||
import { createOpenRouter } from '@openrouter/ai-sdk-provider';
|
import { createOpenRouter } from '@openrouter/ai-sdk-provider';
|
||||||
|
import { deepseek, createDeepSeek } from '@ai-sdk/deepseek';
|
||||||
|
|
||||||
export type ProviderName =
|
export type ProviderName =
|
||||||
| 'bedrock'
|
| 'bedrock'
|
||||||
@@ -13,20 +14,25 @@ export type ProviderName =
|
|||||||
| 'google'
|
| 'google'
|
||||||
| 'azure'
|
| 'azure'
|
||||||
| 'ollama'
|
| 'ollama'
|
||||||
| 'openrouter';
|
| 'openrouter'
|
||||||
|
| 'deepseek';
|
||||||
|
|
||||||
interface ModelConfig {
|
interface ModelConfig {
|
||||||
model: any;
|
model: any;
|
||||||
providerOptions?: any;
|
providerOptions?: any;
|
||||||
|
headers?: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Anthropic beta headers for fine-grained tool streaming
|
// Bedrock provider options for Anthropic beta features
|
||||||
const ANTHROPIC_BETA_OPTIONS = {
|
const BEDROCK_ANTHROPIC_BETA = {
|
||||||
anthropic: {
|
bedrock: {
|
||||||
additionalModelRequestFields: {
|
anthropicBeta: ['fine-grained-tool-streaming-2025-05-14'],
|
||||||
anthropic_beta: ['fine-grained-tool-streaming-2025-05-14']
|
},
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
// Direct Anthropic API headers for beta features
|
||||||
|
const ANTHROPIC_BETA_HEADERS = {
|
||||||
|
'anthropic-beta': 'fine-grained-tool-streaming-2025-05-14',
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,6 +47,7 @@ function validateProviderCredentials(provider: ProviderName): void {
|
|||||||
azure: 'AZURE_API_KEY',
|
azure: 'AZURE_API_KEY',
|
||||||
ollama: null, // No credentials needed for local Ollama
|
ollama: null, // No credentials needed for local Ollama
|
||||||
openrouter: 'OPENROUTER_API_KEY',
|
openrouter: 'OPENROUTER_API_KEY',
|
||||||
|
deepseek: 'DEEPSEEK_API_KEY',
|
||||||
};
|
};
|
||||||
|
|
||||||
const requiredVar = requiredEnvVars[provider];
|
const requiredVar = requiredEnvVars[provider];
|
||||||
@@ -56,7 +63,7 @@ function validateProviderCredentials(provider: ProviderName): void {
|
|||||||
* Get the AI model based on environment variables
|
* Get the AI model based on environment variables
|
||||||
*
|
*
|
||||||
* Environment variables:
|
* Environment variables:
|
||||||
* - AI_PROVIDER: The provider to use (bedrock, openai, anthropic, google, azure, ollama, openrouter)
|
* - AI_PROVIDER: The provider to use (bedrock, openai, anthropic, google, azure, ollama, openrouter, deepseek)
|
||||||
* - AI_MODEL: The model ID/name for the selected provider
|
* - AI_MODEL: The model ID/name for the selected provider
|
||||||
*
|
*
|
||||||
* Provider-specific env vars:
|
* Provider-specific env vars:
|
||||||
@@ -68,6 +75,8 @@ function validateProviderCredentials(provider: ProviderName): void {
|
|||||||
* - AWS_REGION, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY: AWS Bedrock credentials
|
* - AWS_REGION, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY: AWS Bedrock credentials
|
||||||
* - OLLAMA_BASE_URL: Ollama server URL (optional, defaults to http://localhost:11434)
|
* - OLLAMA_BASE_URL: Ollama server URL (optional, defaults to http://localhost:11434)
|
||||||
* - OPENROUTER_API_KEY: OpenRouter API key
|
* - OPENROUTER_API_KEY: OpenRouter API key
|
||||||
|
* - DEEPSEEK_API_KEY: DeepSeek API key
|
||||||
|
* - DEEPSEEK_BASE_URL: DeepSeek endpoint (optional)
|
||||||
*/
|
*/
|
||||||
export function getAIModel(): ModelConfig {
|
export function getAIModel(): ModelConfig {
|
||||||
const provider = (process.env.AI_PROVIDER || 'bedrock') as ProviderName;
|
const provider = (process.env.AI_PROVIDER || 'bedrock') as ProviderName;
|
||||||
@@ -87,13 +96,14 @@ export function getAIModel(): ModelConfig {
|
|||||||
|
|
||||||
let model: any;
|
let model: any;
|
||||||
let providerOptions: any = undefined;
|
let providerOptions: any = undefined;
|
||||||
|
let headers: Record<string, string> | undefined = undefined;
|
||||||
|
|
||||||
switch (provider) {
|
switch (provider) {
|
||||||
case 'bedrock':
|
case 'bedrock':
|
||||||
model = bedrock(modelId);
|
model = bedrock(modelId);
|
||||||
// Add Anthropic beta headers if using Claude models via Bedrock
|
// Add Anthropic beta options if using Claude models via Bedrock
|
||||||
if (modelId.includes('anthropic.claude')) {
|
if (modelId.includes('anthropic.claude')) {
|
||||||
providerOptions = ANTHROPIC_BETA_OPTIONS;
|
providerOptions = BEDROCK_ANTHROPIC_BETA;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -110,40 +120,81 @@ export function getAIModel(): ModelConfig {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'anthropic':
|
case 'anthropic':
|
||||||
model = anthropic(modelId);
|
const customProvider = createAnthropic({
|
||||||
|
apiKey: process.env.ANTHROPIC_API_KEY,
|
||||||
|
baseURL: process.env.ANTHROPIC_BASE_URL || 'https://api.anthropic.com/v1',
|
||||||
|
headers: ANTHROPIC_BETA_HEADERS,
|
||||||
|
});
|
||||||
|
model = customProvider(modelId);
|
||||||
// Add beta headers for fine-grained tool streaming
|
// Add beta headers for fine-grained tool streaming
|
||||||
providerOptions = ANTHROPIC_BETA_OPTIONS;
|
headers = ANTHROPIC_BETA_HEADERS;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'google':
|
case 'google':
|
||||||
model = google(modelId);
|
if (process.env.GOOGLE_BASE_URL) {
|
||||||
|
const customGoogle = createGoogleGenerativeAI({
|
||||||
|
apiKey: process.env.GOOGLE_GENERATIVE_AI_API_KEY,
|
||||||
|
baseURL: process.env.GOOGLE_BASE_URL,
|
||||||
|
});
|
||||||
|
model = customGoogle(modelId);
|
||||||
|
} else {
|
||||||
|
model = google(modelId);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'azure':
|
case 'azure':
|
||||||
model = azure(modelId);
|
if (process.env.AZURE_BASE_URL) {
|
||||||
|
const customAzure = createAzure({
|
||||||
|
apiKey: process.env.AZURE_API_KEY,
|
||||||
|
baseURL: process.env.AZURE_BASE_URL,
|
||||||
|
});
|
||||||
|
model = customAzure(modelId);
|
||||||
|
} else {
|
||||||
|
model = azure(modelId);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'ollama':
|
case 'ollama':
|
||||||
model = ollama(modelId);
|
if (process.env.OLLAMA_BASE_URL) {
|
||||||
|
const customOllama = createOllama({
|
||||||
|
baseURL: process.env.OLLAMA_BASE_URL,
|
||||||
|
});
|
||||||
|
model = customOllama(modelId);
|
||||||
|
} else {
|
||||||
|
model = ollama(modelId);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'openrouter':
|
case 'openrouter':
|
||||||
const openrouter = createOpenRouter({
|
const openrouter = createOpenRouter({
|
||||||
apiKey: process.env.OPENROUTER_API_KEY,
|
apiKey: process.env.OPENROUTER_API_KEY,
|
||||||
|
...(process.env.OPENROUTER_BASE_URL && { baseURL: process.env.OPENROUTER_BASE_URL }),
|
||||||
});
|
});
|
||||||
model = openrouter(modelId);
|
model = openrouter(modelId);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'deepseek':
|
||||||
|
if (process.env.DEEPSEEK_BASE_URL) {
|
||||||
|
const customDeepSeek = createDeepSeek({
|
||||||
|
apiKey: process.env.DEEPSEEK_API_KEY,
|
||||||
|
baseURL: process.env.DEEPSEEK_BASE_URL,
|
||||||
|
});
|
||||||
|
model = customDeepSeek(modelId);
|
||||||
|
} else {
|
||||||
|
model = deepseek(modelId);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unknown AI provider: ${provider}. Supported providers: bedrock, openai, anthropic, google, azure, ollama, openrouter`
|
`Unknown AI provider: ${provider}. Supported providers: bedrock, openai, anthropic, google, azure, ollama, openrouter, deepseek`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log if provider options are being applied
|
// Log if provider options or headers are being applied
|
||||||
if (providerOptions) {
|
if (providerOptions || headers) {
|
||||||
console.log('[AI Provider] Applying provider-specific options');
|
console.log('[AI Provider] Applying provider-specific options/headers');
|
||||||
}
|
}
|
||||||
|
|
||||||
return { model, providerOptions };
|
return { model, providerOptions, headers };
|
||||||
}
|
}
|
||||||
|
|||||||
557
lib/cached-responses.ts
Normal file
557
lib/cached-responses.ts
Normal file
@@ -0,0 +1,557 @@
|
|||||||
|
export interface CachedResponse {
|
||||||
|
promptText: string;
|
||||||
|
hasImage: boolean;
|
||||||
|
xml: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CACHED_EXAMPLE_RESPONSES: CachedResponse[] = [
|
||||||
|
{
|
||||||
|
promptText: "Give me a **animated connector** diagram of transformer's architecture",
|
||||||
|
hasImage: false,
|
||||||
|
xml: `<root>
|
||||||
|
<mxCell id="0"/>
|
||||||
|
<mxCell id="1" parent="0"/>
|
||||||
|
|
||||||
|
<!-- Title -->
|
||||||
|
<mxCell id="title" value="Transformer Architecture" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=20;fontStyle=1;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="300" y="20" width="250" height="30" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Input Embedding (Left - Encoder Side) -->
|
||||||
|
<mxCell id="input_embed" value="Input Embedding" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=11;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="80" y="480" width="120" height="40" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Positional Encoding (Left) -->
|
||||||
|
<mxCell id="pos_enc_left" value="Positional Encoding" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=11;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="80" y="420" width="120" height="40" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Encoder Stack -->
|
||||||
|
<mxCell id="encoder_box" value="ENCODER" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;verticalAlign=top;fontSize=12;fontStyle=1;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="60" y="180" width="160" height="220" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Multi-Head Attention (Encoder) -->
|
||||||
|
<mxCell id="mha_enc" value="Multi-Head
Attention" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=10;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="80" y="330" width="120" height="50" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Add & Norm 1 (Encoder) -->
|
||||||
|
<mxCell id="add_norm1_enc" value="Add & Norm" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=10;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="80" y="280" width="120" height="30" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Feed Forward (Encoder) -->
|
||||||
|
<mxCell id="ff_enc" value="Feed Forward" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=10;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="80" y="240" width="120" height="30" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Add & Norm 2 (Encoder) -->
|
||||||
|
<mxCell id="add_norm2_enc" value="Add & Norm" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=10;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="80" y="200" width="120" height="30" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Nx label for encoder -->
|
||||||
|
<mxCell id="nx_enc" value="Nx" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=11;fontStyle=2;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="30" y="275" width="30" height="30" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Output Embedding (Right - Decoder Side) -->
|
||||||
|
<mxCell id="output_embed" value="Output Embedding" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=11;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="650" y="480" width="120" height="40" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Positional Encoding (Right) -->
|
||||||
|
<mxCell id="pos_enc_right" value="Positional Encoding" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=11;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="650" y="420" width="120" height="40" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Decoder Stack -->
|
||||||
|
<mxCell id="decoder_box" value="DECODER" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;verticalAlign=top;fontSize=12;fontStyle=1;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="630" y="140" width="160" height="260" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Masked Multi-Head Attention (Decoder) -->
|
||||||
|
<mxCell id="masked_mha_dec" value="Masked Multi-Head
Attention" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=10;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="650" y="340" width="120" height="50" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Add & Norm 1 (Decoder) -->
|
||||||
|
<mxCell id="add_norm1_dec" value="Add & Norm" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=10;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="650" y="290" width="120" height="30" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Multi-Head Attention (Decoder - Cross Attention) -->
|
||||||
|
<mxCell id="mha_dec" value="Multi-Head
Attention" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=10;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="650" y="240" width="120" height="40" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Add & Norm 2 (Decoder) -->
|
||||||
|
<mxCell id="add_norm2_dec" value="Add & Norm" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=10;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="650" y="200" width="120" height="30" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Feed Forward (Decoder) -->
|
||||||
|
<mxCell id="ff_dec" value="Feed Forward" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=10;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="650" y="160" width="120" height="30" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Add & Norm 3 (Decoder) -->
|
||||||
|
<mxCell id="add_norm3_dec" value="Add & Norm" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=10;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="650" y="120" width="120" height="30" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Nx label for decoder -->
|
||||||
|
<mxCell id="nx_dec" value="Nx" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=11;fontStyle=2;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="790" y="255" width="30" height="30" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Linear -->
|
||||||
|
<mxCell id="linear" value="Linear" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=11;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="650" y="80" width="120" height="30" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Softmax -->
|
||||||
|
<mxCell id="softmax" value="Softmax" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=11;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="650" y="40" width="120" height="30" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Output Probabilities -->
|
||||||
|
<mxCell id="output" value="Output Probabilities" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=11;fontStyle=1;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="640" y="0" width="140" height="30" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Animated Connectors - Encoder Side -->
|
||||||
|
<mxCell id="conn1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;strokeWidth=2;strokeColor=#6c8ebf;flowAnimation=1;" edge="1" parent="1" source="input_embed" target="pos_enc_left">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<mxCell id="conn2" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;strokeWidth=2;strokeColor=#6c8ebf;flowAnimation=1;" edge="1" parent="1" source="pos_enc_left" target="mha_enc">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<mxCell id="conn3" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;strokeWidth=2;strokeColor=#82b366;flowAnimation=1;" edge="1" parent="1" source="mha_enc" target="add_norm1_enc">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<mxCell id="conn4" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;strokeWidth=2;strokeColor=#d6b656;flowAnimation=1;" edge="1" parent="1" source="add_norm1_enc" target="ff_enc">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<mxCell id="conn5" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;strokeWidth=2;strokeColor=#82b366;flowAnimation=1;" edge="1" parent="1" source="ff_enc" target="add_norm2_enc">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Encoder to Decoder Cross Attention -->
|
||||||
|
<mxCell id="conn_cross" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeWidth=3;strokeColor=#9673a6;flowAnimation=1;dashed=1;" edge="1" parent="1" source="add_norm2_enc" target="mha_dec">
|
||||||
|
<mxGeometry relative="1" as="geometry">
|
||||||
|
<Array as="points">
|
||||||
|
<mxPoint x="400" y="215"/>
|
||||||
|
<mxPoint x="400" y="260"/>
|
||||||
|
</Array>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="cross_label" value="K, V" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=10;fontStyle=1;fillColor=#ffffff;" vertex="1" connectable="0" parent="conn_cross">
|
||||||
|
<mxGeometry x="-0.1" y="1" relative="1" as="geometry">
|
||||||
|
<mxPoint x="10" y="-9" as="offset"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Animated Connectors - Decoder Side -->
|
||||||
|
<mxCell id="conn6" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;strokeWidth=2;strokeColor=#d79b00;flowAnimation=1;" edge="1" parent="1" source="output_embed" target="pos_enc_right">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<mxCell id="conn7" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;strokeWidth=2;strokeColor=#d79b00;flowAnimation=1;" edge="1" parent="1" source="pos_enc_right" target="masked_mha_dec">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<mxCell id="conn8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;strokeWidth=2;strokeColor=#82b366;flowAnimation=1;" edge="1" parent="1" source="masked_mha_dec" target="add_norm1_dec">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<mxCell id="conn9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;strokeWidth=2;strokeColor=#d6b656;flowAnimation=1;" edge="1" parent="1" source="add_norm1_dec" target="mha_dec">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<mxCell id="conn10" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;strokeWidth=2;strokeColor=#82b366;flowAnimation=1;" edge="1" parent="1" source="mha_dec" target="add_norm2_dec">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<mxCell id="conn11" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;strokeWidth=2;strokeColor=#d6b656;flowAnimation=1;" edge="1" parent="1" source="add_norm2_dec" target="ff_dec">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<mxCell id="conn12" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;strokeWidth=2;strokeColor=#82b366;flowAnimation=1;" edge="1" parent="1" source="ff_dec" target="add_norm3_dec">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<mxCell id="conn13" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;strokeWidth=2;strokeColor=#b85450;flowAnimation=1;" edge="1" parent="1" source="add_norm3_dec" target="linear">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<mxCell id="conn14" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;strokeWidth=2;strokeColor=#b85450;flowAnimation=1;" edge="1" parent="1" source="linear" target="softmax">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<mxCell id="conn15" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;strokeWidth=2;strokeColor=#6c8ebf;flowAnimation=1;" edge="1" parent="1" source="softmax" target="output">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Residual Connections (Encoder) -->
|
||||||
|
<mxCell id="res1_enc" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeWidth=1.5;strokeColor=#999999;dashed=1;flowAnimation=1;" edge="1" parent="1" source="mha_enc" target="add_norm1_enc">
|
||||||
|
<mxGeometry relative="1" as="geometry">
|
||||||
|
<Array as="points">
|
||||||
|
<mxPoint x="50" y="355"/>
|
||||||
|
<mxPoint x="50" y="295"/>
|
||||||
|
</Array>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<mxCell id="res2_enc" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeWidth=1.5;strokeColor=#999999;dashed=1;flowAnimation=1;" edge="1" parent="1" source="ff_enc" target="add_norm2_enc">
|
||||||
|
<mxGeometry relative="1" as="geometry">
|
||||||
|
<Array as="points">
|
||||||
|
<mxPoint x="50" y="255"/>
|
||||||
|
<mxPoint x="50" y="215"/>
|
||||||
|
</Array>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Residual Connections (Decoder) -->
|
||||||
|
<mxCell id="res1_dec" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;strokeWidth=1.5;strokeColor=#999999;dashed=1;flowAnimation=1;" edge="1" parent="1" source="masked_mha_dec" target="add_norm1_dec">
|
||||||
|
<mxGeometry relative="1" as="geometry">
|
||||||
|
<Array as="points">
|
||||||
|
<mxPoint x="800" y="365"/>
|
||||||
|
<mxPoint x="800" y="305"/>
|
||||||
|
</Array>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<mxCell id="res2_dec" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;strokeWidth=1.5;strokeColor=#999999;dashed=1;flowAnimation=1;" edge="1" parent="1" source="mha_dec" target="add_norm2_dec">
|
||||||
|
<mxGeometry relative="1" as="geometry">
|
||||||
|
<Array as="points">
|
||||||
|
<mxPoint x="800" y="260"/>
|
||||||
|
<mxPoint x="800" y="215"/>
|
||||||
|
</Array>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<mxCell id="res3_dec" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;strokeWidth=1.5;strokeColor=#999999;dashed=1;flowAnimation=1;" edge="1" parent="1" source="ff_dec" target="add_norm3_dec">
|
||||||
|
<mxGeometry relative="1" as="geometry">
|
||||||
|
<Array as="points">
|
||||||
|
<mxPoint x="800" y="175"/>
|
||||||
|
<mxPoint x="800" y="135"/>
|
||||||
|
</Array>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Input/Output Labels -->
|
||||||
|
<mxCell id="input_label" value="Inputs" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=12;fontStyle=1;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="110" y="530" width="60" height="20" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<mxCell id="output_label" value="Outputs
(shifted right)" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=12;fontStyle=1;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="660" y="530" width="100" height="30" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
</root>`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
promptText: "Replicate this in aws style",
|
||||||
|
hasImage: true,
|
||||||
|
xml: `<root>
|
||||||
|
<mxCell id="0"/>
|
||||||
|
<mxCell id="1" parent="0"/>
|
||||||
|
|
||||||
|
<!-- AWS Cloud Container -->
|
||||||
|
<mxCell id="2" value="AWS" style="sketch=0;outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_aws_cloud;strokeColor=#232F3E;fillColor=none;verticalAlign=top;align=left;spacingLeft=30;fontColor=#232F3E;dashed=0;rounded=1;arcSize=5;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="340" y="40" width="880" height="520" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- User -->
|
||||||
|
<mxCell id="3" value="User" style="sketch=0;outlineConnect=0;fontColor=#232F3E;gradientColor=none;fillColor=#232F3D;strokeColor=none;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=14;fontStyle=0;aspect=fixed;pointerEvents=1;shape=mxgraph.aws4.user;rounded=1;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="80" y="240" width="78" height="78" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- EC2 Instance -->
|
||||||
|
<mxCell id="4" value="EC2" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#ED7100;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=14;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;rounded=1;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="560" y="240" width="78" height="78" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- S3 Bucket -->
|
||||||
|
<mxCell id="5" value="S3" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#7AA116;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=14;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.s3;rounded=1;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="960" y="120" width="78" height="78" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Bedrock -->
|
||||||
|
<mxCell id="6" value="bedrock" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#01A88D;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=14;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.bedrock;rounded=1;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="960" y="260" width="78" height="78" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- DynamoDB -->
|
||||||
|
<mxCell id="7" value="DynamoDB" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#C925D1;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=14;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.dynamodb;rounded=1;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="960" y="400" width="78" height="78" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Arrow: User to EC2 -->
|
||||||
|
<mxCell id="8" value="" style="endArrow=classic;html=1;rounded=0;strokeColor=#232F3E;strokeWidth=2;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="3" target="4">
|
||||||
|
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||||
|
<mxPoint x="400" y="350" as="sourcePoint"/>
|
||||||
|
<mxPoint x="450" y="300" as="targetPoint"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Arrow: EC2 to S3 -->
|
||||||
|
<mxCell id="9" value="" style="endArrow=classic;html=1;rounded=0;strokeColor=#232F3E;strokeWidth=2;exitX=1;exitY=0.25;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="4" target="5">
|
||||||
|
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||||
|
<mxPoint x="700" y="350" as="sourcePoint"/>
|
||||||
|
<mxPoint x="750" y="300" as="targetPoint"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Arrow: EC2 to Bedrock -->
|
||||||
|
<mxCell id="10" value="" style="endArrow=classic;html=1;rounded=0;strokeColor=#232F3E;strokeWidth=2;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="4" target="6">
|
||||||
|
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||||
|
<mxPoint x="700" y="350" as="sourcePoint"/>
|
||||||
|
<mxPoint x="750" y="300" as="targetPoint"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Arrow: EC2 to DynamoDB -->
|
||||||
|
<mxCell id="11" value="" style="endArrow=classic;html=1;rounded=0;strokeColor=#232F3E;strokeWidth=2;exitX=1;exitY=0.75;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="4" target="7">
|
||||||
|
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||||
|
<mxPoint x="700" y="350" as="sourcePoint"/>
|
||||||
|
<mxPoint x="750" y="300" as="targetPoint"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
</root>`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
promptText: "Replicate this flowchart.",
|
||||||
|
hasImage: true,
|
||||||
|
xml: `<root>
|
||||||
|
<mxCell id="0"/>
|
||||||
|
<mxCell id="1" parent="0"/>
|
||||||
|
|
||||||
|
<!-- Start: Lamp doesn't work -->
|
||||||
|
<mxCell id="2" value="Lamp doesn't work" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffcccc;strokeColor=#000000;strokeWidth=2;fontSize=18;fontStyle=0;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="140" y="40" width="180" height="60" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Arrow from start to first decision -->
|
||||||
|
<mxCell id="3" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#000000;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="2" target="4">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Decision: Lamp plugged in? -->
|
||||||
|
<mxCell id="4" value="Lamp<br>plugged in?" style="rhombus;whiteSpace=wrap;html=1;fillColor=#ffff99;strokeColor=#000000;strokeWidth=2;fontSize=18;fontStyle=0;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="130" y="150" width="200" height="200" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Arrow to Plug in lamp (No) -->
|
||||||
|
<mxCell id="5" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#000000;strokeWidth=2;endArrow=block;endFill=1;fontSize=16;" edge="1" parent="1" source="4" target="6">
|
||||||
|
<mxGeometry x="-0.2" relative="1" as="geometry">
|
||||||
|
<mxPoint as="offset"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Action: Plug in lamp -->
|
||||||
|
<mxCell id="6" value="Plug in lamp" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#99ff99;strokeColor=#000000;strokeWidth=2;fontSize=18;fontStyle=0;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="420" y="220" width="200" height="60" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Arrow down to second decision (Yes) -->
|
||||||
|
<mxCell id="7" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#000000;strokeWidth=2;endArrow=block;endFill=1;fontSize=16;" edge="1" parent="1" source="4" target="8">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Decision: Bulb burned out? -->
|
||||||
|
<mxCell id="8" value="Bulb<br>burned out?" style="rhombus;whiteSpace=wrap;html=1;fillColor=#ffff99;strokeColor=#000000;strokeWidth=2;fontSize=18;fontStyle=0;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="130" y="400" width="200" height="200" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Arrow to Replace bulb (Yes) -->
|
||||||
|
<mxCell id="9" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#000000;strokeWidth=2;endArrow=block;endFill=1;fontSize=16;" edge="1" parent="1" source="8" target="10">
|
||||||
|
<mxGeometry x="-0.2" relative="1" as="geometry">
|
||||||
|
<mxPoint as="offset"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Action: Replace bulb -->
|
||||||
|
<mxCell id="10" value="Replace bulb" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#99ff99;strokeColor=#000000;strokeWidth=2;fontSize=18;fontStyle=0;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="420" y="470" width="200" height="60" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Arrow down to Repair lamp (No) -->
|
||||||
|
<mxCell id="11" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#000000;strokeWidth=2;endArrow=block;endFill=1;fontSize=16;" edge="1" parent="1" source="8" target="12">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Action: Repair lamp -->
|
||||||
|
<mxCell id="12" value="Repair lamp" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#99ff99;strokeColor=#000000;strokeWidth=2;fontSize=18;fontStyle=0;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="130" y="650" width="200" height="60" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
</root>`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
promptText: "Draw a cat for me",
|
||||||
|
hasImage: false,
|
||||||
|
xml: `<root>
|
||||||
|
<mxCell id="0"/>
|
||||||
|
<mxCell id="1" parent="0"/>
|
||||||
|
|
||||||
|
<!-- Cat's head -->
|
||||||
|
<mxCell id="2" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#FFE6CC;strokeColor=#000000;strokeWidth=2;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="300" y="150" width="120" height="120" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Left ear -->
|
||||||
|
<mxCell id="3" value="" style="triangle;whiteSpace=wrap;html=1;fillColor=#FFE6CC;strokeColor=#000000;strokeWidth=2;rotation=30;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="280" y="120" width="50" height="60" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Right ear -->
|
||||||
|
<mxCell id="4" value="" style="triangle;whiteSpace=wrap;html=1;fillColor=#FFE6CC;strokeColor=#000000;strokeWidth=2;rotation=-30;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="390" y="120" width="50" height="60" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Left ear inner -->
|
||||||
|
<mxCell id="5" value="" style="triangle;whiteSpace=wrap;html=1;fillColor=#FFB6C1;strokeColor=none;rotation=30;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="290" y="135" width="30" height="35" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Right ear inner -->
|
||||||
|
<mxCell id="6" value="" style="triangle;whiteSpace=wrap;html=1;fillColor=#FFB6C1;strokeColor=none;rotation=-30;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="400" y="135" width="30" height="35" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Left eye -->
|
||||||
|
<mxCell id="7" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#000000;strokeColor=#000000;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="325" y="185" width="15" height="15" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Right eye -->
|
||||||
|
<mxCell id="8" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#000000;strokeColor=#000000;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="380" y="185" width="15" height="15" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Nose -->
|
||||||
|
<mxCell id="9" value="" style="triangle;whiteSpace=wrap;html=1;fillColor=#FFB6C1;strokeColor=#000000;strokeWidth=1;rotation=180;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="350" y="210" width="20" height="15" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Mouth left -->
|
||||||
|
<mxCell id="10" value="" style="curved=1;endArrow=none;html=1;strokeColor=#000000;strokeWidth=2;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="1">
|
||||||
|
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||||
|
<mxPoint x="360" y="220" as="sourcePoint"/>
|
||||||
|
<mxPoint x="340" y="235" as="targetPoint"/>
|
||||||
|
<Array as="points">
|
||||||
|
<mxPoint x="355" y="230"/>
|
||||||
|
</Array>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Mouth right -->
|
||||||
|
<mxCell id="11" value="" style="curved=1;endArrow=none;html=1;strokeColor=#000000;strokeWidth=2;" edge="1" parent="1">
|
||||||
|
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||||
|
<mxPoint x="360" y="220" as="sourcePoint"/>
|
||||||
|
<mxPoint x="380" y="235" as="targetPoint"/>
|
||||||
|
<Array as="points">
|
||||||
|
<mxPoint x="365" y="230"/>
|
||||||
|
</Array>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Left whisker 1 -->
|
||||||
|
<mxCell id="12" value="" style="endArrow=none;html=1;strokeColor=#000000;strokeWidth=1.5;" edge="1" parent="1">
|
||||||
|
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||||
|
<mxPoint x="310" y="200" as="sourcePoint"/>
|
||||||
|
<mxPoint x="260" y="195" as="targetPoint"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Left whisker 2 -->
|
||||||
|
<mxCell id="13" value="" style="endArrow=none;html=1;strokeColor=#000000;strokeWidth=1.5;" edge="1" parent="1">
|
||||||
|
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||||
|
<mxPoint x="310" y="210" as="sourcePoint"/>
|
||||||
|
<mxPoint x="260" y="210" as="targetPoint"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Left whisker 3 -->
|
||||||
|
<mxCell id="14" value="" style="endArrow=none;html=1;strokeColor=#000000;strokeWidth=1.5;" edge="1" parent="1">
|
||||||
|
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||||
|
<mxPoint x="310" y="220" as="sourcePoint"/>
|
||||||
|
<mxPoint x="260" y="225" as="targetPoint"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Right whisker 1 -->
|
||||||
|
<mxCell id="15" value="" style="endArrow=none;html=1;strokeColor=#000000;strokeWidth=1.5;" edge="1" parent="1">
|
||||||
|
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||||
|
<mxPoint x="410" y="200" as="sourcePoint"/>
|
||||||
|
<mxPoint x="460" y="195" as="targetPoint"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Right whisker 2 -->
|
||||||
|
<mxCell id="16" value="" style="endArrow=none;html=1;strokeColor=#000000;strokeWidth=1.5;" edge="1" parent="1">
|
||||||
|
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||||
|
<mxPoint x="410" y="210" as="sourcePoint"/>
|
||||||
|
<mxPoint x="460" y="210" as="targetPoint"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Right whisker 3 -->
|
||||||
|
<mxCell id="17" value="" style="endArrow=none;html=1;strokeColor=#000000;strokeWidth=1.5;" edge="1" parent="1">
|
||||||
|
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||||
|
<mxPoint x="410" y="220" as="sourcePoint"/>
|
||||||
|
<mxPoint x="460" y="225" as="targetPoint"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Body -->
|
||||||
|
<mxCell id="18" value="" style="ellipse;whiteSpace=wrap;html=1;fillColor=#FFE6CC;strokeColor=#000000;strokeWidth=2;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="285" y="250" width="150" height="180" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Belly -->
|
||||||
|
<mxCell id="19" value="" style="ellipse;whiteSpace=wrap;html=1;fillColor=#FFFFFF;strokeColor=none;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="315" y="280" width="90" height="120" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Left front paw -->
|
||||||
|
<mxCell id="20" value="" style="ellipse;whiteSpace=wrap;html=1;fillColor=#FFE6CC;strokeColor=#000000;strokeWidth=2;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="300" y="410" width="40" height="50" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Right front paw -->
|
||||||
|
<mxCell id="21" value="" style="ellipse;whiteSpace=wrap;html=1;fillColor=#FFE6CC;strokeColor=#000000;strokeWidth=2;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="380" y="410" width="40" height="50" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
<!-- Tail -->
|
||||||
|
<mxCell id="22" value="" style="curved=1;endArrow=none;html=1;strokeColor=#000000;strokeWidth=3;fillColor=#FFE6CC;" edge="1" parent="1">
|
||||||
|
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||||
|
<mxPoint x="285" y="340" as="sourcePoint"/>
|
||||||
|
<mxPoint x="240" y="260" as="targetPoint"/>
|
||||||
|
<Array as="points">
|
||||||
|
<mxPoint x="260" y="350"/>
|
||||||
|
<mxPoint x="240" y="320"/>
|
||||||
|
<mxPoint x="235" y="290"/>
|
||||||
|
</Array>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
|
||||||
|
</root>`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export function findCachedResponse(
|
||||||
|
promptText: string,
|
||||||
|
hasImage: boolean
|
||||||
|
): CachedResponse | undefined {
|
||||||
|
return CACHED_EXAMPLE_RESPONSES.find(
|
||||||
|
(c) => c.promptText === promptText && c.hasImage === hasImage && c.xml !== ''
|
||||||
|
);
|
||||||
|
}
|
||||||
4821
package-lock.json
generated
4821
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -9,9 +9,10 @@
|
|||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ai-sdk/amazon-bedrock": "^3.0.52",
|
"@ai-sdk/amazon-bedrock": "^3.0.62",
|
||||||
"@ai-sdk/anthropic": "^2.0.44",
|
"@ai-sdk/anthropic": "^2.0.44",
|
||||||
"@ai-sdk/azure": "^2.0.69",
|
"@ai-sdk/azure": "^2.0.69",
|
||||||
|
"@ai-sdk/deepseek": "^1.0.30",
|
||||||
"@ai-sdk/google": "^2.0.0",
|
"@ai-sdk/google": "^2.0.0",
|
||||||
"@ai-sdk/openai": "^2.0.19",
|
"@ai-sdk/openai": "^2.0.19",
|
||||||
"@ai-sdk/react": "^2.0.22",
|
"@ai-sdk/react": "^2.0.22",
|
||||||
@@ -46,6 +47,8 @@
|
|||||||
"@types/pako": "^2.0.3",
|
"@types/pako": "^2.0.3",
|
||||||
"@types/react": "^19",
|
"@types/react": "^19",
|
||||||
"@types/react-dom": "^19",
|
"@types/react-dom": "^19",
|
||||||
|
"eslint": "9.39.1",
|
||||||
|
"eslint-config-next": "16.0.5",
|
||||||
"tailwindcss": "^4",
|
"tailwindcss": "^4",
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user