{"id":51320840,"url":"https://github.com/alejandro945/innos-platform","last_synced_at":"2026-07-01T13:05:24.841Z","repository":{"id":365113240,"uuid":"1270614813","full_name":"alejandro945/innos-platform","owner":"alejandro945","description":"Platform For Rate Unification - Service As A Software","archived":false,"fork":false,"pushed_at":"2026-07-01T03:50:38.000Z","size":341,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-01T05:24:41.332Z","etag":null,"topics":["inngest","neon","nextjs16","ollama","typescript","vercel"],"latest_commit_sha":null,"homepage":"https://innos-platform.vercel.app","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alejandro945.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-15T22:19:08.000Z","updated_at":"2026-07-01T03:50:42.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/alejandro945/innos-platform","commit_stats":null,"previous_names":["alejandro945/innos-platform"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/alejandro945/innos-platform","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alejandro945%2Finnos-platform","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alejandro945%2Finnos-platform/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alejandro945%2Finnos-platform/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alejandro945%2Finnos-platform/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alejandro945","download_url":"https://codeload.github.com/alejandro945/innos-platform/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alejandro945%2Finnos-platform/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35007386,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-01T02:00:05.325Z","response_time":130,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["inngest","neon","nextjs16","ollama","typescript","vercel"],"created_at":"2026-07-01T13:05:24.259Z","updated_at":"2026-07-01T13:05:24.830Z","avatar_url":"https://github.com/alejandro945.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# INOOS · Comparador de Tarifas\n\nPlataforma web para centralizar las tarifas de proveedores, **estandarizar\n(homologar) sus ítems con IA** y comparar precios en cada proceso de\ncontratación.\n\n---\n\n## Arquitectura\n\n```\n┌────────────┐   HTTPS (pooled)   ┌─────────────────────┐\n│  Vercel    │ ─────────────────▶ │  Neon (Postgres +   │\n│  Next.js   │                    │  pgvector)          │\n│  + /api/   │                    └─────────────────────┘\n│   inngest  │   HTTPS (API key)\n└─────┬──────┘ ─────────────────▶ ┌─────────────────────┐\n      │                            │  Ollama Cloud API   │\n      │  webhooks                  │  (LLM + embeddings) │\n      ▼                            └─────────────────────┘\n┌────────────┐\n│  Inngest   │  cola durable de homologación\n│  Cloud     │\n└────────────┘\n```\n\n| Capa | Tecnología | Rol |\n|---|---|---|\n| App | **Next.js 16** (App Router, TS) en **Vercel** | UI + server actions + API |\n| Datos | **Postgres (Neon) + pgvector** vía **Prisma** | data store + búsqueda vectorial |\n| Auth | **Auth.js (NextAuth v5) + Microsoft Entra ID** | SSO + RBAC (5 roles); login local opcional |\n| IA | **Ollama** (local o Cloud) / Anthropic / OpenAI | homologación + embeddings (agnóstico de proveedor) |\n| Jobs | **Inngest** | pipeline de normalización durable (fallback inline en dev) |\n| UI | **Tailwind CSS** + sonner | componentes, toasts |\n| Archivos | **Vercel Blob** | evidencia de los archivos crudos |\n\n### Flujo de negocio\nCrear proceso → subir archivo del proveedor → parsear + **mapeo de columnas con\nIA** → **homologar con IA + revisión humana** (que aprende) → **comparar** contra\nel repositorio → **cargar las tarifas** homologadas al repositorio → exportar.\n\n### Capa de IA — agnóstica de proveedor\nVer [src/lib/llm.ts](src/lib/llm.ts) y [src/lib/embeddings.ts](src/lib/embeddings.ts).\nSe elige por `LLM_PROVIDER` / `EMBEDDINGS_PROVIDER` (o por las claves presentes).\n**Sin ningún proveedor configurado, la recuperación cae a modo léxico y todo\nsigue funcionando.**\n\n- **Ollama** (local con Docker, o **Ollama Cloud** con API key) — `LLM_PROVIDER=ollama`\n- **Anthropic** (Claude) para razonamiento + **OpenAI** para embeddings — opción nube\n- `EMBEDDING_DIMS` **debe** coincidir con el modelo de embeddings (nomic=768, OpenAI 3-small=1536). Si cambia, re-ejecute `pnpm db:vector` + `pnpm backfill:embeddings`.\n\n---\n\n## Estructura del proyecto\n\n```\nprisma/schema.prisma     Modelo de datos (data store layer)\nprisma/sql/              SQL de pgvector (referencia)\nscripts/                 import:excel · db:vector · backfill:embeddings\nsrc/auth.config.ts       Config de auth edge-safe (usada por el proxy)\nsrc/auth.ts              NextAuth + provisión JIT + roles + login local\nsrc/proxy.ts             Protección de rutas (Next 16 proxy)\nsrc/inngest/             Cliente + función durable de normalización\nsrc/lib/                 prisma, llm, embeddings, retrieval, homologation,\n                         normalize, comparison, analytics, nl-search, …\nsrc/components/          modal, table-filters, combobox, mutate-button, …\nsrc/app/(app)/           App autenticada (dashboard + secciones)\nsrc/app/iniciar-sesion/  Login (SSO + admin local)\nsrc/app/api/inngest/     Endpoint de Inngest\n```\n\n---\n\n## Puesta en marcha local (Docker + Ollama)\n\nStack 100% local, sin claves de nube. Requiere Docker y pnpm.\n\n```bash\n# 1) Postgres (pgvector) + Ollama + descarga de modelos\ndocker compose up -d\ndocker compose logs -f ollama-init     # esperar a \"Models ready\"\n\n# 2) Variables de entorno (la Opción A ya apunta a local)\ncp .env.example .env.local \u0026\u0026 cp .env.example .env\n#   genere AUTH_SECRET:  openssl rand -base64 32\n\n# 3) Esquema + pgvector + datos de ejemplo\npnpm install\npnpm exec prisma migrate dev      # crea las tablas\npnpm db:vector                    # columna vector(EMBEDDING_DIMS) + índice HNSW\npnpm import:excel                 # siembra catálogo + tarifas desde el Excel\npnpm backfill:embeddings          # genera embeddings con Ollama\npnpm dev\n```\n\n**Login local sin SSO:** con `LOCAL_ADMIN_EMAIL` + `LOCAL_ADMIN_PASSWORD` (ya en\n`.env.example`), el login muestra un formulario de administrador que entra con rol\n`ADMIN`, sin Azure. Déjelas **vacías en producción** para deshabilitarlo.\n\n**Comandos útiles:** `pnpm test` (Vitest) · `pnpm lint` · `pnpm build`.\n\n---\n\n## Despliegue en producción (Vercel + Neon + Ollama Cloud)\n\nTodo gestionado, sin servidores de IA que administrar.\n\n### 1. Base de datos → Neon\n`pgvector` nativo, *serverless* y con **connection pooling** (lo que Vercel\nnecesita).\n1. Crea un proyecto en [neon.tech](https://neon.tech).\n2. Copia dos cadenas: **pooled** (`-pooler`) → `DATABASE_URL`; **direct** → `DIRECT_URL`.\n3. Prepara el esquema (con `.env` apuntando a Neon):\n   ```bash\n   pnpm exec prisma migrate deploy\n   pnpm db:vector\n   pnpm import:excel          # opcional\n   pnpm backfill:embeddings\n   ```\n\n### 2. IA → Ollama (elige una)\n\n**Opción A — Ollama Cloud** (gestionado, requiere suscripción para varios modelos):\n1. API key en **ollama.com → Settings → Keys**.\n2. `OLLAMA_BASE_URL=https://ollama.com` + `OLLAMA_API_KEY` + `OLLAMA_CHAT_MODEL`\n   (nombre exacto del catálogo cloud que soporte `format`).\n\n**Opción B — Ollama autohospedado en Oracle Cloud (Always Free)** — gratis, CPU:\n\n1. **VM**: OCI → Compute → Instance → Ubuntu 22.04, shape\n   **VM.Standard.A1.Flex** (Ampere ARM), 4 OCPU / 24 GB (Always Free). Guarda la\n   IP pública y tu llave SSH.\n2. **Red OCI**: en la VCN → Security List/NSG agrega Ingress TCP 80 y 443 desde\n   `0.0.0.0/0`.\n3. **Firewall del SO** (las imágenes de Oracle traen iptables restrictivo):\n   ```bash\n   sudo iptables -I INPUT 6 -m state --state NEW -p tcp --dport 80 -j ACCEPT\n   sudo iptables -I INPUT 6 -m state --state NEW -p tcp --dport 443 -j ACCEPT\n   sudo netfilter-persistent save\n   ```\n4. **Instala Ollama y baja modelos** (queda en `127.0.0.1:11434`, privado):\n   ```bash\n   curl -fsSL https://ollama.com/install.sh | sh\n   ollama pull qwen2.5:3b\n   ollama pull nomic-embed-text\n   ```\n5. **Caddy como proxy con token + HTTPS** (necesitas un dominio apuntando a la IP\n   para TLS automático de Let's Encrypt):\n   ```bash\n   sudo apt install -y caddy\n   ```\n   `/etc/caddy/Caddyfile`:\n   ```\n   ollama.TU-DOMINIO.com {\n     @unauth not header Authorization \"Bearer {$OLLAMA_TOKEN}\"\n     respond @unauth 401\n     reverse_proxy 127.0.0.1:11434\n   }\n   ```\n   Define el token y reinicia:\n   ```bash\n   sudo systemctl edit caddy   # agrega: [Service]\\nEnvironment=OLLAMA_TOKEN=\u003ctoken-largo\u003e\n   sudo systemctl restart caddy\n   ```\n6. **Configura la app**:\n   ```bash\n   OLLAMA_BASE_URL=https://ollama.TU-DOMINIO.com\n   OLLAMA_API_KEY=\u003cel mismo OLLAMA_TOKEN\u003e\n   OLLAMA_CHAT_MODEL=qwen2.5:3b\n   ```\n   \u003e Sin dominio puedes usar `http://IP_VM:11434` (la app llama server-to-server,\n   \u003e no hay mixed-content), pero el tráfico viajaría sin cifrar — usa el proxy con\n   \u003e token y, si puedes, HTTPS. Rendimiento: en CPU, `qwen2.5:3b` es el punto\n   \u003e dulce; modelos 7B+ funcionan pero más lentos (Inngest lo hace tolerable).\n\n**Embeddings (cualquier opción):** `nomic-embed-text` (768) si tu Ollama lo\nexpone, OpenAI (1536), o vacío → modo léxico. Si fallan, el sistema cae a léxico\nautomáticamente.\n\n### 3. Inngest (cola durable — necesario en serverless)\nLa homologación de cientos de ítems excede el límite de una función de Vercel.\n1. Cuenta en [inngest.com](https://www.inngest.com).\n2. Conéctala a `https://TU-APP.vercel.app/api/inngest`.\n3. Copia `INNGEST_EVENT_KEY` + `INNGEST_SIGNING_KEY` a Vercel.\n\n### 4. App en Vercel\n1. Importa el repo (root = `inoos-platform/`).\n2. Build con Prisma: usa `\"build\": \"prisma generate \u0026\u0026 next build\"` (o\n   `postinstall: prisma generate`).\n3. Storage → **Blob** → copia `BLOB_READ_WRITE_TOKEN`.\n4. Carga las variables de entorno (abajo) y haz Deploy.\n\n### 5. Microsoft Entra ID (SSO)\n1. Registra una app en **Entra ID** (Azure Portal) y crea un *client secret*.\n2. Redirect URI: `https://TU-APP.vercel.app/api/auth/callback/microsoft-entra-id`\n   (local: `http://localhost:3000/api/auth/callback/microsoft-entra-id`).\n3. Activa el claim de **grupos** en el token y mapéalos a roles en\n   [src/lib/rbac.ts](src/lib/rbac.ts) (`ENTRA_GROUP_TO_ROLE`).\n\n---\n\n## Variables de entorno\n\nEn Next.js solo las variables `NEXT_PUBLIC_*` llegan al navegador; **todas las de\nabajo son del servidor** (seguras). En Vercel se agregan al proyecto (scope\nProduction/Preview).\n\n```bash\n# Base de datos (Neon en prod; docker en local)\nDATABASE_URL=\nDIRECT_URL=\n\n# Auth.js / Entra ID\nAUTH_SECRET=                         # openssl rand -base64 32\nAUTH_MICROSOFT_ENTRA_ID_ID=\nAUTH_MICROSOFT_ENTRA_ID_SECRET=\nAUTH_MICROSOFT_ENTRA_ID_ISSUER=      # https://login.microsoftonline.com/TENANT/v2.0\n\n# Login local sin SSO (vacías en producción)\nLOCAL_ADMIN_EMAIL=\nLOCAL_ADMIN_PASSWORD=\n\n# IA — Ollama (local http://localhost:11434 · Cloud https://ollama.com)\nLLM_PROVIDER=ollama\nOLLAMA_BASE_URL=\nOLLAMA_API_KEY=                      # requerido en Ollama Cloud\nOLLAMA_CHAT_MODEL=\nEMBEDDINGS_PROVIDER=ollama           # u \"openai\", o vacío (léxico)\nOLLAMA_EMBED_MODEL=nomic-embed-text\nEMBEDDING_DIMS=768                   # nomic=768 · OpenAI 3-small=1536\n# OPENAI_API_KEY=                    # si EMBEDDINGS_PROVIDER=openai\n# ANTHROPIC_API_KEY=                 # si LLM_PROVIDER=anthropic\n\n# Jobs (sin esto, la homologación corre inline — solo dev)\nINNGEST_EVENT_KEY=\nINNGEST_SIGNING_KEY=\n\n# Archivos\nBLOB_READ_WRITE_TOKEN=\n```\n\n\u003e **pgvector y Prisma:** la columna `embedding` la gestiona `pnpm db:vector`, NO\n\u003e Prisma. Evite `prisma db push` a secas (querrá borrarla); para cambios aditivos\n\u003e use `prisma migrate` o `prisma db execute`, y si recrea la columna vuelva a\n\u003e correr `pnpm backfill:embeddings`.\n\n---\n\n## Estado del proyecto\n\n- [x] **Fase 0 — Fundaciones:** Next.js + Prisma + SSO Entra ID + RBAC + app shell.\n- [x] **Fase 1 — Data store:** CRUD proveedores/catálogo/tarifas + importador Excel.\n- [x] **Fase 2 — Carga y parseo:** procesos, subida de archivo, parseo, mapeo de columnas con IA.\n- [x] **Fase 3 — Normalización con IA:** embeddings + pgvector, recuperación de candidatos, agente LLM, pipeline (Inngest + fallback inline), bandeja de revisión que aprende.\n- [x] **Fase 4 — Comparación y reportes:** comparación por ítem (mín/máx/prom + mejor precio + ahorro), exportación Excel, reporte imprimible (PDF), página de reportes.\n- [x] **Fase 5 — Avanzadas:** búsqueda en lenguaje natural (IA), simulador de ahorro / recomendación de adjudicación, anomalías de precio, alertas de vencimiento, alertas en el dashboard.\n- [x] **UX:** toasts, modales accesibles, búsqueda + filtros en tablas, revisión en lote (alta confianza / crear-aprobar sin-match / selección múltiple) con typeahead y candidatos IA, error boundary, vista previa de mapeo, onboarding, comparación 1-por-proveedor, administración (usuarios + auditoría), homologación con progreso + pausar/reanudar resiliente.\n- [ ] **Fase 6 — Hardening** (tests e2e, observabilidad)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falejandro945%2Finnos-platform","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falejandro945%2Finnos-platform","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falejandro945%2Finnos-platform/lists"}