create-atlas-agent 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +69 -0
  2. package/index.ts +526 -0
  3. package/package.json +33 -0
  4. package/template/.env.example +49 -0
  5. package/template/Dockerfile +31 -0
  6. package/template/bin/atlas.ts +1092 -0
  7. package/template/bin/enrich.ts +551 -0
  8. package/template/data/.gitkeep +0 -0
  9. package/template/data/demo-sqlite.sql +372 -0
  10. package/template/data/demo.sql +371 -0
  11. package/template/docker-compose.yml +23 -0
  12. package/template/docs/deploy.md +341 -0
  13. package/template/eslint.config.mjs +18 -0
  14. package/template/fly.toml +46 -0
  15. package/template/gitignore +5 -0
  16. package/template/next.config.ts +8 -0
  17. package/template/package.json +55 -0
  18. package/template/postcss.config.mjs +8 -0
  19. package/template/public/.gitkeep +0 -0
  20. package/template/railway.json +13 -0
  21. package/template/render.yaml +19 -0
  22. package/template/semantic/catalog.yml +5 -0
  23. package/template/semantic/entities/.gitkeep +0 -0
  24. package/template/semantic/glossary.yml +6 -0
  25. package/template/semantic/metrics/.gitkeep +0 -0
  26. package/template/src/app/api/chat/route.ts +107 -0
  27. package/template/src/app/api/health/route.ts +97 -0
  28. package/template/src/app/error.tsx +24 -0
  29. package/template/src/app/globals.css +1 -0
  30. package/template/src/app/layout.tsx +19 -0
  31. package/template/src/app/page.tsx +650 -0
  32. package/template/src/global.d.ts +1 -0
  33. package/template/src/lib/agent.ts +112 -0
  34. package/template/src/lib/db/connection.ts +150 -0
  35. package/template/src/lib/providers.ts +63 -0
  36. package/template/src/lib/semantic.ts +53 -0
  37. package/template/src/lib/startup.ts +211 -0
  38. package/template/src/lib/tools/__tests__/sql.test.ts +538 -0
  39. package/template/src/lib/tools/explore-sandbox.ts +189 -0
  40. package/template/src/lib/tools/explore.ts +164 -0
  41. package/template/src/lib/tools/report.ts +33 -0
  42. package/template/src/lib/tools/sql.ts +202 -0
  43. package/template/src/types/vercel-sandbox.d.ts +54 -0
  44. package/template/tsconfig.json +41 -0
  45. package/template/vercel.json +3 -0
