Skip to content

Commit bf682df

Browse files
committed
Merge branch 'next'
2 parents 8f19f36 + 61a0ee5 commit bf682df

File tree

11 files changed

+2373
-1
lines changed

11 files changed

+2373
-1
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/node_modules
2+
/.env
3+
/dist

README.md

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,86 @@
1-
# diagram-org-chart-builder-ai-demo
1+
# AI Org Chart Builder with DHTMLX Diagram
2+
3+
A full-stack web application that integrates DHTMLX Diagram with AI for generating organizational charts and diagrams from natural language text descriptions.
4+
5+
### **[✨ Try the Live Demo >>>](https://dhtmlx.com/docs/demo/ai-org-chart-builder/)**
6+
7+
## Features
8+
9+
- **Natural Language to Diagram:** Describe your diagram in plain English (e.g., "An organizational chart with CEO and department heads") and watch it come to life.
10+
- **Live Preview:** See the generated DHTMLX Diagram instantly.
11+
- **Editable JSON:** View, edit, and fine-tune the generated JSON configuration in a built-in code editor.
12+
- **Instant Updates:** Modify the JSON and click "Update Preview" to see your changes immediately without calling the AI again.
13+
14+
## AI Service
15+
16+
- Configured to work with any OpenAI API-compatible service.
17+
- Tested with `gpt-4.1-nano` model.
18+
19+
## Setup and Installation
20+
21+
Follow these steps to get the project running on your local machine.
22+
23+
```bash
24+
cd diagram-org-chart-builder-ai-demo
25+
npm install
26+
```
27+
28+
### Set up environment variables:
29+
Create a new file named `.env` inside the `diagram-org-chart-builder-ai-demo` directory by copying from `env.sample`. This file holds your secret keys and configuration.
30+
31+
📄 `diagram-org-chart-builder-ai-demo/.env`
32+
```ini
33+
# --- OpenAI API Configuration ---
34+
OPENAI_API_KEY=sk-YourSecretApiKeyGoesHere
35+
OPENAI_BASE_URL=https://api.openai.com/v1
36+
37+
# --- Security Configuration ---
38+
CORS_ALLOWED_ORIGINS=http://localhost:3001,http://127.0.0.1:3001,http://localhost:5500,http://127.0.0.1:5500
39+
40+
# --- Server Configuration (optional) ---
41+
PORT=3001
42+
```
43+
44+
- **`OPENAI_API_KEY`**: (Required) Your secret API key for the AI service.
45+
- **`OPENAI_BASE_URL`**: The API endpoint for the AI service. Can be changed to use a proxy or a different provider compatible with the OpenAI API.
46+
- **`CORS_ALLOWED_ORIGINS`**: A crucial security setting. This is a comma-separated list of web addresses allowed to connect to your backend server. For production, you **must** change this to your public frontend's URL (e.g., `https://myapp.com`).
47+
- **`PORT`**: (Optional) The port number on which the server will run. Defaults to 3001 if not set.
48+
49+
### Run the Application
50+
51+
In the same `diagram-org-chart-builder-ai-demo` directory, run the start command:
52+
```bash
53+
npm start
54+
```
55+
56+
You should see the following output in your terminal:
57+
```
58+
Server started on port 3001
59+
```
60+
61+
### 4. Open in Browser
62+
63+
Open your favorite web browser and navigate to:
64+
[http://localhost:3001](http://localhost:3001)
65+
66+
You should see the application, ready to generate diagrams!
67+
68+
## How It Works: From Text to Diagram
69+
70+
1. **Describe your idea:** The user enters a text description of the diagram, for example, "director and three departments: sales, marketing and development".
71+
2. **AI-Powered JSON Generation:** The text is sent to the server, where our AI generates a structured JSON configuration based on the request.
72+
3. **Instant visualization:** The frontend receives the ready data and immediately renders an interactive DHTMLX diagram that can be seen on the screen right away.
73+
4. **Full control:** Next to the diagram, the JSON code is displayed. You can edit it manually and update the visualization in real time to bring the result to perfection.
74+
75+
## Deployment
76+
77+
This application is ready to be deployed on any service that supports Node.js, such as Render, Heroku, or Vercel.
78+
79+
**Key deployment steps:**
80+
- **Do not** upload your `.env` file. Use the hosting provider's "Environment Variables" section to set `OPENAI_API_KEY`, `OPENAI_BASE_URL`, and `CORS_ALLOWED_ORIGINS`.
81+
- The `Root Directory` should be left blank (or set to /).
82+
- The `Start Command` should be `npm start`.
83+
84+
## License
85+
86+
DHTMLX Diagram is a commercial library - use it under a [valid license](https://dhtmlx.com/docs/products/licenses.shtml) or [evaluation agreement](https://dhtmlx.com/docs/products/dhtmlxDiagram/download.shtml).

backend/ai-prompts.js

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/schema.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
export const schemaList = [
2+
{
3+
type: "function",
4+
function: {
5+
name: "create_org_chart_diagram",
6+
description: "Creates an organizational chart diagram from an array of nodes.",
7+
parameters: {
8+
type: "object",
9+
properties: {
10+
nodes: {
11+
type: "array",
12+
description: "An array of objects, where each object represents an employee.",
13+
items: {
14+
type: "object",
15+
properties: {
16+
id: {
17+
type: "string",
18+
description: "Unique numerical employee identifier using dot notation for hierarchy (e.g., '1', '1.1', '1.2')."
19+
},
20+
text: {
21+
type: "string",
22+
description: "Employee's position (e.g., 'CEO', 'Manager', 'Developer')."
23+
},
24+
parent: {
25+
type: "string",
26+
description: "ID of the direct manager."
27+
},
28+
title: {
29+
type: "string",
30+
description: "Employee name to display."
31+
},
32+
partner: {
33+
type: "boolean",
34+
description: "Indicates if the employee is a partner."
35+
},
36+
assistant: {
37+
type: "boolean",
38+
description: "Indicates if the employee is an assistant."
39+
}
40+
},
41+
required: ["id", "text"]
42+
}
43+
}
44+
},
45+
required: ["nodes"]
46+
}
47+
}
48+
}
49+
];

backend/server.js

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import 'dotenv/config';
2+
import express from 'express';
3+
import { createServer } from 'http';
4+
import { Server } from 'socket.io';
5+
import OpenAI from 'openai';
6+
import path from 'path';
7+
import { fileURLToPath } from 'url';
8+
import { rephrasePrompt, diagramPrompt } from './ai-prompts.js';
9+
import { schemaList } from './schema.js';
10+
11+
const __filename = fileURLToPath(import.meta.url);
12+
const __dirname = path.dirname(__filename);
13+
14+
const app = express();
15+
const http = createServer(app);
16+
17+
const io = new Server(http, {
18+
cors: {
19+
origin: "*",
20+
methods: ["GET", "POST"]
21+
}
22+
});
23+
24+
app.use(express.static(path.join(__dirname, '../frontend')));
25+
26+
const openai = new OpenAI({
27+
apiKey: process.env.OPENAI_API_KEY,
28+
baseURL: process.env.OPENAI_BASE_URL
29+
});
30+
31+
io.on('connection', socket => {
32+
socket.on('rephrase_text', async (data, callback) => {
33+
try {
34+
const rephrasedText = await callOpenAIForRephrase(data.text);
35+
callback({ status: 'success', payload: rephrasedText });
36+
} catch(e) {
37+
callback({ status: 'error', message: e.message });
38+
}
39+
});
40+
41+
socket.on('generate_diagram', async (data, callback) => {
42+
try {
43+
const assistantMessage = await callOpenAIForDiagram(data.text);
44+
if (!assistantMessage?.tool_calls?.length) {
45+
throw new Error("AI did not return a valid function call.");
46+
}
47+
const toolCall = assistantMessage.tool_calls[0];
48+
const parsedResult = JSON.parse(toolCall.function.arguments);
49+
50+
parsedResult.nodes.forEach(node => {
51+
if (!node.text) {
52+
node.text = "Unknown Position";
53+
}
54+
});
55+
56+
const fixedNodes = fixHierarchy(parsedResult.nodes);
57+
callback({ status: 'success', payload: fixedNodes });
58+
} catch (e) {
59+
console.error('Error generating diagram:', e.message);
60+
callback({ status: 'error', message: e.message });
61+
}
62+
});
63+
});
64+
65+
function fixHierarchy(nodes) {
66+
if (!nodes || nodes.length === 0) return [];
67+
const existingIds = new Set(nodes.map(node => node.id));
68+
nodes.forEach(node => {
69+
if (node.parent && !existingIds.has(node.parent)) {
70+
console.warn(`"Orphan" detected! Node "${node.text}" refers to a non-existent parent "${node.parent}". Connection removed.`);
71+
delete node.parent;
72+
}
73+
});
74+
return nodes;
75+
}
76+
77+
async function callOpenAIForRephrase(userText) {
78+
const messages = [{ role: 'system', content: rephrasePrompt }, { role: 'user', content: userText }];
79+
80+
try {
81+
const res = await openai.chat.completions.create({
82+
model: 'gpt-4.1-nano',
83+
messages: messages,
84+
});
85+
return res.choices[0].message.content;
86+
} catch (e) {
87+
throw new Error(`Error from AI service: ${e.message}`);
88+
}
89+
}
90+
91+
async function callOpenAIForDiagram(userText) {
92+
const messages = [{ role: 'system', content: diagramPrompt }, { role: 'user', content: userText }];
93+
94+
try {
95+
const res = await openai.chat.completions.create({
96+
model: 'gpt-4.1-nano',
97+
messages: messages,
98+
tools: schemaList,
99+
tool_choice: { type: "function", function: { name: "create_org_chart_diagram" } },
100+
});
101+
return res.choices[0].message;
102+
} catch (e) {
103+
throw new Error(`Error from AI service: ${e.message}`);
104+
}
105+
}
106+
107+
const PORT = process.env.PORT || 3001;
108+
http.listen(PORT, () => {
109+
console.log(`Server is running on http://localhost:${PORT}`);
110+
});

env.sample

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Environment variables
2+
#
3+
# INSTRUCTIONS:
4+
# 1. Copy this file to a new file named .env
5+
# 2. Fill in the required values below.
6+
#
7+
# NOTE: The .env file contains secrets and must NOT be committed to Git.
8+
#
9+
# --- OpenAI API Configuration ---
10+
11+
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
12+
OPENAI_BASE_URL=https://api.openai.com/v1
13+
14+
15+
# --- Security Configuration ---
16+
# A comma-separated list of domains allowed to connect to this server.
17+
# For local development, the default is usually sufficient.
18+
# For production, set this to your public frontend URL.
19+
# Example: https://my-app.com,https://staging.my-app.com
20+
21+
CORS_ALLOWED_ORIGINS=http://localhost:3001,http://localhost:3002,http://127.0.0.1:3001,http://localhost:5500,http://127.0.0.1:5500
22+
23+
# --- Server Configuration (optional) ---
24+
PORT=3001

0 commit comments

Comments
 (0)