{"id":31070493,"url":"https://github.com/lumenlabsdev/open-assistants-api","last_synced_at":"2026-04-11T17:44:17.507Z","repository":{"id":314101046,"uuid":"1053872197","full_name":"LumenLabsDev/open-assistants-api","owner":"LumenLabsDev","description":"Assistants-shaped API façade built on the OpenAI Responses API. Fastify + TypeScript, Redis persistence, clean architecture, Docker-ready.","archived":false,"fork":false,"pushed_at":"2025-09-10T15:07:58.000Z","size":110,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-10T17:17:56.797Z","etag":null,"topics":["assistants-api","clean-architecture","compatibility-layer","docker","fastify","ghcr","middleware","mvc","openai","redis","responses-api","structured-logging","typescript","validation","zod"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LumenLabsDev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2025-09-10T03:55:30.000Z","updated_at":"2025-09-10T15:08:01.000Z","dependencies_parsed_at":"2025-09-10T17:18:15.204Z","dependency_job_id":"de0138f9-1082-4d05-b98c-d2431c99b1a8","html_url":"https://github.com/LumenLabsDev/open-assistants-api","commit_stats":null,"previous_names":["lumenlabsdev/assistants-api-middleware"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/LumenLabsDev/open-assistants-api","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LumenLabsDev%2Fopen-assistants-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LumenLabsDev%2Fopen-assistants-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LumenLabsDev%2Fopen-assistants-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LumenLabsDev%2Fopen-assistants-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LumenLabsDev","download_url":"https://codeload.github.com/LumenLabsDev/open-assistants-api/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LumenLabsDev%2Fopen-assistants-api/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":275333311,"owners_count":25446100,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-15T02:00:09.272Z","response_time":75,"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":["assistants-api","clean-architecture","compatibility-layer","docker","fastify","ghcr","middleware","mvc","openai","redis","responses-api","structured-logging","typescript","validation","zod"],"created_at":"2025-09-15T23:01:19.989Z","updated_at":"2025-09-15T23:03:05.577Z","avatar_url":"https://github.com/LumenLabsDev.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Open Assistants API (Responses-backed)\r\n\r\n[![CI](https://github.com/LumenLabsDev/open-assistants-api/actions/workflows/ci.yml/badge.svg)](https://github.com/LumenLabsDev/open-assistants-api/actions/workflows/ci.yml)\r\n![Node](https://img.shields.io/badge/node-%3E%3D18-informational)\r\n![Fastify](https://img.shields.io/badge/Fastify-%20-lightgrey)\r\n![TypeScript](https://img.shields.io/badge/TypeScript-%20-blue)\r\n![Redis](https://img.shields.io/badge/Redis-%20-red)\r\n![Azure Cosmos DB](https://img.shields.io/badge/Azure%20Cosmos%20DB-supported-0078D4)\r\n![License: MIT](https://img.shields.io/badge/License-MIT-yellow)\r\n![Docker](https://img.shields.io/badge/Docker-ready-success)\r\n\r\n**Assistants-shaped API façade built on the OpenAI Responses API.** Keep your Assistants-style client calls and migrate your backend to **Responses** at your own pace.  \r\nStack: **Fastify + TypeScript**, **Redis or Azure Cosmos DB** persistence, **Zod** validation, **structured logging**, and clean architecture.\r\n\r\n## Highlights\r\n\r\n- **Assistants**: create / list / get / update  \r\n- **Threads**: create • **Messages**: list / add  \r\n- **Runs**: create / list / get — translates thread context into a single **Responses** call  \r\n- **Clean architecture**: domain, application (services), infrastructure (repos/clients), interfaces (HTTP controllers)  \r\n- **MVC-ish controllers** with Zod validation and presenters that shape responses like Assistants  \r\n- **Structured logs** (ISO timestamp, level, component, version)\r\n\r\n## Requirements\r\n\r\n- Node **18+**\r\n- `OPENAI_API_KEY` in environment or `.env`\r\n- Choose a database provider:\r\n  - Redis (**7+**): set `REDIS_URL`\r\n  - Azure Cosmos DB: set `DATABASE_PROVIDER=cosmos`, `COSMOS_ENDPOINT`, `COSMOS_KEY`, `COSMOS_DATABASE_ID`\r\n\r\n### Quick start\r\n```bash\r\nnpm install\r\necho \"OPENAI_API_KEY=sk-...\" \u003e .env\r\necho \"REDIS_URL=redis://localhost:6379\" \u003e\u003e .env\r\nnpm run dev  # http://localhost:3500\r\n```\r\n\r\n### Cosmos DB quick start\r\n```bash\r\nnpm install\r\necho \"OPENAI_API_KEY=sk-...\" \u003e .env\r\necho \"DATABASE_PROVIDER=cosmos\" \u003e\u003e .env\r\necho \"COSMOS_ENDPOINT=https://your-account.documents.azure.com:443/\" \u003e\u003e .env\r\necho \"COSMOS_KEY=your-cosmos-db-key\" \u003e\u003e .env\r\necho \"COSMOS_DATABASE_ID=assistants-api\" \u003e\u003e .env\r\nnpm run dev  # http://localhost:3500\r\n```\r\n### Docker\r\nBuild locally:\r\n```bash\r\ndocker build -t open-assistants-api:local .\r\ndocker run --rm -p 3500:3500 \\\r\n  -e OPENAI_API_KEY=sk-... \\\r\n  -e REDIS_URL=redis://host.docker.internal:6379 \\\r\n  open-assistants-api:local\r\n```\r\n\r\nRun with Cosmos DB:\r\n```bash\r\ndocker run --rm -p 3500:3500 \\\r\n  -e OPENAI_API_KEY=sk-... \\\r\n  -e DATABASE_PROVIDER=cosmos \\\r\n  -e COSMOS_ENDPOINT=https://your-account.documents.azure.com:443/ \\\r\n  -e COSMOS_KEY=your-cosmos-db-key \\\r\n  -e COSMOS_DATABASE_ID=assistants-api \\\r\n  open-assistants-api:local\r\n```\r\n\r\nImages are published to GHCR on release:\r\n- `ghcr.io/LumenLabsDev/open-assistants-api:latest`\r\n- `ghcr.io/LumenLabsDev/open-assistants-api:\u003cversion\u003e`\r\n\r\nGitHub Releases also attach an `linux-amd64` image tar for offline use.\r\n\r\n\r\n`.env`:\r\n```bash\r\nOPENAI_API_KEY=sk-...\r\nPORT=3500\r\n# Select database provider (default = redis)\r\nDATABASE_PROVIDER=redis\r\n\r\n# When using Redis\r\nREDIS_URL=redis://localhost:6379\r\n\r\n# When using Azure Cosmos DB\r\n# DATABASE_PROVIDER=cosmos\r\n# COSMOS_ENDPOINT=https://your-account.documents.azure.com:443/\r\n# COSMOS_KEY=your-cosmos-db-key\r\n# COSMOS_DATABASE_ID=assistants-api\r\n```\r\n\r\n### Health check\r\n```bash\r\ncurl -s http://localhost:3500/health\r\n# {\"status\":\"ok\"}\r\n```\r\n\r\n### API shape (Assistants-like)\r\n- Assistants resources include `object: \"assistant\"`, `created_at`.\r\n- Threads: `object: \"thread\"` with `created_at`.\r\n- Messages returned as `object: \"list\"` of items shaped like Assistants messages (text content parts).\r\n- Runs returned as `object: \"thread.run\"`.\r\n\r\n### Example flow\r\n1) Create assistant\r\n```bash\r\ncurl -s -X POST http://localhost:3500/v1/assistants -H 'content-type: application/json' \\\r\n  -d '{\"name\":\"Helper\",\"instructions\":\"Answer briefly.\",\"model\":\"gpt-4o-mini\"}'\r\n```\r\n\r\n2) Create thread\r\n```bash\r\ncurl -s -X POST http://localhost:3500/v1/threads\r\n```\r\n\r\n3) Add message\r\n```bash\r\ncurl -s -X POST http://localhost:3500/v1/threads/\u003cthread_id\u003e/messages -H 'content-type: application/json' \\\r\n  -d '{\"role\":\"user\",\"content\":\"Hello!\"}'\r\n```\r\n\r\n4) Run\r\n```bash\r\ncurl -s -X POST http://localhost:3500/v1/threads/\u003cthread_id\u003e/runs -H 'content-type: application/json' \\\r\n  -d '{\"assistant_id\":\"\u003cassistant_id\u003e\"}'\r\n```\r\n\r\n5) List messages\r\n```bash\r\ncurl -s http://localhost:3500/v1/threads/\u003cthread_id\u003e/messages\r\n```\r\n\r\n### Quick reference (HTTP)\r\n- POST `/v1/assistants` — create assistant `{ name?, model?, instructions? }`\r\n- GET `/v1/assistants` — list assistants\r\n- GET `/v1/assistants/:assistant_id` — get assistant\r\n- POST `/v1/assistants/:assistant_id` — update assistant\r\n- POST `/v1/threads` — create thread\r\n- GET `/v1/threads/:thread_id/messages` — list messages (list wrapper)\r\n- POST `/v1/threads/:thread_id/messages` — add message `{ role, content }` (`role` in `user|assistant|system`)\r\n- POST `/v1/threads/:thread_id/runs` — create run `{ assistant_id, temperature? }`\r\n- GET `/v1/threads/:thread_id/runs` — list runs (list wrapper)\r\n- GET `/v1/threads/:thread_id/runs/:run_id` — get run\r\n\r\n### Project layout\r\n```\r\nsrc/\r\n  domain/            # Entities and interfaces (ports)\r\n  application/       # Use cases (services)\r\n  infra/             # Redis/Cosmos repositories, OpenAI Responses client, ID generator\r\n  interfaces/http/   # Controllers, presenters, DTOs, validation, error handling\r\n  container.ts       # Simple DI wiring (switches infra via env)\r\n  server.ts          # Fastify bootstrap\r\n```\r\n\r\n### Notes \u0026 next steps\r\n- Persistence requires a DB provider (Redis or Cosmos). Configure env vars accordingly.\r\n- Add streaming endpoints and tool-calls if needed.\r\n- The included OpenAPI YAMLs serve as references; not used for runtime validation.\r\n\r\n### Architecture\r\n\r\nComponent layering (Clean Architecture-inspired):\r\n\r\n```mermaid\r\ngraph TD\r\n  subgraph Interfaces_HTTP\r\n    Ctr[Controllers]\r\n    Val[Zod Schemas]\r\n    Pres[Presenters]\r\n  end\r\n\r\n  subgraph Application_Use_Cases\r\n    SvcA[AssistantsService]\r\n    SvcT[ThreadsService]\r\n    SvcR[RunsService]\r\n  end\r\n\r\n  subgraph Domain\r\n    Types[Types]\r\n    Ports[Ports]\r\n    Errors[Errors]\r\n    Models[Models]\r\n  end\r\n\r\n  subgraph Infrastructure_Layer\r\n    Repos[(Redis Repos)]\r\n    Resp[OpenAI Responses Client]\r\n    IdGen[UUID Id Generator]\r\n    Redis[(Redis)]\r\n    OpenAI[(OpenAI Responses)]\r\n  end\r\n\r\n  Ctr --\u003e|validate| Val\r\n  Ctr --\u003e|call| SvcA\r\n  Ctr --\u003e|call| SvcT\r\n  Ctr --\u003e|call| SvcR\r\n\r\n  SvcA --\u003e Ports\r\n  SvcT --\u003e Ports\r\n  SvcR --\u003e Ports\r\n  SvcA --\u003e Types\r\n  SvcT --\u003e Types\r\n  SvcR --\u003e Types\r\n\r\n  Repos --\u003e|implements| Ports\r\n  Resp --\u003e|implements| Ports\r\n  IdGen --\u003e Repos\r\n\r\n  Repos --\u003e|persist| Redis\r\n  Resp --\u003e|invoke| OpenAI\r\n\r\n  Pres --\u003e Ctr\r\n```\r\n\r\nRun flow (create run over a thread):\r\n\r\n```mermaid\r\nsequenceDiagram\r\n  participant Client\r\n  participant HTTP as HTTP_Controller\r\n  participant Runs as RunsService\r\n  participant Assist as AssistantsRepo_Redis\r\n  participant Msg as MessagesRepo_Redis\r\n  participant RunRepo as RunsRepo_Redis\r\n  participant OA as OpenAI_Responses\r\n\r\n  Client-\u003e\u003eHTTP: POST /v1/threads/:id/runs { assistant_id }\r\n  HTTP-\u003e\u003eRuns: createRun(threadId, assistantId)\r\n  Runs-\u003e\u003eAssist: get(assistantId)\r\n  Assist--\u003e\u003eRuns: assistant\r\n  Runs-\u003e\u003eRunRepo: create({ queued })\r\n  Runs-\u003e\u003eMsg: listByThread(threadId)\r\n  Runs-\u003e\u003eOA: createTextResponse(model, input)\r\n  OA--\u003e\u003eRuns: text\r\n  Runs-\u003e\u003eMsg: add({ role: assistant, content: text })\r\n  Msg--\u003e\u003eRuns: message{id}\r\n  Runs-\u003e\u003eRunRepo: update({ completed, responseMessageId })\r\n  Runs--\u003e\u003eHTTP: run\r\n  HTTP--\u003e\u003eClient: 201 { run }\r\n```\r\n\r\n### Architecture\r\n- Clean Architecture layering:\r\n  - Domain: core types, errors, ports (`src/domain`)\r\n  - Application: use cases (`src/application`)\r\n  - Interfaces: HTTP controllers/validation/presenters (`src/interfaces/http`)\r\n  - Infrastructure: repos and external clients (`src/infra`)\r\n- Dependency flow: outer layers depend inward; infra implements domain ports.\r\n\r\n### CI/CD\r\n- CI: `.github/workflows/ci.yml` — build and test on Node 18/20/22; matrix includes runs with and without Redis service. Tests that require Redis are skipped when `REDIS_URL` is not set.\r\n- Release: `.github/workflows/release.yml` — on release from `main`, pushes multi-arch Docker images to GHCR and uploads an amd64 image tar to the release.\r\n\r\n### Testing\r\n```bash\r\nnpm test        # run once\r\nnpm run test:watch\r\n```\r\nTests include:\r\n- Services tests using Redis repos (set `REDIS_URL`) and a fake Responses client\r\n- HTTP integration test booting Fastify via `buildApp()`\r\n\r\n### License\r\nMIT\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flumenlabsdev%2Fopen-assistants-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flumenlabsdev%2Fopen-assistants-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flumenlabsdev%2Fopen-assistants-api/lists"}