@@ -0,0 +1,341 @@
1
+ # Deploy Guides
2
+
3
+ Atlas ships a multi-stage `Dockerfile` that produces a standalone Next.js build. It runs on any Docker-capable platform. This guide covers Docker, Railway, Fly.io, Render, and Vercel.
4
+
5
+ ---
6
+
7
+ ## Quick Deploy: Vercel
8
+
9
+ Go from zero to production in 5 minutes.
10
+
11
+ 1. **Scaffold your project:**
12
+
13
+ ```bash
14
+ bun create atlas-agent my-app
15
+ cd my-app
16
+ ```
17
+
18
+ 2. **Push to GitHub:**
19
+
20
+ ```bash
21
+ git init && git add -A && git commit -m "Initial commit"
22
+ gh repo create my-app --public --source=. --push
23
+ ```
24
+
25
+ 3. **Import in Vercel:** Open the [Vercel Dashboard](https://vercel.com/), click **Add New > Project**, and import your repo. Vercel auto-detects Next.js via `vercel.json`.
26
+
27
+ 4. **Set environment variables** in the Vercel project settings:
28
+
29
+ ```
30
+ ATLAS_PROVIDER=anthropic
31
+ ANTHROPIC_API_KEY=sk-ant-...
32
+ DATABASE_URL=postgresql://user:pass@host:5432/dbname
33
+ ```
34
+
35
+ 5. **Deploy.** Vercel builds and deploys automatically.
36
+
37
+ 6. **Verify:** `https://<your-app>.vercel.app/api/health` -- should return `{"status":"ok"}`
38
+
39
+ **What happens automatically on Vercel:**
40
+
41
+ - The explore tool uses `@vercel/sandbox` instead of `just-bash` when the `VERCEL` env var is present (no config needed)
42
+ - `output: "standalone"` is auto-skipped on Vercel (see `next.config.ts` -- the `VERCEL` env var check)
43
+ - `maxDuration` is already set to 60s in the chat API route
44
+ - `pg` and `just-bash` are in `serverExternalPackages` for serverless compatibility
45
+
46
+ For more details, see the [full Vercel section](#vercel) below.
47
+
48
+ ---
49
+
50
+ ## Quick Deploy: Railway
51
+
52
+ Go from zero to production with managed Postgres included.
53
+
54
+ 1. **Scaffold your project:**
55
+
56
+ ```bash
57
+ bun create atlas-agent my-app
58
+ cd my-app
59
+ ```
60
+
61
+ 2. **Push to GitHub:**
62
+
63
+ ```bash
64
+ git init && git add -A && git commit -m "Initial commit"
65
+ gh repo create my-app --public --source=. --push
66
+ ```
67
+
68
+ 3. **Create a Railway project:** Go to the [Railway Dashboard](https://railway.app/) and click **New Project**.
69
+
70
+ 4. **Add a Postgres plugin:** Click **+ New** inside the project and add **Database > PostgreSQL**. Link it to your web service -- Railway injects `DATABASE_URL` automatically.
71
+
72
+ 5. **Connect your repo:** Click **+ New > GitHub Repo** and select your repo. Railway detects `railway.json` and builds from the Dockerfile.
73
+
74
+ 6. **Set environment variables** in the Railway service settings (only 2 -- `DATABASE_URL` is already set):
75
+
76
+ ```
77
+ ATLAS_PROVIDER=anthropic
78
+ ANTHROPIC_API_KEY=sk-ant-...
79
+ ```
80
+
81
+ 7. **Seed your data.** Either seed the demo dataset or generate a semantic layer from your own tables:
82
+
83
+ ```bash
84
+ # Option A: Demo data
85
+ psql "$RAILWAY_DATABASE_URL" < data/demo.sql
86
+
87
+ # Option B: Your own data
88
+ DATABASE_URL="$RAILWAY_DATABASE_URL" bun run atlas -- init
89
+ ```
90
+
91
+ 8. **Deploy.** Railway builds and starts the container automatically.
92
+
93
+ 9. **Verify:** `https://<your-app>.up.railway.app/api/health` -- should return `{"status":"ok"}`
94
+
95
+ **What happens automatically on Railway:**
96
+
97
+ - `DATABASE_URL` is injected by the Postgres plugin -- no manual config needed
98
+ - `railway.json` configures Dockerfile builds, health checks, and restart policy
99
+ - The Docker `HEALTHCHECK` polls `/api/health` every 30 seconds
100
+
101
+ For more details, see the [full Railway section](#railway) below.
102
+
103
+ ---
104
+
105
+ ## Required environment variables
106
+
107
+ Every deployment needs these three:
108
+
109
+ | Variable | Example |
110
+ |----------|---------|
111
+ | `ATLAS_PROVIDER` | `anthropic` |
112
+ | Provider API key | `ANTHROPIC_API_KEY=sk-ant-...` |
113
+ | `DATABASE_URL` | `postgresql://user:pass@host:5432/dbname` |
114
+
115
+ Optional variables (safe defaults for most deployments):
116
+
117
+ | Variable | Default | Description |
118
+ |----------|---------|-------------|
119
+ | `ATLAS_MODEL` | Provider default | Override the LLM model |
120
+ | `ATLAS_ROW_LIMIT` | `1000` | Max rows returned per query |
121
+ | `ATLAS_QUERY_TIMEOUT` | `30000` | Query timeout in ms |
122
+ | `PORT` | `3000` | Set automatically by most platforms |
123
+
124
+ ## Health check
125
+
126
+ All deployments should verify with the health endpoint:
127
+
128
+ ```
129
+ GET /api/health
130
+ ```
131
+
132
+ Returns JSON with status `"ok"`, `"degraded"`, or `"error"` and sub-checks for database connectivity, provider configuration, and semantic layer presence. Returns HTTP 200 when status is `"ok"` or `"degraded"`, and HTTP 503 when status is `"error"` (database unreachable).
133
+
134
+ ---
135
+
136
+ ## Docker
137
+
138
+ The `Dockerfile` uses a three-stage build: install deps, build Next.js standalone output, then run with a minimal image.
139
+
140
+ ### Build and run
141
+
142
+ ```bash
143
+ docker build -t atlas .
144
+ docker run -p 3000:3000 \
145
+ -e ATLAS_PROVIDER=anthropic \
146
+ -e ANTHROPIC_API_KEY=sk-ant-... \
147
+ -e DATABASE_URL=postgresql://user:pass@host:5432/dbname \
148
+ atlas
149
+ ```
150
+
151
+ ### Verify
152
+
153
+ ```bash
154
+ curl http://localhost:3000/api/health
155
+ ```
156
+
157
+ The Dockerfile includes a built-in `HEALTHCHECK` that polls `/api/health` every 30 seconds.
158
+
159
+ ### Notes
160
+
161
+ - The image is based on `oven/bun:1.3`
162
+ - Standalone output copies only what's needed: `.next/standalone`, `.next/static`, `public/`, and `semantic/`
163
+ - The semantic layer (`semantic/`) is baked into the image at build time. If you update YAMLs, rebuild the image
164
+
165
+ ---
166
+
167
+ ## Railway
168
+
169
+ Railway auto-detects the `Dockerfile` via `railway.json` at the repo root.
170
+
171
+ ### Steps
172
+
173
+ 1. Create a new Railway project
174
+ 2. Add a **Postgres** plugin (or use an external database)
175
+ 3. Connect your GitHub repo -- Railway detects `railway.json` and builds from the Dockerfile
176
+ 4. Set environment variables in the Railway dashboard:
177
+
178
+ ```
179
+ ATLAS_PROVIDER=anthropic
180
+ ANTHROPIC_API_KEY=sk-ant-...
181
+ DATABASE_URL=<Railway-provided Postgres URL>
182
+ ```
183
+
184
+ 5. If using the demo dataset, seed the database:
185
+
186
+ ```bash
187
+ # Connect to the Railway Postgres and run the seed file
188
+ psql "$RAILWAY_DATABASE_URL" < data/demo.sql
189
+ ```
190
+
191
+ Or generate a semantic layer from your own data:
192
+
193
+ ```bash
194
+ DATABASE_URL="$RAILWAY_DATABASE_URL" bun run atlas -- init
195
+ ```
196
+
197
+ 6. Deploy -- Railway builds and starts the container automatically
198
+
199
+ ### Configuration
200
+
201
+ The `railway.json` config sets:
202
+
203
+ - Dockerfile-based builds
204
+ - Health check at `/api/health` with a 60-second timeout
205
+ - Restart on failure (max 10 retries)
206
+
207
+ ### Verify
208
+
209
+ Railway exposes a public URL. Check health at `https://<your-app>.up.railway.app/api/health`.
210
+
211
+ ---
212
+
213
+ ## Fly.io
214
+
215
+ Atlas includes a `fly.toml` at the repo root.
216
+
217
+ ### Steps
218
+
219
+ 1. Install the [Fly CLI](https://fly.io/docs/flyctl/install/)
220
+
221
+ 2. Launch the app (without deploying yet):
222
+
223
+ ```bash
224
+ fly launch --no-deploy
225
+ ```
226
+
227
+ 3. Set secrets:
228
+
229
+ ```bash
230
+ fly secrets set \
231
+ DATABASE_URL="postgresql://user:pass@host:5432/dbname" \
232
+ ANTHROPIC_API_KEY="sk-ant-..."
233
+ ```
234
+
235
+ 4. Deploy:
236
+
237
+ ```bash
238
+ fly deploy
239
+ ```
240
+
241
+ ### Using Fly Postgres
242
+
243
+ To use Fly's managed Postgres instead of an external database:
244
+
245
+ ```bash
246
+ fly postgres create --name atlas-db
247
+ fly postgres attach atlas-db
248
+ ```
249
+
250
+ Fly automatically sets `DATABASE_URL` when you attach. Seed the demo data:
251
+
252
+ ```bash
253
+ fly postgres connect --app atlas-db
254
+ # Then in the psql shell, paste contents of data/demo.sql
255
+ ```
256
+
257
+ ### Configuration
258
+
259
+ The `fly.toml` configures:
260
+
261
+ - Region: `iad` (US East) -- change `primary_region` for your location
262
+ - Health check: `GET /api/health` every 30 seconds
263
+ - Auto-stop/start machines (scales to zero when idle)
264
+ - VM: `shared-cpu-1x` with 512 MB memory
265
+
266
+ ### Verify
267
+
268
+ ```bash
269
+ fly status
270
+ curl https://<your-app>.fly.dev/api/health
271
+ ```
272
+
273
+ ---
274
+
275
+ ## Render
276
+
277
+ Atlas includes a `render.yaml` Blueprint at the repo root.
278
+
279
+ ### Steps
280
+
281
+ 1. Go to the [Render Dashboard](https://dashboard.render.com/) and click **New > Blueprint**
282
+ 2. Connect your GitHub repo -- Render reads `render.yaml`
283
+ 3. Set the prompted environment variables:
284
+ - `DATABASE_URL` -- your Postgres connection string
285
+ - `ANTHROPIC_API_KEY` -- your API key
286
+ 4. Deploy
287
+
288
+ ### Using Render Postgres
289
+
290
+ 1. Create a Render Postgres instance from the dashboard
291
+ 2. Copy the **Internal Connection String**
292
+ 3. Set it as `DATABASE_URL` in the Atlas service environment
293
+
294
+ ### Configuration
295
+
296
+ The `render.yaml` configures:
297
+
298
+ - Docker-based deployment using the repo's `Dockerfile`
299
+ - Health check at `/api/health`
300
+ - Starter plan
301
+ - `ATLAS_PROVIDER` defaults to `anthropic`
302
+ - `autoDeploy` is disabled -- trigger deploys manually from the Render dashboard, or set `autoDeploy: true` in `render.yaml` to deploy on every push
303
+
304
+ ### Verify
305
+
306
+ ```bash
307
+ curl https://<your-app>.onrender.com/api/health
308
+ ```
309
+
310
+ ---
311
+
312
+ ## Vercel
313
+
314
+ Atlas supports Vercel-native deployment. On Vercel, the explore tool uses `@vercel/sandbox` instead of `just-bash` for shell operations.
315
+
316
+ ### Steps
317
+
318
+ 1. Import your repo in the [Vercel Dashboard](https://vercel.com/)
319
+ 2. Vercel auto-detects Next.js via `vercel.json` (framework: `nextjs`)
320
+ 3. Set environment variables:
321
+
322
+ ```
323
+ ATLAS_PROVIDER=anthropic
324
+ ANTHROPIC_API_KEY=sk-ant-...
325
+ DATABASE_URL=postgresql://user:pass@host:5432/dbname
326
+ ```
327
+
328
+ 4. Deploy
329
+
330
+ ### Notes
331
+
332
+ - Vercel is auto-detected via the `VERCEL` environment variable -- no manual `ATLAS_RUNTIME` setting needed
333
+ - The `output: "standalone"` build option is skipped on Vercel (Vercel uses its own build pipeline)
334
+ - `@vercel/sandbox` is an optional dependency and only loaded when running on Vercel
335
+ - `pg` and `just-bash` are listed in `serverExternalPackages` in `next.config.ts` for compatibility with serverless functions
336
+
337
+ ### Verify
338
+
339
+ ```bash
340
+ curl https://<your-app>.vercel.app/api/health
341
+ ```
@@ -0,0 +1,18 @@
1
+ import js from "@eslint/js";
2
+ import tseslint from "typescript-eslint";
3
+ import nextPlugin from "@next/eslint-plugin-next";
4
+
5
+ export default tseslint.config(
6
+ js.configs.recommended,
7
+ tseslint.configs.recommended,
8
+ {
9
+ plugins: { "@next/next": nextPlugin },
10
+ rules: {
11
+ ...nextPlugin.configs.recommended.rules,
12
+ ...nextPlugin.configs["core-web-vitals"].rules,
13
+ },
14
+ },
15
+ {
16
+ ignores: [".next/", "node_modules/"],
17
+ }
18
+ );
@@ -0,0 +1,46 @@
1
+ # Deploy Atlas on Fly.io
2
+ #
3
+ # Quick start:
4
+ # fly launch --no-deploy
5
+ # fly secrets set DATABASE_URL="postgresql://..." ANTHROPIC_API_KEY="sk-ant-..."
6
+ # fly deploy
7
+ #
8
+ # Attach Fly Postgres instead of an external DB (name is a suggestion):
9
+ # fly postgres create --name %PROJECT_NAME%-db
10
+ # fly postgres attach %PROJECT_NAME%-db
11
+
12
+ app = "%PROJECT_NAME%"
13
+ primary_region = "iad"
14
+
15
+ [build]
16
+ dockerfile = "Dockerfile"
17
+
18
+ [env]
19
+ HOSTNAME = "0.0.0.0"
20
+ ATLAS_PROVIDER = "anthropic"
21
+ # Secrets (set via `fly secrets set`):
22
+ # DATABASE_URL
23
+ # ANTHROPIC_API_KEY (or OPENAI_API_KEY, etc.)
24
+
25
+ [http_service]
26
+ internal_port = 3000
27
+ force_https = true
28
+ auto_stop_machines = "stop"
29
+ auto_start_machines = true
30
+ min_machines_running = 0
31
+
32
+ [http_service.concurrency]
33
+ type = "requests"
34
+ hard_limit = 250
35
+ soft_limit = 200
36
+
37
+ [[http_service.checks]]
38
+ grace_period = "30s"
39
+ interval = "30s"
40
+ method = "GET"
41
+ timeout = "5s"
42
+ path = "/api/health"
43
+
44
+ [[vm]]
45
+ size = "shared-cpu-1x"
46
+ memory = "512mb"
@@ -0,0 +1,5 @@
1
+ node_modules/
2
+ .next/
3
+ .env
4
+ .env.local
5
+ *.tsbuildinfo
@@ -0,0 +1,8 @@
1
+ import type { NextConfig } from "next";
2
+
3
+ const nextConfig: NextConfig = {
4
+ ...(process.env.VERCEL ? {} : { output: "standalone" }),
5
+ serverExternalPackages: ["pg", "just-bash"],
6
+ };
7
+
8
+ export default nextConfig;
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "%PROJECT_NAME%",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "description": "Atlas text-to-SQL data analyst agent",
6
+ "scripts": {
7
+ "dev": "next dev --turbopack",
8
+ "build": "next build",
9
+ "start": "next start",
10
+ "lint": "eslint src/",
11
+ "type": "tsgo --noEmit",
12
+ "atlas": "bun bin/atlas.ts",
13
+ "db:up": "docker compose up -d postgres",
14
+ "db:down": "docker compose down",
15
+ "db:reset": "docker compose down -v && docker compose up -d postgres",
16
+ "test": "bun test"
17
+ },
18
+ "dependencies": {
19
+ "@ai-sdk/amazon-bedrock": "^4.0.63",
20
+ "@ai-sdk/anthropic": "^3.0.46",
21
+ "@ai-sdk/openai": "^3.0.31",
22
+ "@ai-sdk/react": "^3.0.99",
23
+ "ai": "^6.0.97",
24
+ "js-yaml": "^4.1.1",
25
+ "just-bash": "^2.10.2",
26
+ "next": "^16.1.6",
27
+ "node-sql-parser": "^5.4.0",
28
+ "pg": "^8.18.0",
29
+ "react": "^19.2.4",
30
+ "react-dom": "^19.2.4",
31
+ "react-markdown": "^10.1.0",
32
+ "react-syntax-highlighter": "^16.1.0",
33
+ "recharts": "^3.7.0",
34
+ "zod": "^4.3.6"
35
+ },
36
+ "optionalDependencies": {
37
+ "@vercel/sandbox": "^1"
38
+ },
39
+ "devDependencies": {
40
+ "@eslint/js": "^10.0.1",
41
+ "@next/eslint-plugin-next": "^16.1.6",
42
+ "@tailwindcss/postcss": "^4.2.1",
43
+ "@types/js-yaml": "^4.0.9",
44
+ "@types/node": "^25.3.0",
45
+ "@types/pg": "^8.16.0",
46
+ "@types/react": "^19.2.14",
47
+ "@types/react-dom": "^19.2.3",
48
+ "@types/react-syntax-highlighter": "^15.5.13",
49
+ "@typescript/native-preview": "^7.0.0-dev.20260223.1",
50
+ "eslint": "^10.0.2",
51
+ "tailwindcss": "^4.2.1",
52
+ "typescript": "^5.7.0",
53
+ "typescript-eslint": "^8.56.1"
54
+ }
55
+ }
@@ -0,0 +1,8 @@
1
+ /** @type {import('postcss-load-config').Config} */
2
+ const config = {
3
+ plugins: {
4
+ "@tailwindcss/postcss": {},
5
+ },
6
+ };
7
+
8
+ export default config;
File without changes
@@ -0,0 +1,13 @@
1
+ {
2
+ "$schema": "https://railway.com/railway.schema.json",
3
+ "build": {
4
+ "builder": "DOCKERFILE",
5
+ "dockerfilePath": "Dockerfile"
6
+ },
7
+ "deploy": {
8
+ "restartPolicyType": "ON_FAILURE",
9
+ "restartPolicyMaxRetries": 10,
10
+ "healthcheckPath": "/api/health",
11
+ "healthcheckTimeout": 60
12
+ }
13
+ }
@@ -0,0 +1,19 @@
1
+ # Render Blueprint — deploy Atlas with "New > Blueprint" pointing at this repo.
2
+ # https://render.com/docs/infrastructure-as-code
3
+
4
+ services:
5
+ - type: web
6
+ name: %PROJECT_NAME%
7
+ runtime: docker
8
+ dockerfilePath: ./Dockerfile
9
+ plan: starter
10
+ autoDeploy: false
11
+ healthCheckPath: /api/health
12
+ healthCheckTimeout: 15
13
+ envVars:
14
+ - key: DATABASE_URL
15
+ sync: false
16
+ - key: ATLAS_PROVIDER
17
+ value: anthropic
18
+ - key: ANTHROPIC_API_KEY
19
+ sync: false
@@ -0,0 +1,5 @@
1
+ # Atlas Semantic Layer — Catalog
2
+ # Run `bun run atlas -- init` to auto-generate from your database.
3
+ version: "1.0"
4
+ entities: []
5
+ glossary: glossary.yml
File without changes
@@ -0,0 +1,6 @@
1
+ # Atlas Glossary — Business term definitions
2
+ # Run `bun run atlas -- init` to auto-generate terms from your database.
3
+ terms:
4
+ example_term:
5
+ status: defined
6
+ definition: Replace this with your own business terms
File without changes
@@ -0,0 +1,107 @@
1
+ import { type UIMessage } from "ai";
2
+ import { runAgent } from "@/lib/agent";
3
+ import { validateEnvironment } from "@/lib/startup";
4
+ import { GatewayModelNotFoundError } from "@ai-sdk/gateway";
5
+
6
+ export const maxDuration = 60;
7
+
8
+ export async function POST(req: Request) {
9
+ // Startup diagnostics — fast-fail with actionable errors
10
+ const diagnostics = await validateEnvironment();
11
+ if (diagnostics.length > 0) {
12
+ return Response.json(
13
+ {
14
+ error: "configuration_error",
15
+ message: diagnostics.map((d) => d.message).join("\n\n"),
16
+ diagnostics,
17
+ },
18
+ { status: 400 }
19
+ );
20
+ }
21
+
22
+ // Parse request body separately so malformed JSON gets a 400, not 500
23
+ let messages: UIMessage[];
24
+ try {
25
+ ({ messages } = await req.json());
26
+ } catch {
27
+ return Response.json(
28
+ {
29
+ error: "invalid_request",
30
+ message: "Invalid request body. Expected JSON with a 'messages' array.",
31
+ },
32
+ { status: 400 }
33
+ );
34
+ }
35
+
36
+ try {
37
+ const result = await runAgent({ messages });
38
+ return result.toUIMessageStreamResponse();
39
+ } catch (err) {
40
+ const message = err instanceof Error ? err.message : "";
41
+
42
+ // Gateway-specific errors (structured types, checked before regex fallbacks)
43
+ if (GatewayModelNotFoundError.isInstance(err)) {
44
+ console.error("[atlas] Gateway model not found in /api/chat:", message);
45
+ return Response.json(
46
+ {
47
+ error: "provider_model_not_found",
48
+ message:
49
+ "Model not found on the AI Gateway. Check that your ATLAS_MODEL uses the correct provider/model format (e.g., anthropic/claude-sonnet-4.6).",
50
+ },
51
+ { status: 400 }
52
+ );
53
+ }
54
+
55
+ // LLM provider auth errors (invalid or expired API key)
56
+ if (
57
+ /401|403|unauthorized|authentication|invalid.*key/i.test(message)
58
+ ) {
59
+ console.error("[atlas] Provider auth error in /api/chat:", message);
60
+ return Response.json(
61
+ {
62
+ error: "provider_auth_error",
63
+ message:
64
+ "LLM provider authentication failed. Check that your API key is valid and has not expired.",
65
+ },
66
+ { status: 503 }
67
+ );
68
+ }
69
+
70
+ // LLM provider rate limit errors
71
+ if (/429|rate.?limit|too many requests|overloaded/i.test(message)) {
72
+ console.error("[atlas] Provider rate limit in /api/chat:", message);
73
+ return Response.json(
74
+ {
75
+ error: "provider_rate_limit",
76
+ message:
77
+ "LLM provider rate limit reached. Wait a moment and try again.",
78
+ },
79
+ { status: 503 }
80
+ );
81
+ }
82
+
83
+ // Provider network errors (e.g., network failure to LLM API)
84
+ if (/fetch failed|ECONNREFUSED|ENOTFOUND/i.test(message)) {
85
+ console.error("[atlas] Provider unreachable in /api/chat:", message);
86
+ return Response.json(
87
+ {
88
+ error: "provider_unreachable",
89
+ message:
90
+ "Could not reach the LLM provider. Check your network connection and provider status.",
91
+ },
92
+ { status: 503 }
93
+ );
94
+ }
95
+
96
+ // Fallback — safe 500 with correlation ID for debugging
97
+ const errorId = crypto.randomUUID().slice(0, 8);
98
+ console.error(`[atlas] Unexpected error in /api/chat [${errorId}]:`, err);
99
+ return Response.json(
100
+ {
101
+ error: "internal_error",
102
+ message: `An unexpected error occurred (ref: ${errorId}). If this persists, check the server logs.`,
103
+ },
104
+ { status: 500 }
105
+ );
106
+ }
107
+ }