bentosmau commited on
Commit ·
c69a8c3
1
Parent(s): c8ed262
Add a functional AI chatbot using Gradio and OpenAI
Browse filesIntegrate Gradio and OpenAI to create a conversational AI chatbot with streaming responses and conversation history, updating `app.py`, `replit.md`, `chat-app/README.md`, and dependency files.
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: e3ff2484-bbd8-4aba-bea0-1940769b874a
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: aa4da6a9-2b45-4fbd-afc1-90fc22861497
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1739408b-93a5-479b-a658-30f2493b0467/e3ff2484-bbd8-4aba-bea0-1940769b874a/17Q9XjF
Replit-Helium-Checkpoint-Created: true
- .replit +26 -1
- chat-app/README.md +34 -0
- chat-app/app.py +98 -0
- chat-app/requirements.txt +2 -0
- main.py +6 -0
- pyproject.toml +9 -0
- replit.md +38 -57
- uv.lock +0 -0
.replit
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
modules = ["nodejs-24"]
|
| 2 |
|
| 3 |
[[artifacts]]
|
| 4 |
id = "artifacts/api-server"
|
|
@@ -17,6 +17,27 @@ env = { "CI" = "true" }
|
|
| 17 |
[workflows]
|
| 18 |
runButton = "Project"
|
| 19 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
[agent]
|
| 21 |
stack = "PNPM_WORKSPACE"
|
| 22 |
expertMode = true
|
|
@@ -24,3 +45,7 @@ expertMode = true
|
|
| 24 |
[postMerge]
|
| 25 |
path = "scripts/post-merge.sh"
|
| 26 |
timeoutMs = 20000
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
modules = ["nodejs-24", "python-3.11"]
|
| 2 |
|
| 3 |
[[artifacts]]
|
| 4 |
id = "artifacts/api-server"
|
|
|
|
| 17 |
[workflows]
|
| 18 |
runButton = "Project"
|
| 19 |
|
| 20 |
+
[[workflows.workflow]]
|
| 21 |
+
name = "Project"
|
| 22 |
+
mode = "parallel"
|
| 23 |
+
author = "agent"
|
| 24 |
+
|
| 25 |
+
[[workflows.workflow.tasks]]
|
| 26 |
+
task = "workflow.run"
|
| 27 |
+
args = "Start application"
|
| 28 |
+
|
| 29 |
+
[[workflows.workflow]]
|
| 30 |
+
name = "Start application"
|
| 31 |
+
author = "agent"
|
| 32 |
+
|
| 33 |
+
[[workflows.workflow.tasks]]
|
| 34 |
+
task = "shell.exec"
|
| 35 |
+
args = "PORT=5000 python chat-app/app.py"
|
| 36 |
+
waitForPort = 5000
|
| 37 |
+
|
| 38 |
+
[workflows.workflow.metadata]
|
| 39 |
+
outputType = "webview"
|
| 40 |
+
|
| 41 |
[agent]
|
| 42 |
stack = "PNPM_WORKSPACE"
|
| 43 |
expertMode = true
|
|
|
|
| 45 |
[postMerge]
|
| 46 |
path = "scripts/post-merge.sh"
|
| 47 |
timeoutMs = 20000
|
| 48 |
+
|
| 49 |
+
[nix]
|
| 50 |
+
channel = "stable-25_05"
|
| 51 |
+
packages = ["ffmpeg-full"]
|
chat-app/README.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Mi IA Chat
|
| 3 |
+
emoji: 🤖
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: indigo
|
| 6 |
+
sdk: gradio
|
| 7 |
+
sdk_version: "4.44.0"
|
| 8 |
+
app_file: app.py
|
| 9 |
+
pinned: false
|
| 10 |
+
license: mit
|
| 11 |
+
---
|
| 12 |
+
|
| 13 |
+
# Mi IA Chat
|
| 14 |
+
|
| 15 |
+
Asistente de inteligencia artificial conversacional construido con Gradio y OpenAI.
|
| 16 |
+
|
| 17 |
+
## Características
|
| 18 |
+
|
| 19 |
+
- Chat conversacional con IA avanzada
|
| 20 |
+
- Streaming de respuestas en tiempo real
|
| 21 |
+
- Historial de conversación
|
| 22 |
+
- Interfaz limpia y moderna
|
| 23 |
+
|
| 24 |
+
## Cómo ejecutar localmente
|
| 25 |
+
|
| 26 |
+
```bash
|
| 27 |
+
pip install -r requirements.txt
|
| 28 |
+
python app.py
|
| 29 |
+
```
|
| 30 |
+
|
| 31 |
+
## Variables de entorno necesarias
|
| 32 |
+
|
| 33 |
+
- `AI_INTEGRATIONS_OPENAI_BASE_URL` - URL base de la API
|
| 34 |
+
- `AI_INTEGRATIONS_OPENAI_API_KEY` - Clave API (o usa tu propia clave de OpenAI como `OPENAI_API_KEY`)
|
chat-app/app.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import gradio as gr
|
| 3 |
+
from openai import OpenAI
|
| 4 |
+
|
| 5 |
+
client = OpenAI(
|
| 6 |
+
base_url=os.environ.get("AI_INTEGRATIONS_OPENAI_BASE_URL"),
|
| 7 |
+
api_key=os.environ.get("AI_INTEGRATIONS_OPENAI_API_KEY", "dummy"),
|
| 8 |
+
)
|
| 9 |
+
|
| 10 |
+
SYSTEM_PROMPT = """Eres un asistente de IA inteligente y útil, similar a ChatGPT o Gemini.
|
| 11 |
+
Respondes en el mismo idioma que el usuario.
|
| 12 |
+
Eres amable, preciso y detallado en tus respuestas."""
|
| 13 |
+
|
| 14 |
+
def responder(mensaje, historial):
|
| 15 |
+
mensajes = [{"role": "system", "content": SYSTEM_PROMPT}]
|
| 16 |
+
for turno in historial:
|
| 17 |
+
mensajes.append({"role": "user", "content": turno[0]})
|
| 18 |
+
if turno[1]:
|
| 19 |
+
mensajes.append({"role": "assistant", "content": turno[1]})
|
| 20 |
+
mensajes.append({"role": "user", "content": mensaje})
|
| 21 |
+
|
| 22 |
+
respuesta_parcial = ""
|
| 23 |
+
historial = historial + [[mensaje, ""]]
|
| 24 |
+
|
| 25 |
+
stream = client.chat.completions.create(
|
| 26 |
+
model="gpt-5.2",
|
| 27 |
+
messages=mensajes,
|
| 28 |
+
stream=True,
|
| 29 |
+
max_completion_tokens=8192,
|
| 30 |
+
)
|
| 31 |
+
|
| 32 |
+
for chunk in stream:
|
| 33 |
+
contenido = chunk.choices[0].delta.content or ""
|
| 34 |
+
respuesta_parcial += contenido
|
| 35 |
+
historial[-1][1] = respuesta_parcial
|
| 36 |
+
yield historial, ""
|
| 37 |
+
|
| 38 |
+
with gr.Blocks(title="Mi IA Chat") as demo:
|
| 39 |
+
gr.Markdown(
|
| 40 |
+
"""
|
| 41 |
+
# 🤖 Mi IA Chat
|
| 42 |
+
### Asistente de inteligencia artificial conversacional
|
| 43 |
+
"""
|
| 44 |
+
)
|
| 45 |
+
|
| 46 |
+
chatbot = gr.Chatbot(
|
| 47 |
+
show_label=False,
|
| 48 |
+
height=500,
|
| 49 |
+
avatar_images=(None, "https://api.dicebear.com/7.x/bottts/svg?seed=ai"),
|
| 50 |
+
)
|
| 51 |
+
|
| 52 |
+
with gr.Row():
|
| 53 |
+
entrada = gr.Textbox(
|
| 54 |
+
placeholder="Escribe tu mensaje aquí...",
|
| 55 |
+
show_label=False,
|
| 56 |
+
scale=9,
|
| 57 |
+
container=False,
|
| 58 |
+
autofocus=True,
|
| 59 |
+
)
|
| 60 |
+
btn_enviar = gr.Button("Enviar", scale=1, variant="primary")
|
| 61 |
+
|
| 62 |
+
with gr.Row():
|
| 63 |
+
btn_limpiar = gr.Button("🗑️ Limpiar conversación", size="sm")
|
| 64 |
+
|
| 65 |
+
gr.Examples(
|
| 66 |
+
examples=[
|
| 67 |
+
"¿Cuál es la diferencia entre machine learning e inteligencia artificial?",
|
| 68 |
+
"Explícame cómo funciona una red neuronal de forma sencilla",
|
| 69 |
+
"¿Puedes escribir un poema sobre la tecnología?",
|
| 70 |
+
"¿Cuáles son las mejores prácticas en programación Python?",
|
| 71 |
+
],
|
| 72 |
+
inputs=entrada,
|
| 73 |
+
label="Ejemplos de preguntas",
|
| 74 |
+
)
|
| 75 |
+
|
| 76 |
+
entrada.submit(
|
| 77 |
+
fn=responder,
|
| 78 |
+
inputs=[entrada, chatbot],
|
| 79 |
+
outputs=[chatbot, entrada],
|
| 80 |
+
)
|
| 81 |
+
btn_enviar.click(
|
| 82 |
+
fn=responder,
|
| 83 |
+
inputs=[entrada, chatbot],
|
| 84 |
+
outputs=[chatbot, entrada],
|
| 85 |
+
)
|
| 86 |
+
btn_limpiar.click(
|
| 87 |
+
fn=lambda: ([], ""),
|
| 88 |
+
outputs=[chatbot, entrada],
|
| 89 |
+
)
|
| 90 |
+
|
| 91 |
+
if __name__ == "__main__":
|
| 92 |
+
port = int(os.environ.get("PORT", 5000))
|
| 93 |
+
demo.launch(
|
| 94 |
+
server_name="0.0.0.0",
|
| 95 |
+
server_port=port,
|
| 96 |
+
share=False,
|
| 97 |
+
theme=gr.themes.Soft(primary_hue="blue"),
|
| 98 |
+
)
|
chat-app/requirements.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
gradio>=4.44.0
|
| 2 |
+
openai>=1.50.0
|
main.py
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
def main():
|
| 2 |
+
print("Hello from repl-nix-workspace!")
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
if __name__ == "__main__":
|
| 6 |
+
main()
|
pyproject.toml
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[project]
|
| 2 |
+
name = "repl-nix-workspace"
|
| 3 |
+
version = "0.1.0"
|
| 4 |
+
description = "Add your description here"
|
| 5 |
+
requires-python = ">=3.11"
|
| 6 |
+
dependencies = [
|
| 7 |
+
"gradio>=6.9.0",
|
| 8 |
+
"openai>=2.28.0",
|
| 9 |
+
]
|
replit.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
| 2 |
|
| 3 |
## Overview
|
| 4 |
|
| 5 |
-
pnpm workspace monorepo
|
| 6 |
|
| 7 |
## Stack
|
| 8 |
|
|
@@ -15,82 +15,63 @@ pnpm workspace monorepo using TypeScript. Each package manages its own dependenc
|
|
| 15 |
- **Validation**: Zod (`zod/v4`), `drizzle-zod`
|
| 16 |
- **API codegen**: Orval (from OpenAPI spec)
|
| 17 |
- **Build**: esbuild (CJS bundle)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
|
| 19 |
## Structure
|
| 20 |
|
| 21 |
```text
|
| 22 |
artifacts-monorepo/
|
| 23 |
-
├──
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
│ └── api-server/ # Express API server
|
| 25 |
├── lib/ # Shared libraries
|
| 26 |
│ ├── api-spec/ # OpenAPI spec + Orval codegen config
|
| 27 |
│ ├── api-client-react/ # Generated React Query hooks
|
| 28 |
│ ├── api-zod/ # Generated Zod schemas from OpenAPI
|
| 29 |
│ └── db/ # Drizzle ORM schema + DB connection
|
| 30 |
-
├── scripts/ # Utility scripts
|
| 31 |
-
|
| 32 |
-
├── pnpm-workspace.yaml # pnpm workspace (artifacts/*, lib/*, lib/integrations/*, scripts)
|
| 33 |
-
├── tsconfig.base.json # Shared TS options (composite, bundler resolution, es2022)
|
| 34 |
-
├── tsconfig.json # Root TS project references
|
| 35 |
-
└── package.json # Root package with hoisted devDeps
|
| 36 |
```
|
| 37 |
|
| 38 |
-
## TypeScript & Composite Projects
|
| 39 |
-
|
| 40 |
-
Every package extends `tsconfig.base.json` which sets `composite: true`. The root `tsconfig.json` lists all packages as project references. This means:
|
| 41 |
-
|
| 42 |
-
- **Always typecheck from the root** — run `pnpm run typecheck` (which runs `tsc --build --emitDeclarationOnly`). This builds the full dependency graph so that cross-package imports resolve correctly. Running `tsc` inside a single package will fail if its dependencies haven't been built yet.
|
| 43 |
-
- **`emitDeclarationOnly`** — we only emit `.d.ts` files during typecheck; actual JS bundling is handled by esbuild/tsx/vite...etc, not `tsc`.
|
| 44 |
-
- **Project references** — when package A depends on package B, A's `tsconfig.json` must list B in its `references` array. `tsc --build` uses this to determine build order and skip up-to-date packages.
|
| 45 |
-
|
| 46 |
-
## Root Scripts
|
| 47 |
-
|
| 48 |
-
- `pnpm run build` — runs `typecheck` first, then recursively runs `build` in all packages that define it
|
| 49 |
-
- `pnpm run typecheck` — runs `tsc --build --emitDeclarationOnly` using project references
|
| 50 |
-
|
| 51 |
## Packages
|
| 52 |
|
| 53 |
### `artifacts/api-server` (`@workspace/api-server`)
|
| 54 |
|
| 55 |
-
Express 5 API server.
|
| 56 |
-
|
| 57 |
-
- Entry: `src/index.ts` — reads `PORT`, starts Express
|
| 58 |
-
- App setup: `src/app.ts` — mounts CORS, JSON/urlencoded parsing, routes at `/api`
|
| 59 |
-
- Routes: `src/routes/index.ts` mounts sub-routers; `src/routes/health.ts` exposes `GET /health` (full path: `/api/health`)
|
| 60 |
-
- Depends on: `@workspace/db`, `@workspace/api-zod`
|
| 61 |
-
- `pnpm --filter @workspace/api-server run dev` — run the dev server
|
| 62 |
-
- `pnpm --filter @workspace/api-server run build` — production esbuild bundle (`dist/index.cjs`)
|
| 63 |
-
- Build bundles an allowlist of deps (express, cors, pg, drizzle-orm, zod, etc.) and externalizes the rest
|
| 64 |
|
| 65 |
### `lib/db` (`@workspace/db`)
|
| 66 |
|
| 67 |
-
Database layer using Drizzle ORM with PostgreSQL.
|
| 68 |
-
|
| 69 |
-
- `src/index.ts` — creates a `Pool` + Drizzle instance, exports schema
|
| 70 |
-
- `src/schema/index.ts` — barrel re-export of all models
|
| 71 |
-
- `src/schema/<modelname>.ts` — table definitions with `drizzle-zod` insert schemas (no models definitions exist right now)
|
| 72 |
-
- `drizzle.config.ts` — Drizzle Kit config (requires `DATABASE_URL`, automatically provided by Replit)
|
| 73 |
-
- Exports: `.` (pool, db, schema), `./schema` (schema only)
|
| 74 |
-
|
| 75 |
-
Production migrations are handled by Replit when publishing. In development, we just use `pnpm --filter @workspace/db run push`, and we fallback to `pnpm --filter @workspace/db run push-force`.
|
| 76 |
|
| 77 |
### `lib/api-spec` (`@workspace/api-spec`)
|
| 78 |
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
1. `lib/api-client-react/src/generated/` — React Query hooks + fetch client
|
| 82 |
-
2. `lib/api-zod/src/generated/` — Zod schemas
|
| 83 |
-
|
| 84 |
-
Run codegen: `pnpm --filter @workspace/api-spec run codegen`
|
| 85 |
-
|
| 86 |
-
### `lib/api-zod` (`@workspace/api-zod`)
|
| 87 |
-
|
| 88 |
-
Generated Zod schemas from the OpenAPI spec (e.g. `HealthCheckResponse`). Used by `api-server` for response validation.
|
| 89 |
-
|
| 90 |
-
### `lib/api-client-react` (`@workspace/api-client-react`)
|
| 91 |
-
|
| 92 |
-
Generated React Query hooks and fetch client from the OpenAPI spec (e.g. `useHealthCheck`, `healthCheck`).
|
| 93 |
-
|
| 94 |
-
### `scripts` (`@workspace/scripts`)
|
| 95 |
-
|
| 96 |
-
Utility scripts package. Each script is a `.ts` file in `src/` with a corresponding npm script in `package.json`. Run scripts via `pnpm --filter @workspace/scripts run <script>`. Scripts can import any workspace package (e.g., `@workspace/db`) by adding it as a dependency in `scripts/package.json`.
|
|
|
|
| 2 |
|
| 3 |
## Overview
|
| 4 |
|
| 5 |
+
pnpm workspace monorepo usando TypeScript para el backend + app Python/Gradio para el chatbot de IA.
|
| 6 |
|
| 7 |
## Stack
|
| 8 |
|
|
|
|
| 15 |
- **Validation**: Zod (`zod/v4`), `drizzle-zod`
|
| 16 |
- **API codegen**: Orval (from OpenAPI spec)
|
| 17 |
- **Build**: esbuild (CJS bundle)
|
| 18 |
+
- **Python version**: 3.11
|
| 19 |
+
- **Chat AI**: Gradio + OpenAI (vía Replit AI Integrations)
|
| 20 |
+
|
| 21 |
+
## Aplicación Principal: Chat IA
|
| 22 |
+
|
| 23 |
+
La aplicación de chat está en `chat-app/`:
|
| 24 |
+
|
| 25 |
+
- `chat-app/app.py` — App Gradio con chat conversacional estilo ChatGPT/Gemini
|
| 26 |
+
- `chat-app/requirements.txt` — Dependencias Python (gradio, openai)
|
| 27 |
+
- `chat-app/README.md` — Configuración para Hugging Face Spaces
|
| 28 |
+
|
| 29 |
+
### Ejecutar localmente
|
| 30 |
+
|
| 31 |
+
```bash
|
| 32 |
+
PORT=5000 python chat-app/app.py
|
| 33 |
+
```
|
| 34 |
+
|
| 35 |
+
### Publicar en Hugging Face Spaces
|
| 36 |
+
|
| 37 |
+
1. Crea una cuenta en [huggingface.co](https://huggingface.co)
|
| 38 |
+
2. Ve a Settings → Access Tokens → New token (con permiso `write`)
|
| 39 |
+
3. Crea un nuevo Space en Hugging Face (tipo: Gradio)
|
| 40 |
+
4. Sube los archivos de `chat-app/` al Space
|
| 41 |
+
5. Agrega tu API key en los Secrets del Space:
|
| 42 |
+
- `AI_INTEGRATIONS_OPENAI_BASE_URL`
|
| 43 |
+
- `AI_INTEGRATIONS_OPENAI_API_KEY`
|
| 44 |
+
- O bien, usa `OPENAI_API_KEY` con tu propia clave de OpenAI
|
| 45 |
|
| 46 |
## Structure
|
| 47 |
|
| 48 |
```text
|
| 49 |
artifacts-monorepo/
|
| 50 |
+
├── chat-app/ # App Python/Gradio - Chatbot de IA
|
| 51 |
+
│ ├── app.py # Aplicación principal Gradio
|
| 52 |
+
│ ├── requirements.txt # Dependencias Python
|
| 53 |
+
│ └── README.md # Configuración Hugging Face Spaces
|
| 54 |
+
├── artifacts/ # Deployable applications (Node.js)
|
| 55 |
│ └── api-server/ # Express API server
|
| 56 |
├── lib/ # Shared libraries
|
| 57 |
│ ├── api-spec/ # OpenAPI spec + Orval codegen config
|
| 58 |
│ ├── api-client-react/ # Generated React Query hooks
|
| 59 |
│ ├── api-zod/ # Generated Zod schemas from OpenAPI
|
| 60 |
│ └── db/ # Drizzle ORM schema + DB connection
|
| 61 |
+
├── scripts/ # Utility scripts
|
| 62 |
+
└── package.json # Root package
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
```
|
| 64 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
## Packages
|
| 66 |
|
| 67 |
### `artifacts/api-server` (`@workspace/api-server`)
|
| 68 |
|
| 69 |
+
Express 5 API server (Node.js backend).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
|
| 71 |
### `lib/db` (`@workspace/db`)
|
| 72 |
|
| 73 |
+
Database layer using Drizzle ORM with PostgreSQL.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
|
| 75 |
### `lib/api-spec` (`@workspace/api-spec`)
|
| 76 |
|
| 77 |
+
OpenAPI 3.1 spec and Orval codegen config.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uv.lock
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|